Skip to content

Running DFS

deep_feature_synthesis() walks the database from a target table, stacking primitives up to max_depth, and returns a (feature_matrix, features) tuple.

feature_matrix, features = tusk.deep_feature_synthesis(
    database=db,
    target_table="customers",
    agg_primitives=["mean", "count"],
    trans_primitives=["month", "weekday"],
    max_depth=2,
)

The feature matrix has one row per visible row of the target table, keyed by its primary key. The target must have a primary_key. Without one, compilation raises SchemaError.

Lazy out, always

tusk builds one query plan. The feature matrix comes back on the backend you put in, as that backend's native lazy table, if the backend supports lazy tables. You decide when to compute it, the same way you compute anything else on that backend.

On a backend with no separate lazy type, such as pandas or pyarrow, the feature matrix comes back already computed. Validation is the only part of tusk that computes on its own, and only for the checks you ask for.

Definitions without computation

features_only=True returns only the FeatureList of Feature definitions. It skips compilation entirely. Synthesis raises SchemaError instead if the walk builds no features at all. Feed the definitions back later:

features = tusk.deep_feature_synthesis(db, "customers", features_only=True)
feature_matrix = features.apply(db_new)

This is how you apply a feature set fitted on training data to new data. All features in a FeatureList, and so all features passed to apply_features(), must share one target table.

Cutoff times

cutoff_time is one global value, not one value per row. Only rows whose row_creation_time is at or before it are visible.

It must be a datetime, not just a date. A date has no time of day, so its behavior can differ across backends.

Its time zone awareness must match every Datetime column in the database, including the ones beyond the declared row creation times. A comparison between a tz-aware timestamp and a naive one has no defined ordering. A mismatch therefore raises ValidationError up front, for the whole database. The cutoff time reaches columns beyond the ones it filters on. TimeSince subtracts it from its feature's input column. validate(database="consistent_time_zones") reports the same mixing without a cutoff time.

It filters the target table too. The feature matrix can therefore have fewer rows than the target. A row that did not exist yet at the cutoff time has no features to compute.

Tables with no row_creation_time are timeless and pass through unfiltered. A cutoff time on a database that declares none is therefore silently a no-op.

A cutoff time also reaches inside a row. A table that declares row_update_times gives back the earlier value of every column filled in after the cutoff time. A taxi fare settled at the end of a ride is therefore not visible from a cutoff time taken while the ride was still running. This happens before any join or aggregation. Aggregated and stacked features therefore see those earlier values too.

With features_only=True, the cutoff time has no effect, because compilation does not run and feature definitions do not record it.

Warnings

DFS skips what it cannot build and warns instead of failing:

  • UnmatchedPrimitiveWarning: a requested primitive matched no column of its input dtypes anywhere in the walk. It therefore contributed no features.
  • CategoricalDtypeWarning: DFS skipped a Categorical or Enum column because the primitive requires a string input.

Both have their own class so you can filter them independently.