compute_features extracts one row of native summaries per series.
compare_feature_coverage fits a scaler, PCA projection, and grid on the
retained real reference panel, then projects each synthetic corpus into that
same space. Adding another corpus leaves existing scores unchanged under
these defaults. Corpus size still affects occupancy; compare matched sizes.
The twelve-feature schema is native_v1. Its feature names, order,
definitions, normalization, and undefined-value rules are fixed; changes to
those contracts require a new schema identifier. Period and window settings
remain explicit extraction parameters, recorded with the schema. Raw-input
coverage metadata records both the requested and effective window size.
These are SynForecast definitions, not a reproduction of an external feature
package.
The first two PCA components keep only part of the real-feature variance:
54–84% on the M4 panels we evaluated, with visible losses for spike and
variance-shift differences. Treat the map as a two-dimensional diagnostic,
not as comprehensive feature-space coverage. Full-feature nearest-neighbour
distances agree with the map on the aggregate ordering of corpora but often
disagree on individual series, and grid boundaries alone can move a score
noticeably. Report full-feature distances and real-calibrated coverage
alongside the map; neither a single radius nor a grid score establishes
general fidelity.
Choosing a period and window
Extraction never reads timestamps: declare the period yourself, either in observations (seasonal_period) or through freq, which uses the same
convention as the preset pools (hourly 24, daily 7, weekly 52, monthly 12,
quarterly 4, yearly none). Without either, no seasonality is declared, the
two seasonal features are zero for every series, and
compare_feature_coverage drops them as constant columns and logs a warning.
A period of 1 means no seasonality.
An availability check of all 100,000 M4 training series supports the
following settings, using at most the last 512 observations:
All M4 series are finite in all twelve features under these settings. Weekly
uses M4’s nonseasonal convention: its two seasonal features are constant and
annual seasonality is unmeasured. A 52-week period excludes 65 of the 359
Weekly series, which lack two full cycles; report that as a separate eligible
population. Daily’s seven-day cycle is an explicit assumption (M4 metadata
declares it nonseasonal), and Hourly’s 24-hour period covers only that cycle.
n_jobs sets the native worker threads; the default -1 uses all CPUs and
honours RAYON_NUM_THREADS, as in the generators.
Native feature contracts
All features require at least three observations. Every feature is computed on the series normalized to zero mean and unit population variance, so none depends on the input scale or offset. Constant input maps to zero; normalization scales by the maximum absolute value first to avoid overflow.
When a declared period cannot fit two cycles, the three decomposition-based
features (
trend_strength, seasonal_strength, spike) are NaN, so a panel
of mixed lengths never mixes two decomposition definitions. Without a
declared period, the decomposition uses a trend window near one tenth of the
series length.
For lumpiness and shift features, window_size can explicitly set the width
(at least 2). The default is the declared period, or 10 without one; at least
two full windows are required. window_size does not change the decomposition
or seasonal ACF. For short nonseasonal panels, a fixed window_size=5 keeps
series of 10 to 19 observations that a ten-observation window would exclude.
Record the width with cached features and use the same setting for real and
synthetic corpora. ACF uses the full mean-centered sum of squares in its
denominator at every lag and returns zero if that sum is at most machine
epsilon. Crossing counts and other estimates remain length-dependent, so
match length distributions as well as sample sizes.
Reading the scores
miscoverage: uncovered real cells divided by all grid cells.reverse_miscoverage: synthetic-only cells divided by all grid cells, restricted to the grid’s bounding box.uncovered_real_cell_fraction: uncovered real cells divided by occupied real cells.uncovered_real_series_fraction: retained real series in uncovered cells divided by all retained real series.synthetic_out_of_range_fraction: synthetic series outside the box divided by all retained synthetic series, including those outside it.
CoverageResult.summary() returns these scores with the retained counts as a
flat dict. Results compare by identity, since several fields are arrays.
The default real-range grid adds one percent padding per axis. It excludes
out-of-range synthetic points rather than clipping them onto edge cells.
Out-of-range is evidence of extrapolation in the displayed coordinates, not
proof of unrealistic data. Differences in discarded dimensions may be hidden;
inspect explained variance and the features themselves. Zero miscoverage is
not evidence of predictive fidelity or privacy.
missing="raise" rejects undefined feature values. Explicit missing="drop"
evaluates complete cases, logs exclusions, and returns retained/dropped IDs
and reasons. Counts and fractions describe the retained population. A column
is constant when its range in the fitting population is at most 1e-12, scaled
by its magnitude when that exceeds one. Real-constant columns cannot enter
reference PCA; synthetic deviations from those reference values are counted
separately.
At least two retained real series and one variable feature are needed for the
default fit. Rank-one PCA uses a zero second coordinate. Result metadata stores
fitted parameters, a space identifier, and a reference fingerprint. A shared
space identifier does not make different reference samples or sample sizes
interchangeable. Persist extraction settings with cached native feature frames;
precomputed frames do not carry an implicit native schema, and
precomputed=True rejects the extraction keywords rather than ignoring them.
fit="pooled" and grid_range="pooled" provide a joint-fitting sensitivity
case. Synthetic outliers can shift that space and its bin widths. Optional
t-SNE requires embedding="tsne", fit="pooled", seed=..., scikit-learn,
and more than 30 retained series for its fixed perplexity of 30. It fits all
declared points jointly and does not project new points. Pooled PCA uses the
same occupancy measure but does not reproduce Kang’s t-SNE experiment.
API
compute_features
native_v1 schema fixes feature definitions and order. Every
feature is computed on the series normalized to zero mean and unit
variance, so none depends on the input scale. Features depend on
observation counts, not on the calendar of time_col.
crossing_points is a raw count and therefore grows with series
length, so compare panels of comparable length. Short series yield NaN
for undefined features and one summary log warning; invalid raw inputs
raise.
Parameters:
Returns:
feature_coverage
compare_feature_coverage for the protocol.
compare_feature_coverage
time_col are never compared across corpora,
and crossing_points is a raw count that grows with series length.
Generate synthetic series at lengths comparable to the real panel; the
preset pools default to fixed lengths that may not match yours.
Parameters:
Returns:
CoverageDiagnostics
drop_reasons maps each dropped ID to the names of its non-finite
features. Instances compare by identity, not by value.
CoverageDiagnostics.drop_reasons
CoverageDiagnostics.dropped_ids
CoverageDiagnostics.n_input
CoverageDiagnostics.retained_ids
CoverageResult
miscoverage divides uncovered real cells by all grid cells;
uncovered_real_cell_fraction divides by occupied real cells instead.
reverse_miscoverage counts synthetic-only cells within the grid.
Out-of-range synthetic points are excluded from occupancy, but included
in the denominator of synthetic_out_of_range_fraction. These scores
describe retained series; inspect the diagnostics before comparing corpora.
explained_variance is None for t-SNE. metadata holds the fitted
parameters and the reference_id fingerprint of the retained real
features; each result owns its copy. Instances compare by identity, since
several fields are arrays; compare summary() values instead.
CoverageResult.constant_features_dropped
CoverageResult.embedding
CoverageResult.explained_variance
CoverageResult.features_used
CoverageResult.fit
CoverageResult.grid_edges
CoverageResult.grid_range
CoverageResult.metadata
CoverageResult.miscoverage
CoverageResult.n_bins
CoverageResult.n_synthetic_out_of_range
CoverageResult.plot
CoverageResult.real_diagnostics
CoverageResult.real_embedding
CoverageResult.real_occupancy
CoverageResult.reverse_miscoverage
CoverageResult.space_id
CoverageResult.summary
pd.DataFrame({name: r.summary() for name, r in results.items()}).T
tabulates the output of :func:compare_feature_coverage.

