$npx -y skills add DataZooDE/anofox-forecast --skill anofox-forecast-edaExploratory data analysis and data quality for the anofox_forecast DuckDB extension — 34 per-series statistics, data-quality scoring, quality-report summaries, and 117 tsfresh-compatible feature extraction. Use before forecasting to understand series characteristics (length, gaps
| 1 | # Anofox Forecast — EDA & Data Quality Cheat Sheet |
| 2 | |
| 3 | **Extension:** `anofox_forecast` v0.15.3 | **DuckDB:** v1.4.5 LTS / v1.5.4+ | **Dual naming:** `ts_*` and `anofox_fcst_ts_*` |
| 4 | |
| 5 | Understand your data before modelling it: per-series statistics, data-quality scores, feature vectors for ML. |
| 6 | |
| 7 | ## Statistics |
| 8 | |
| 9 | ### `ts_stats` (table function) — 34 metrics per series |
| 10 | |
| 11 | ```sql |
| 12 | ts_stats(source VARCHAR, group_col COLUMN, date_col COLUMN, value_col COLUMN, |
| 13 | frequency VARCHAR) → TABLE |
| 14 | ``` |
| 15 | |
| 16 | Returns 34 columns: `length`, `n_nulls`, `n_nan`, `n_zeros`, `n_positive`, `n_negative`, `n_unique_values`, `is_constant`, `n_zeros_start`, `n_zeros_end`, `plateau_size`, `plateau_size_nonzero`, `mean`, `median`, `std_dev`, `variance`, `min`, `max`, `range`, `sum`, `skewness`, `kurtosis`, `tail_index`, `bimodality_coef`, `trimmed_mean`, `coef_variation`, `q1`, `q3`, `iqr`, `autocorr_lag1`, `trend_strength`, `seasonality_strength`, `entropy`, `stability`, plus date-derived `expected_length` and `n_gaps`. |
| 17 | |
| 18 | ```sql |
| 19 | -- Every stat for every series |
| 20 | SELECT * FROM ts_stats('sales', product_id, ds, y, '1d'); |
| 21 | |
| 22 | -- Quick sanity check |
| 23 | SELECT product_id, length, n_nulls, n_gaps, trend_strength, seasonality_strength |
| 24 | FROM ts_stats('sales', product_id, ds, y, '1d') |
| 25 | WHERE length < 30 OR n_gaps > 0 |
| 26 | ORDER BY n_gaps DESC; |
| 27 | ``` |
| 28 | |
| 29 | ### `ts_stats_agg` (aggregate) |
| 30 | |
| 31 | For custom `GROUP BY` shapes. Takes `(date, value)` — DO NOT wrap in `LIST(...)`. |
| 32 | |
| 33 | ```sql |
| 34 | ts_stats_agg(date_col TIMESTAMP, value_col DOUBLE) |
| 35 | → STRUCT(length UBIGINT, n_nulls UBIGINT, …, mean DOUBLE, …) |
| 36 | ``` |
| 37 | |
| 38 | ```sql |
| 39 | SELECT product_id, |
| 40 | ts_stats_agg(ds, y).mean AS mean, |
| 41 | ts_stats_agg(ds, y).seasonality_strength AS seas_str |
| 42 | FROM sales GROUP BY product_id; |
| 43 | ``` |
| 44 | |
| 45 | ### `ts_stats_by` — alias for `ts_stats` |
| 46 | |
| 47 | ### `ts_stats_summary` — aggregate stats across all groups |
| 48 | |
| 49 | Summarise the per-group stats into panel-level statistics (mean, std, min, max of each metric). |
| 50 | |
| 51 | ```sql |
| 52 | SELECT * FROM ts_stats_summary('sales', product_id, ds, y, '1d'); |
| 53 | ``` |
| 54 | |
| 55 | ## Data quality |
| 56 | |
| 57 | ### `ts_data_quality` (table function) — per-series score card |
| 58 | |
| 59 | ```sql |
| 60 | ts_data_quality(source VARCHAR, unique_id_col COLUMN, date_col COLUMN, value_col COLUMN, |
| 61 | n_short INTEGER, frequency VARCHAR) → TABLE |
| 62 | ``` |
| 63 | |
| 64 | Returns `unique_id` (group column **renamed** — the extension normalises it to `unique_id` in this output, even if the input col was `product_id`), plus `overall_score`, `structural_score`, `temporal_score`, `magnitude_score`, `behavioral_score`, `n_gaps`, `n_missing`, `is_constant`. |
| 65 | |
| 66 | `n_short` is the series-length threshold below which a series is flagged short (typical: 14 for daily, 12 for monthly). |
| 67 | |
| 68 | ```sql |
| 69 | SELECT * FROM ts_data_quality('sales', product_id, ds, y, 14, '1d'); |
| 70 | |
| 71 | -- Rank series by quality (note: output col is `unique_id`, not `product_id`) |
| 72 | SELECT unique_id, overall_score, structural_score, temporal_score |
| 73 | FROM ts_data_quality('sales', product_id, ds, y, 14, '1d') |
| 74 | ORDER BY overall_score; |
| 75 | ``` |
| 76 | |
| 77 | ### `ts_data_quality_agg` (aggregate) |
| 78 | |
| 79 | Same as `ts_stats_agg` — takes `(date, value)`, returns a STRUCT. |
| 80 | |
| 81 | ```sql |
| 82 | SELECT product_id, |
| 83 | ts_data_quality_agg(ds, y).overall_score AS q |
| 84 | FROM sales GROUP BY product_id; |
| 85 | ``` |
| 86 | |
| 87 | ### `ts_data_quality_summary` — panel-level roll-up |
| 88 | |
| 89 | ```sql |
| 90 | SELECT * FROM ts_data_quality_summary('sales', product_id, ds, y, 14); |
| 91 | ``` |
| 92 | |
| 93 | ### `ts_quality_report` — human-readable report |
| 94 | |
| 95 | ## Feature extraction (117 tsfresh-compatible features) |
| 96 | |
| 97 | ### `ts_features_by` — extract all 117 features |
| 98 | |
| 99 | ```sql |
| 100 | ts_features_by(source VARCHAR, group_col COLUMN, date_col COLUMN, value_col COLUMN) → TABLE |
| 101 | ``` |
| 102 | |
| 103 | Returns the group column + 116 feature columns: `mean`, `standard_deviation`, `skewness`, `kurtosis`, `length`, `linear_trend_slope`, `autocorrelation_lag1`, … |
| 104 | |
| 105 | ```sql |
| 106 | -- Extract all features |
| 107 | SELECT * FROM ts_features_by('sales', product_id, ds, y); |
| 108 | |
| 109 | -- Filter by feature values |
| 110 | SELECT product_id, mean, linear_trend_slope |
| 111 | FROM ts_features_by('sales', product_id, ds, y) |
| 112 | WHERE length > 30 AND autocorrelation_lag1 > 0.5; |
| 113 | ``` |
| 114 | |
| 115 | ### `ts_features` (scalar) |
| 116 | |
| 117 | Scalar over `(date, value)`. Same 116 outputs as struct fields. |
| 118 | |
| 119 | ### `ts_features_list` / `ts_features_table` |
| 120 | |
| 121 | Discover the feature catalogue: |
| 122 | |
| 123 | ```sql |
| 124 | SELECT column_name, feature_name, parameter_suffix |
| 125 | FROM ts_features_list() |
| 126 | WHERE feature_name LIKE '%autocorr%'; |
| 127 | ``` |
| 128 | |
| 129 | ### Custom feature subsets |
| 130 | |
| 131 | Configure via JSON or CSV: |
| 132 | |
| 133 | ```sql |
| 134 | -- Load a JSON config |
| 135 | SELECT * FROM ts_features_by('sales', id, ds, y, |
| 136 | ts_features_config_from_json('{"features": ["mean", "std", "autocorrelation_lag1"]}')); |
| 137 | |
| 138 | -- Or C |