core_lens.base.namespaces.stats#
Statistical analysis namespace for core_lens Result objects.
Exceptions#
Raised when correlation cannot be computed (e.g. fewer than 2 columns). |
Classes#
Correlation methods. |
|
Statistical hypothesis testing methods. |
|
Methods for calculating change over time. |
|
Methods for detecting cross-sectional anomalies. |
|
Methods for detecting time-series anomalies. |
|
Methods for calculating similarity or distance. |
|
Statistical methods on |
Module Contents#
- class core_lens.base.namespaces.stats.CorrelateMethod(*args, **kwds)#
Bases:
enum.EnumCorrelation methods.
- Variables:
PEARSON – Pearson correlation coefficient.
SPEARMAN – Spearman rank correlation.
KENDALL – Kendall Tau correlation.
- PEARSON = 'pearson'#
- SPEARMAN = 'spearman'#
- KENDALL = 'kendall'#
- class core_lens.base.namespaces.stats.TestMethod(*args, **kwds)#
Bases:
enum.EnumStatistical hypothesis testing methods.
- Variables:
T_TEST – Student’s t-test.
MANN_WHITNEY – Mann-Whitney U test.
WILCOXON – Wilcoxon signed-rank test.
KS – Kolmogorov-Smirnov test.
CHI_SQUARE – Chi-square test.
- __test__ = False#
- T_TEST = 't-test'#
- MANN_WHITNEY = 'mann-whitney'#
- WILCOXON = 'wilcoxon'#
- KS = 'ks'#
- CHI_SQUARE = 'chi-square'#
- class core_lens.base.namespaces.stats.ChangeMethod(*args, **kwds)#
Bases:
enum.EnumMethods for calculating change over time.
- Variables:
ABSOLUTE – Absolute difference between periods.
PERCENTAGE – Percentage change between periods.
TREND – Linear trend over time.
- ABSOLUTE = 'absolute'#
- PERCENTAGE = 'percentage'#
- TREND = 'trend'#
- class core_lens.base.namespaces.stats.AnomalyCrossMethod(*args, **kwds)#
Bases:
enum.EnumMethods for detecting cross-sectional anomalies.
- Variables:
ZSCORE – Z-score method.
IQR – Interquartile range method.
PERCENTILE – Percentile-based method.
THRESHOLD – Fixed threshold method.
- ZSCORE = 'zscore'#
- IQR = 'iqr'#
- PERCENTILE = 'percentile'#
- THRESHOLD = 'threshold'#
- class core_lens.base.namespaces.stats.AnomalyTsMethod(*args, **kwds)#
Bases:
enum.EnumMethods for detecting time-series anomalies.
- Variables:
STL – Seasonal-Trend decomposition using LOESS.
CUSUM – Cumulative sum control chart.
MAD – Median Absolute Deviation.
- STL = 'stl'#
- CUSUM = 'cusum'#
- MAD = 'mad'#
- class core_lens.base.namespaces.stats.SimilarityMethod(*args, **kwds)#
Bases:
enum.EnumMethods for calculating similarity or distance.
- Variables:
EUCLIDEAN – Euclidean distance.
COSINE – Cosine similarity.
MAHALANOBIS – Mahalanobis distance.
MANHATTAN – Manhattan distance.
- EUCLIDEAN = 'euclidean'#
- COSINE = 'cosine'#
- MAHALANOBIS = 'mahalanobis'#
- MANHATTAN = 'manhattan'#
- exception core_lens.base.namespaces.stats.CorrelationError#
Bases:
ValueErrorRaised when correlation cannot be computed (e.g. fewer than 2 columns).
- class core_lens.base.namespaces.stats.StatsNamespace(result: core_lens.base.result.Result)#
Statistical methods on
result.stats.*.All methods return a fresh
Result. Computed values always go indata; method parameters go inmetadata.- describe(columns: list[str] | None = None, by: str = 'column') core_lens.base.result.Result#
Per-column or per-entity descriptive statistics.
Uses polars’ in-built methods for mean, std, min, max, quantiles etc.
- Parameters:
columns (list[str] | None, optional) – Numeric columns to describe. Defaults to all numeric cols.
by (str, optional) –
"column"(one row per column, design default) or"entity"(one row per entity with mean/std per column).
- Returns:
New
Resultwith descriptive stats.- Return type:
- Raises:
ValueError – If
byis not\"column\"or\"entity\".
- correlate(columns: list[str], method: CorrelateMethod = CorrelateMethod.PEARSON, across: str = 'entity', group_by: str | None = None, min_obs: int = 3) core_lens.base.result.Result#
Compute pairwise correlations between columns.
Correlations are computed with
scipy.statsand returned as aResultwhosedatais a PolarsDataFrame. Two modes are supported:Pooled (
group_by is None): all rows are treated as one
observation set and a single correlation is computed per column pair. * Per-entity (
group_byset): the result is grouped by the given key column (e.g."mws_id") and one correlation is computed per entity across its remaining rows (typically its time series). This is what enables questions such as “which MWS show the strongest dependence of cropping intensity on annual rainfall?”.- Parameters:
columns – Column names to correlate. Must contain at least two entries. In pooled mode every pair is computed; in per-entity mode the first two columns are used as the pair.
method – The correlation coefficient to compute. One of
CorrelateMethod(PEARSON,SPEARMAN,KENDALL).across –
"entity"or"time". Recorded in result metadata to describe the intended axis of the relationship.group_by – Optional key column to compute one correlation per group (e.g.
"mws_id"). WhenNone, the pooled behaviour is preserved for backward compatibility.min_obs – Minimum number of non-null observations required per group (per-entity mode) or overall (pooled mode) before a correlation is computed. Groups below this threshold are skipped.
- Returns:
- A
Resultwhosedata DataFrame has the following columns:
Pooled mode:
column_a | column_b | correlation | p_valuePer-entity mode:
{group_by} | correlation | p_value | n_obs
Geometry is dropped (
has_geometry=False).
- A
- Return type:
- Raises:
CorrelationError – If fewer than two columns are supplied.
ValueError – If
methodis not a validCorrelateMethod, or ifgroup_byis set but the column is not present in the underlying data.
Examples
Pooled correlation across all MWS in the AoI:
result = aoi.mws.annual.stats.correlate( columns=["dw_precipitation", "ci_cropping_intensity"], method=CorrelateMethod.PEARSON, ) print(result.data.collect(engine="in-memory"))
Per-MWS correlation, ranked by strength of dependence:
top5 = ( aoi.mws.annual.stats .correlate( columns=["dw_precipitation", "ci_cropping_intensity"], method=CorrelateMethod.PEARSON, group_by="mws_id", min_obs=3, ) .data .with_columns(pl.col("correlation").abs().alias("abs_corr")) .sort("abs_corr", descending=True) .select("mws_id", "correlation", "p_value", "n_obs") .limit(5) .collect(engine="in-memory") ) print(top5)
- test(column: str, groups: str | None = None, periods: list[tuple[int, int]] | None = None, against: float | None = None, method: TestMethod | None = None, significance_level: float = 0.05) core_lens.base.result.Result#
Hypothesis test in three modes: group-based, period-based, single-sample.
- Parameters:
column (str) – Numeric column to test.
groups (str | None, optional) – Categorical column to split groups on.
periods (list[tuple[int, int]] | None, optional) – List of
(from_year, to_year)period tuples.against (float | None, optional) – Reference value for a one-sample test.
method (TestMethod | None, optional) – A
TestMethodenum value. Auto-selected via Shapiro-Wilk ifNone.significance_level (float, optional) – Alpha level for
significantflag (default 0.05).
- Returns:
Result whose data has per-group descriptive stats
group | n | mean | std | medianandmetadatawithstatistic,p_value,significant.- Return type:
- Raises:
ValueError – If none of
groups,periods,againstsupplied, or ifmethodis not recognised.
- Under the hood:
Uses
scipy.statsfunctions for statistical testing. -shapirois used for normality testing to auto-select parametric vs non-parametric methods. - Parametric tests:ttest_1samp,ttest_ind. - Non-parametric tests:wilcoxon,mannwhitneyu,ks_2samp.
- change(column: str, from_period: int, to_period: int, method: ChangeMethod = ChangeMethod.ABSOLUTE) core_lens.base.result.Result#
Change between two time periods per entity.
- Parameters:
column (str) – Value column to compute change for.
from_period (int) – Start year/period integer.
to_period (int) – End year/period integer.
method (ChangeMethod, optional) – A
ChangeMethodenum value.
- Returns:
For absolute / percentage: data has
key_col | value_from | value_to | change | pct_change. For trend:key_col | slope | r_squared | direction.- Return type:
- Raises:
ValueError – If
methodis not recognised or year column absent.
- Under the hood:
Absolute and Percentage methods use native Polars expressions.
Trend method uses
scipy.stats.linregressto perform linear regression over the time period.
- anomaly(column: str, mode: str, method: AnomalyCrossMethod | AnomalyTsMethod, baseline: tuple[int, int] | None = None, target_years: tuple[int, int] | None = None, threshold: float = 2.0) core_lens.base.result.Result#
Anomaly detection in cross-sectional or timeseries mode.
- Parameters:
column (str) – Value column to analyse.
mode (str) –
"cross_sectional"or"timeseries".method (AnomalyCrossMethod | AnomalyTsMethod) – Enum value for detection method.
baseline (tuple[int, int] | None, optional) –
(from_year, to_year)inclusive, used as the reference/”normal” period. Required for timeseries; optional for cross-sectional.target_years (tuple[int, int] | None, optional) –
(from_year, to_year)inclusive, years actually scored. Timeseries: defaults to “everything afterbaseline[1]” if omitted. Cross-sectional: defaults to all rows. Must not overlapbaseline.threshold (float, optional) – Sigma / score threshold for anomaly flag (default 2.0).
- Returns:
data has
key_col | anomaly_score | is_anomaly(cross-sectional) orkey_col | <time_col> | anomaly_score | is_anomaly(timeseries, baseline excluded, restricted totarget_yearsif given).<time_col>is the finer-grained date/period column when the data is sub-annual, falling back to plainyearfor annual data.metadataincludesn_entities_dropped.- Return type:
- Raises:
ValueError – If
mode/methodinvalid, observation count too low, orbaselineandtarget_yearsoverlap.
Notes
STL fitting scope. For
AnomalyTsMethod.STLthe decomposition (trend + seasonal + remainder) is fit on the entire series of each entity, baseline and target years together.baselineis used only to calibrate the scale (standard deviation) of the remainder;target_yearsselects which points are scored. Scores areremainder / baseline_remainder_std.Because target observations take part in the fit, the trend and seasonal components can partly absorb target-period anomalies, shrinking the remainder and lowering the score. The effect grows with the size and duration of the anomaly; a sustained shift can be absorbed almost entirely into the trend. Scores therefore measure deviation from a locally fitted trend and seasonal pattern, not from the baseline’s “normal” behaviour, and the method is conservative: it may miss anomalies but is unlikely to over-flag them.
A stricter alternative is to fit on the baseline only and forecast the target period (e.g.
statsmodels.tsa.forecasting.stl.STLForecast), scoring forecast errors against the baseline error spread. This is not implemented.For sub-annual data, residuals are matched to rows by the finer time column (
date/period), not by year, so each observation gets its own score.
- similarity(target: str, columns: dict[str, Any], method: SimilarityMethod = SimilarityMethod.EUCLIDEAN, top_n: int = 10) core_lens.base.result.Result#
Find entities most similar to
targetacrosscolumns.- Parameters:
target – Key value of the reference entity.
columns –
Mapping
{column_name: None | (resolution, filter_dict)}.None— use the column directly fromresult.data.(resolution, filter_dict)— fetch from the entity’s file at the given resolution ("static","annual","sub_annual"). Supportedfilter_dictkeys:"year": int | (int, int)— year equality or range."season": str— season label ("kharif"etc.)."agg": str— time-aggregation before joining ("mean"default,"sum","min","max").
method – Distance metric. A
SimilarityMethodenum value.top_n – Number of most-similar entities to return (default 10).
- Returns:
Result with
key_col | similarity_score | rank.- Raises:
ValueError – If
methodis invalid, no columns can be resolved, ortargetis not found.
- Under the hood:
Euclidean, Manhattan, and Cosine distances are computed using optimized NumPy operations.
Mahalanobis distance uses
scipy.spatial.distance.mahalanobis.