Skip to content

Commit 666be0e

Browse files
EliEli
authored andcommitted
Added exception in pathological case in transition.py plus test.
1 parent 6405fdf commit 666be0e

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

tests/test_transition.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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])

vtools/functions/transition.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
21
from scipy.interpolate import PchipInterpolator
32
import pandas as pd
43
import numpy as np
54

65
__all__ = ["transition_ts"]
76

7+
88
def transition_ts(
99
ts0, ts1, method="linear", create_gap=None, overlap=(0, 0), return_type="series"
1010
):
@@ -37,6 +37,10 @@ def transition_ts(
3737
end_val = ts1.loc[trans_end:].iloc[0]
3838

3939
else:
40+
if ts0.index[-1] >= ts1.index[0]:
41+
raise ValueError(
42+
"ts0 ends after ts1 starts; overlap must be resolved with create_gap."
43+
)
4044
trans_start = ts0.index[-1] + freq
4145
trans_end = ts1.index[0] - freq
4246
start_time = ts0.index[-1]
@@ -107,4 +111,4 @@ def transition_ts(
107111
ts1_trunc = ts1.loc[trans_end + freq :]
108112
return pd.concat([ts0_trunc, interpolated, ts1_trunc])
109113
else:
110-
raise ValueError("return_type must be either 'glue' or 'series'.")
114+
raise ValueError("return_type must be either 'glue' or 'series'.")

0 commit comments

Comments
 (0)