SQL Auto-Feature Families
GraphReduce can generate relational features while compiling a graph through
GraphReduce.do_transformations_sql(). A feature family is a bounded group of
SQL expressions created when a child relation is reduced to its parent key.
Only base is enabled by default. Every other family is opt-in.
At a glance
| Family | Question it answers | Main requirement |
|---|---|---|
base |
How much activity occurred, and what are its basic summaries? | None |
temporal |
How did numeric values differ across lookback windows? | date_key and ts_periods |
sequence |
Was activity steady, recent, or bursty? | date_key and ts_periods |
conditional |
What kinds of activity occurred? | Eligible categories or predicate annotations |
episode |
How many rows and distinct events occurred? | A primary key for distinct-event counts |
semantic |
Which domain concepts should become features? | annotation_expressions or auto-annotation |
context |
How did a value compare with its peers? | Explicit context_keys |
Configure a node
Feature families can be selected on an individual SQL node:
from graphreduce.enum import ComputeLayerEnum
from graphreduce.node import DuckdbNode
events = DuckdbNode(
fpath="events",
fmt="sql",
pk="event_id",
prefix="evt",
date_key="event_time",
compute_layer=ComputeLayerEnum.duckdb,
feature_families=("base", "temporal", "sequence", "conditional"),
ts_periods=(7, 30, 90, 365),
feature_family_max_columns=4,
categorical_cardinality_threshold=20,
categorical_top_k=5,
)
Or set schema-independent defaults on the graph:
graph = GraphReduce(
parent_node=customers,
compute_layer=ComputeLayerEnum.duckdb,
auto_features=True,
feature_families=("base", "temporal", "sequence"),
ts_periods=(7, 30, 90, 365),
feature_family_max_columns=4,
categorical_cardinality_threshold=20,
categorical_top_k=5,
)
graph.do_transformations_sql()
Graph-level feature settings default to None. When omitted, the node's value
is preserved; when supplied, the graph value overrides every node during graph
hydration. Node-specific concepts such as annotation_expressions and
context_keys should remain on the node whose schema defines them.
Time windows
ts_periods contains lookback lengths in days. Each period is evaluated
relative to the current point-in-time reference when one is available, or to
the graph's cut_date otherwise.
Three controls work together:
date_keyidentifies the event timestamp on the node.compute_period_valandcompute_period_unitbound all available history.ts_periodscreates summaries within that already-bounded history.
A 365-day ts_periods entry cannot recover rows if the graph's compute window
contains only 90 days. Set ts_periods=() to disable rolling-window features.
base: schema-aware summaries
The base family inspects a sample and emits aggregations appropriate for the
inferred physical and semantic types:
- identifier-like columns contribute a count without duplicating the same count for every identifier;
- numeric columns use compatible functions from
type_func_map; - booleans produce true counts and shares;
- dates and timestamps produce configured date summaries;
- eligible categorical columns produce distinct counts and bounded count/share/presence features; and
- text-like columns can produce inexpensive length, word-count, empty-value, URL, number, and punctuation summaries.
For a time-series relation, base also creates recency, windowed event counts,
and adjacent-window change ratios.
A categorical column is eligible only when its observed cardinality is at most
categorical_cardinality_threshold. At most categorical_top_k frequent
values are expanded; an other bucket is added when the top-value limit omits
eligible values. Higher-cardinality columns are skipped.
Text summaries are controlled independently by auto_text_features. They do
not tokenize, embed, or call a language model.
temporal: windowed numeric values
For each selected numeric column and lookback period, temporal generates
windowed sum, avg, min, and max features. The reduce key, date column,
and identifier-like values are excluded.
Approximate feature count for N selected numeric columns and P periods:
Use feature_family_max_columns to keep this family bounded on wide tables.
sequence: activity shape
sequence describes cadence without exposing an ordered event list. It can
generate:
- events per day within each period;
- each period's share of lifetime activity;
- burst ratios between adjacent periods;
- the lifetime active span; and
- lifetime activity per active day.
These features help distinguish steady activity, recent bursts, and dormant entities with similar lifetime counts.
conditional: activity composition
conditional selects conditions from two sources:
- predicate columns created by semantic annotation; and
- frequent values from categorical columns within the cardinality threshold.
For each selected condition and period, it generates count, share, and presence features, plus count-change ratios between adjacent periods. Semantic predicates are selected before generic categories.
This family can grow quickly. Bound both feature_family_max_columns and
categorical_top_k.
episode: rows versus distinct events
episode emits a total row count and, when a primary key is available, a
distinct primary-key count. Time-series nodes also receive those counts by
lookback period.
This distinction is useful after many-to-many joins: the row count measures join volume, while the distinct count better approximates underlying events.
semantic: explicit domain meaning
semantic compiles caller-supplied SQL expressions before reduction:
events = DuckdbNode(
# ...normal node configuration...
feature_families=("base", "semantic", "conditional", "temporal"),
annotation_expressions={
"is_verified": "{status} = 'verified'",
"is_high_value": "{amount} >= 100",
"line_value": ("value", "{amount} * {quantity}"),
},
)
A plain expression is treated as a predicate and becomes a 0/1 indicator.
The ("value", expression) form preserves a numeric result for normal
aggregation. Braced placeholders are resolved against the node's columns.
The family does not guess business meaning from names. With
auto_annotate_features=True, it can additionally infer a bounded set of
categorical indicators, text indicators, and category-gated numeric values.
context: peer-relative values
context compares numeric values within an explicit peer group before the
child is reduced:
items = DuckdbNode(
# ...normal node configuration...
feature_families=("base", "context"),
context_keys=("category_id",),
)
For each context key, GraphReduce can create peer-group size and the difference between a row's numeric value and its peer-group average. The resulting values are then aggregated to the parent.
GraphReduce deliberately does not infer peer groups. A plausible but incorrect
group creates misleading features, so context_keys is required.
Keep the feature budget deliberate
The most important controls are:
| Setting | Controls |
|---|---|
feature_families |
Families enabled on a node |
ts_periods |
Count and duration of time windows |
feature_family_max_columns |
Source columns or conditions used by expanding families |
feature_family_max_features_per_column |
Derived features emitted per selected column |
categorical_cardinality_threshold |
Maximum observed cardinality for automatic categories |
categorical_top_k |
Values expanded per eligible category |
auto_text_features |
Automatic text summaries |
annotation_expressions |
Deterministic domain predicates and values |
context_keys |
Explicit peer groups |
A practical rollout is:
- Establish a baseline with
("base",). - Add
temporalfor meaningful numeric measurements. - Add
sequencewhen cadence or burstiness may matter. - Add
conditionalfor important statuses or categories. - Add
episodewhen rows and logical events differ. - Add
semanticfor known domain concepts. - Add
contextonly when the peer group is unambiguous.
Feature counts multiply across columns, periods, relations, and graph hops. Enable a family where its signal is plausible instead of applying a maximal configuration to every node.