def test_interpolator_bounds(): with pytest.raises(ValueError): rq.Pipeline( rq.LowPass(window=10, portion=2, subsample_rate=1, quantile=0.5, alpha=2.0)) with pytest.raises(ValueError): rq.Pipeline( rq.LowPass(window=10, portion=2, subsample_rate=1, quantile=2.5))
def test_median_array_input(window_size=71, length=1000): pipe = rq.Pipeline(rq.LowPass(window=window_size, portion=window_size // 2)) x = example_input(length) y = pipe.feed(x) z = pd.Series(x).rolling(window_size).median() assert np.equal(y[window_size:], z.values[window_size:]).all( ) # exact equality, since no arithmetic is done on the numbers
def test_median_scalar_inputs(window_size=3, length=100): # no interpolation yet pipe = rq.Pipeline(rq.LowPass(window=window_size, portion=window_size // 2)) v = example_input(length) for i, x in enumerate(v): y = pipe.feed(x) if i >= window_size: assert y == np.median(v[(i - window_size + 1):(i + 1)])
def test_basic_nans(window_size=5, length=20): # make sure the pipeline effectively flushes its contents with NaNs pipe = rq.Pipeline(rq.LowPass(window=window_size, portion=window_size // 2)) x = example_input(length) y = pipe.feed(x) for i in range(window_size): pipe.feed(np.nan) z = pipe.feed(x) assert np.equal(y, z).all()
def test_fancy_interpolation( window_size=10, n_trials=200 ): # small windows may be more prone to boundary/edge-condition bugs for trial in range(n_trials): x = example_input(window_size) quantile = np.random.uniform() alpha, beta = np.random.uniform(size=2) pipe = rq.Pipeline( rq.LowPass(window=window_size, quantile=quantile, alpha=alpha, beta=beta)) y = pipe.feed(x) z = mquantiles(x, quantile, alphap=alpha, betap=beta) assert z == y[-1]
# illustration of what my hypothetical API should look like import numpy as np import rolling_quantiles as rq filter = rq.Pipeline( # stateful filter rq.LowPass(window=100, portion=50, subsample_rate=2), rq.HighPass(window=10, portion=3, subsample_rate=1)) # expose specialized pipelines like `rq.MedianFilter` input = np.random.randn(1000) output = filter.feed( input ) # a single `ufunc` entry point that takes in arrays or scalars and spits out an appropriate amount of output
def measure_runtime(f): start = time.perf_counter() # could also try time.monotonic() res = f() return time.perf_counter() - start, res signal = np.cumsum(np.random.normal(size=100_000_000)) series = pd.Series(signal) # construct a priori for fairness window_sizes = np.array([4, 10, 20, 30, 40, 50]) + 1 # odd rq_times, sc_times, pd_times = [], [], [] for window_size in window_sizes: pipe = rq.Pipeline( rq.LowPass(window=window_size, portion=window_size // 2, subsample_rate=1)) rq_time, rq_res = measure_runtime(lambda: pipe.feed(signal)) sc_time, sc_res = measure_runtime(lambda: medfilt(signal, window_size)) pd_time, pd_res = measure_runtime(lambda: series.rolling(window_size). quantile(0.5, interpolation="nearest")) # rq_res and sc_res will differ slightly at the edges because medfilt pads both sides with zeros as if it were a convolution. # I pad at the beginning only, since I employ an online algorithm. offset = window_size // 2 discrepancy = rq_res[1000:2000] - sc_res[(1000 - offset):(2000 - offset)] #print("maximum discrepancy between the two is", np.amax(np.abs(discrepancy))) assert np.amax(np.abs(discrepancy)) < 1e-10 print("runtimes are", rq_time, "versus", sc_time, "versus", pd_time) rq_times.append(rq_time) sc_times.append(sc_time) pd_times.append(pd_time)
def test_window_size(): with pytest.raises(ValueError): rq.Pipeline(rq.LowPass())
def test_innocuous_interpolation(window_size=1001, length=10000): pipe = rq.Pipeline(rq.LowPass(window=window_size, quantile=0.5)) x = example_input(length) y = pipe.feed(x) z = pd.Series(x).rolling(window_size).median() assert np.equal(y[window_size:], z.values[window_size:]).all()
def test_typical_interpolation(window_size=40, quantile=0.2): x = example_input(window_size) # one window only, due to scipy pipe = rq.Pipeline(rq.LowPass(window=window_size, quantile=quantile)) y = pipe.feed(x) z = mquantiles(x, quantile, alphap=1, betap=1) assert z == y[-1]