|
| 1 | +import pytest |
| 2 | +import pandas as pd |
| 3 | +import numpy as np |
| 4 | +from vtools import transition_ts |
| 5 | + |
| 6 | + |
| 7 | +def pink_noise(size, alpha=1.0): |
| 8 | + f = np.fft.rfftfreq(size) |
| 9 | + f[0] = 1e-6 |
| 10 | + spectrum = 1 / f ** (alpha / 2.0) |
| 11 | + phases = np.exp(2j * np.pi * np.random.rand(len(f))) |
| 12 | + signal = np.fft.irfft(spectrum * phases, n=size) |
| 13 | + return signal / np.std(signal) |
| 14 | + |
| 15 | + |
| 16 | +@pytest.fixture |
| 17 | +def test_series(): |
| 18 | + freq = "15min" |
| 19 | + date0 = pd.date_range("2023-01-01", "2023-06-01", freq=freq) |
| 20 | + date1 = pd.date_range("2023-05-20", "2023-09-01", freq=freq) |
| 21 | + t0 = np.linspace(0, 1, len(date0)) |
| 22 | + t1 = np.linspace(0, 1, len(date1)) |
| 23 | + ts0 = pd.Series(1.0 + 0.5 * pink_noise(len(date0)) + 0.1 * np.sin(2 * np.pi * 3 * t0), index=date0) |
| 24 | + ts1 = pd.Series(2.0 + 0.4 * pink_noise(len(date1)) + 0.15 * np.cos(2 * np.pi * 2 * t1), index=date1) |
| 25 | + return ts0, ts1 |
| 26 | + |
| 27 | + |
| 28 | +def test_natural_gap_linear(test_series): |
| 29 | + ts0, ts1 = test_series |
| 30 | + with pytest.raises(ValueError, match="overlap must be resolved with create_gap"): |
| 31 | + transition_ts(ts0, ts1, method='linear', create_gap=None, return_type='series') |
| 32 | + |
| 33 | + |
| 34 | +def test_explicit_gap_linear(test_series): |
| 35 | + ts0, ts1 = test_series |
| 36 | + gap = ["2023-05-15", "2023-06-10"] |
| 37 | + result = transition_ts(ts0, ts1, method="linear", create_gap=gap, return_type="series") |
| 38 | + glue = transition_ts(ts0, ts1, method="linear", create_gap=gap, return_type="glue") |
| 39 | + assert glue.index[0] == pd.Timestamp(gap[0]) |
| 40 | + assert glue.index[-1] == pd.Timestamp(gap[1]) |
| 41 | + |
| 42 | + |
| 43 | +def test_explicit_gap_pchip_with_overlap(test_series): |
| 44 | + ts0, ts1 = test_series |
| 45 | + gap = ["2023-05-15", "2023-06-10"] |
| 46 | + result = transition_ts(ts0, ts1, method="pchip", create_gap=gap, overlap=(10, 10), return_type="series") |
| 47 | + assert result.index.is_monotonic_increasing |
| 48 | + assert not result.index.duplicated().any() |
| 49 | + |
| 50 | + |
| 51 | +def test_gap_inside_natural_gap(test_series): |
| 52 | + ts0, ts1 = test_series |
| 53 | + gap = ["2023-05-10", "2023-05-15"] |
| 54 | + transition = transition_ts(ts0, ts1, method="linear", create_gap=gap, return_type="glue") |
| 55 | + diff = np.diff(transition.values) |
| 56 | + assert np.std(diff) < 0.5 |
| 57 | + |
| 58 | + |
| 59 | +def test_overlapping_series_with_gap(test_series): |
| 60 | + ts0, ts1 = test_series |
| 61 | + gap = ["2023-05-25", "2023-05-28"] |
| 62 | + result = transition_ts(ts0, ts1, method="pchip", create_gap=gap, overlap=(12, 12), return_type="series") |
| 63 | + assert result.index.is_monotonic_increasing |
| 64 | + assert not result.index.duplicated().any() |
| 65 | + |
| 66 | + |
| 67 | +def test_glue_return_type(test_series): |
| 68 | + ts0, ts1 = test_series |
| 69 | + gap = ["2023-05-15", "2023-06-10"] |
| 70 | + glue = transition_ts(ts0, ts1, method="linear", create_gap=gap, return_type="glue") |
| 71 | + assert glue.index[0] >= pd.Timestamp(gap[0]) |
| 72 | + assert glue.index[-1] <= pd.Timestamp(gap[1]) |
0 commit comments