def calculate_test_positivity(positive_tests: pd.Series, negative_tests: pd.Series, smooth: int = 7, lag_lookback: int = 7) -> pd.Series: """Calculates positive test rate. Args: positive_tests: Number of cumulative positive tests. negative_tests: Number of cumulative negative tests. Returns: Positive test rate. """ daily_negative_tests = negative_tests.diff() daily_positive_tests = positive_tests.diff() positive_smoothed = series_utils.smooth_with_rolling_average( daily_positive_tests) negative_smoothed = series_utils.smooth_with_rolling_average( daily_negative_tests, include_trailing_zeros=False) last_n_positive = positive_smoothed[-lag_lookback:] last_n_negative = negative_smoothed[-lag_lookback:] if any(last_n_positive) and last_n_negative.isna().all(): return pd.Series([], dtype="float64") return positive_smoothed / (negative_smoothed + positive_smoothed)
def calculate_test_positivity( region_dataset: OneRegionTimeseriesDataset, lag_lookback: int = 7 ) -> pd.Series: """Calculates positive test rate from combined data.""" data = region_dataset.date_indexed positive_tests = series_utils.interpolate_stalled_and_missing_values( data[CommonFields.POSITIVE_TESTS] ) negative_tests = series_utils.interpolate_stalled_and_missing_values( data[CommonFields.NEGATIVE_TESTS] ) daily_negative_tests = negative_tests.diff() daily_positive_tests = positive_tests.diff() positive_smoothed = series_utils.smooth_with_rolling_average(daily_positive_tests) negative_smoothed = series_utils.smooth_with_rolling_average( daily_negative_tests, include_trailing_zeros=False ) last_n_positive = positive_smoothed[-lag_lookback:] last_n_negative = negative_smoothed[-lag_lookback:] if any(last_n_positive) and last_n_negative.isna().all(): return pd.Series([], dtype="float64") return positive_smoothed / (negative_smoothed + positive_smoothed)
def test_window_exceeds_series(): """ It should not average the first value. """ series = _series_with_date_index([1, 2]) smoothed = series_utils.smooth_with_rolling_average(series=series, window=5) pd.testing.assert_series_equal(smoothed, _series_with_date_index([1, 1.5]))
def test_exclude_trailing_zeroes(): """ It should not average the first value. """ series = _series_with_date_index([1, 2, 4, 5, 0]) smoothed = series_utils.smooth_with_rolling_average( series=series, window=2, include_trailing_zeros=False) pd.testing.assert_series_equal( smoothed, _series_with_date_index([1, 1.5, 3, 4.5, np.nan]))
def test_sliding_window(): """ It should average within sliding window. """ series = _series_with_date_index([1, 2, 4, 5, 0]) smoothed = series_utils.smooth_with_rolling_average(series=series, window=2) pd.testing.assert_series_equal( smoothed, _series_with_date_index([1, 1.5, 3, 4.5, 2.5]))
def _calculate_smoothed_daily_cases(new_cases: pd.Series, smooth: int = 7): if new_cases.first_valid_index() is None: return new_cases new_cases = new_cases.copy() # Front filling all cases with 0s. We're assuming all regions are accurately # reporting the first day a new case occurs. This will affect the first few cases # in a timeseries, because it's smoothing over a full period, rather than just the first # couple days of reported data. new_cases[:new_cases.first_valid_index() - timedelta(days=1)] = 0 smoothed = series_utils.smooth_with_rolling_average(new_cases, window=smooth) return smoothed