From a9d18279286c6f1ad4308e43943659358d71c7d0 Mon Sep 17 00:00:00 2001 From: Olivier Boulant Date: Tue, 12 Jan 2021 14:48:53 +0100 Subject: [PATCH] test: Improve test coverage (#106) * test(CostMl, CostCosine): Add test, delete duplicate functions, set random seed * refactor(pw_constant): inject seed for reproducible random state * test(test_costs): set random seed * test(test_costs): running black * test(costlinear): fix an import mishandled by merge with master --- src/ruptures/datasets/pw_constant.py | 8 +- tests/test_costs.py | 117 ++++++++++----------------- 2 files changed, 47 insertions(+), 78 deletions(-) diff --git a/src/ruptures/datasets/pw_constant.py b/src/ruptures/datasets/pw_constant.py index 40ebeec6..5fa3eaa2 100644 --- a/src/ruptures/datasets/pw_constant.py +++ b/src/ruptures/datasets/pw_constant.py @@ -1,12 +1,14 @@ """Piecewise constant signal (with noise)""" import numpy as np -from numpy import random as rd +from numpy.random import RandomState from ruptures.utils import draw_bkps -def pw_constant(n_samples=200, n_features=1, n_bkps=3, noise_std=None, delta=(1, 10)): +def pw_constant( + n_samples=200, n_features=1, n_bkps=3, noise_std=None, delta=(1, 10), seed=None +): """Return a piecewise constant signal and the associated changepoints. Args: @@ -15,6 +17,7 @@ def pw_constant(n_samples=200, n_features=1, n_bkps=3, noise_std=None, delta=(1, n_bkps (int, optional): number of changepoints noise_std (float, optional): noise std. If None, no noise is added delta (tuple, optional): (delta_min, delta_max) max and min jump values + seed (int): random seed Returns: tuple: signal of shape (n_samples, n_features), list of breakpoints @@ -27,6 +30,7 @@ def pw_constant(n_samples=200, n_features=1, n_bkps=3, noise_std=None, delta=(1, delta_min, delta_max = delta # mean value center = np.zeros(n_features) + rd = RandomState(seed) for ind in np.split(tt_, bkps): if ind.size > 0: # jump value diff --git a/tests/test_costs.py b/tests/test_costs.py index 5cc1f542..a8e2ce1e 100644 --- a/tests/test_costs.py +++ b/tests/test_costs.py @@ -1,16 +1,6 @@ import pytest -from ruptures.costs import ( - CostAR, - CostCLinear, - CostLinear, - CostL1, - CostL2, - CostLinear, - CostNormal, - CostRank, - CostRbf, - cost_factory, -) +import numpy as np +from ruptures.costs import cost_factory, CostLinear from ruptures.datasets import pw_constant from ruptures.exceptions import NotEnoughPoints import numpy as np @@ -18,84 +8,43 @@ @pytest.fixture(scope="module") def signal_bkps_1D(): - signal, bkps = pw_constant(n_features=1) + signal, bkps = pw_constant(n_features=1, seed=1234567890) return signal, bkps @pytest.fixture(scope="module") def signal_bkps_1D_noisy(): - signal, bkps = pw_constant(n_features=1, noise_std=1) + signal, bkps = pw_constant(n_features=1, noise_std=1, seed=1234567890) return signal, bkps @pytest.fixture(scope="module") def signal_bkps_5D(): - signal, bkps = pw_constant(n_features=5) + signal, bkps = pw_constant(n_features=5, seed=1234567890) return signal, bkps @pytest.fixture(scope="module") def signal_bkps_5D_noisy(): - signal, bkps = pw_constant(n_features=5, noise_std=1) + signal, bkps = pw_constant(n_features=5, noise_std=1, seed=1234567890) return signal, bkps -cost_classes = {CostAR, CostL1, CostL2, CostNormal, CostRbf, CostRank, CostCLinear} -cost_names = {"ar", "l1", "l2", "normal", "rbf", "rank", "clinear"} - - -@pytest.mark.parametrize("cost", cost_classes) -def test_costs_1D(signal_bkps_1D, cost): - signal, bkps = signal_bkps_1D - cost.fit(signal) - cost.fit(signal.flatten()) - cost.error(0, 100) - cost.error(100, signal.shape[0]) - cost.error(10, 50) - cost.sum_of_costs(bkps) - with pytest.raises(NotEnoughPoints): - cost.error(1, 2) - - -@pytest.mark.parametrize("cost", cost_classes) -def test_costs_1D_noisy(signal_bkps_1D_noisy, cost): - signal, bkps = signal_bkps_1D_noisy - cost.fit(signal) - cost.fit(signal.flatten()) - cost.error(0, 100) - cost.error(100, signal.shape[0]) - cost.error(10, 50) - cost.sum_of_costs(bkps) - with pytest.raises(NotEnoughPoints): - cost.error(1, 2) - - -@pytest.mark.parametrize("cost", cost_classes) -def test_costs_5D(signal_bkps_5D, cost): - signal, bkps = signal_bkps_5D - cost.fit(signal) - cost.error(0, 100) - cost.error(100, signal.shape[0]) - cost.error(10, 50) - cost.sum_of_costs(bkps) - with pytest.raises(NotEnoughPoints): - cost.error(1, 2) - - -@pytest.mark.parametrize("cost", cost_classes) -def test_costs_5D_noisy(signal_bkps_5D_noisy, cost): - signal, bkps = signal_bkps_5D_noisy - cost.fit(signal) - cost.error(0, 100) - cost.error(100, signal.shape[0]) - cost.error(10, 50) - cost.sum_of_costs(bkps) - with pytest.raises(NotEnoughPoints): - cost.error(1, 2) +cost_names = { + "ar", + "l1", + "l2", + "normal", + "rbf", + "rank", + "clinear", + "mahalanobis", + "cosine", +} @pytest.mark.parametrize("cost_name", cost_names) -def test_costs_1D(signal_bkps_1D, cost_name): +def test_costs_1D_names(signal_bkps_1D, cost_name): signal, bkps = signal_bkps_1D cost = cost_factory(cost_name) cost.fit(signal) @@ -105,11 +54,15 @@ def test_costs_1D(signal_bkps_1D, cost_name): cost.error(10, 50) cost.sum_of_costs(bkps) with pytest.raises(NotEnoughPoints): - cost.error(1, 2) + if cost_name == "cosine": + cost.min_size = 4 + cost.error(1, 2) + else: + cost.error(1, 2) @pytest.mark.parametrize("cost_name", cost_names) -def test_costs_1D_noisy(signal_bkps_1D_noisy, cost_name): +def test_costs_1D_noisy_names(signal_bkps_1D_noisy, cost_name): signal, bkps = signal_bkps_1D_noisy cost = cost_factory(cost_name) cost.fit(signal) @@ -119,11 +72,15 @@ def test_costs_1D_noisy(signal_bkps_1D_noisy, cost_name): cost.error(10, 50) cost.sum_of_costs(bkps) with pytest.raises(NotEnoughPoints): - cost.error(1, 2) + if cost_name == "cosine": + cost.min_size = 4 + cost.error(1, 2) + else: + cost.error(1, 2) @pytest.mark.parametrize("cost_name", cost_names) -def test_costs_5D(signal_bkps_5D, cost_name): +def test_costs_5D_names(signal_bkps_5D, cost_name): signal, bkps = signal_bkps_5D cost = cost_factory(cost_name) cost.fit(signal) @@ -132,11 +89,15 @@ def test_costs_5D(signal_bkps_5D, cost_name): cost.error(10, 50) cost.sum_of_costs(bkps) with pytest.raises(NotEnoughPoints): - cost.error(1, 2) + if cost_name == "cosine": + cost.min_size = 4 + cost.error(1, 2) + else: + cost.error(1, 2) @pytest.mark.parametrize("cost_name", cost_names) -def test_costs_5D_noisy(signal_bkps_5D_noisy, cost_name): +def test_costs_5D_noisy_names(signal_bkps_5D_noisy, cost_name): signal, bkps = signal_bkps_5D_noisy cost = cost_factory(cost_name) cost.fit(signal) @@ -145,7 +106,11 @@ def test_costs_5D_noisy(signal_bkps_5D_noisy, cost_name): cost.error(10, 50) cost.sum_of_costs(bkps) with pytest.raises(NotEnoughPoints): - cost.error(1, 2) + if cost_name == "cosine": + cost.min_size = 4 + cost.error(1, 2) + else: + cost.error(1, 2) def test_factory_exception():