Skip to main content

featkit — automated feature store generation from relational facts tables

Project description

featkit

featkit is a Python framework for automated feature store generation from relational facts tables.

It implements a three-layer architecture:

  • Layer 1 — input facts table with typed columns (ID, time, categorical, measurement)
  • Layer 2 — horizontal concept table built via pivot (2A) and distributional aggregations (2B)
  • Layer 3 — temporal feature table produced by sliding operators over the Layer 2 columns

The framework is engine-agnostic: the same pipeline definition produces either a standalone SQL script (Snowflake, Databricks SQL, Spark SQL) or a lazy PySpark execution plan, with the choice abstracted behind a code generator interface.

Key concepts

Layer What it does
Layer 2A — Pivot GROUP BY (ID, time) + CASE WHEN per categorical combination × measurement × aggregator
Layer 2B — Distributional Per-categorical CTEs computing entropy, HHI, dominant proportion, mode, count
Layer 3 — Temporal Sliding window operators (PROM_U, SUM_U, CREC, FREQ, REC, …) over all Layer 2 columns

Installation

pip install featkit

Quickstart

from featkit import FeatureStorePipeline, FeatureStoreConfig
from featkit.dataset import SimpleDataset
from featkit.fields import IDField, TimeField, CategoricalField, MeasurementField
from featkit.enums import MeasurementType, TimeGranularity, CategoricalTreatment
from featkit.generators.sql import SnowflakeSQLCodeGenerator

# Define schema
fields = [
    IDField(name="ID_CLIENTE"),
    TimeField(name="PERIODO",
              source_granularity=TimeGranularity.MONTHLY,
              target_granularity=TimeGranularity.MONTHLY),
    CategoricalField(name="SECTOR", treatment=CategoricalTreatment.PIVOT,
                     allowed_values=["RETAIL", "CORP", "PYME"]),
    CategoricalField(name="CANAL",  treatment=CategoricalTreatment.PIVOT,
                     allowed_values=["DIGITAL", "PRESENCIAL", "TELEFONO"]),
    MeasurementField(name="MTO", measurement_type=MeasurementType.MONTO),
    MeasurementField(name="TRX", measurement_type=MeasurementType.CANTIDAD),
]

dataset = SimpleDataset(
    source_reference="MY_DB.MY_SCHEMA.FACTS_TABLE",
    fields=fields,
)

config = FeatureStoreConfig(
    dataset=dataset,
    output_schema="MY_DB.MY_SCHEMA",
    output_table_prefix="FS",
    time_windows=[3, 6, 9, 12],
)

pipeline = FeatureStorePipeline(config).build()
output = pipeline.run(SnowflakeSQLCodeGenerator())

output.save("./output")
# Writes: output/script.sql, output/dag.json, output/diagram.md

Feature naming anatomy

Every feature produced by featkit has a deterministic, human-readable name built from fixed segments separated by __ (double underscore). Understanding the segments lets you decode any feature name without looking at the code.

There are four families of features, each with its own naming pattern.


Layer 2A — Pivot features

Pattern: {AGG}__{MEASUREMENT}[__{FIELD}_{VALUE}…]

Segment Source Example
AGG Layer2Aggregator enum SUM, COUNT, AVG, MIN, MAX
MEASUREMENT MeasurementField.name MTO, TRX
FIELD_VALUE CategoricalField.name + _ + value, one per non-marginal field, sorted alphabetically by field name CANAL_DIGITAL, SECTOR_RETAIL

The valid aggregators for each MEASUREMENT depend on its MeasurementType. Only contract-permitted aggregator–measurement combinations are generated.

Measurement type Semantic meaning Valid AGG values
MONTO Monetary amount SUM, MAX, MIN, AVG
CANTIDAD Count / quantity SUM
TICKET Average ticket size AVG
FLAG Binary indicator MAX
FECHA Date / timestamp MAX, MIN
BALANCE Point-in-time balance MAX, MIN, AVG
TIME_DIFF Duration / elapsed time SUM, AVG, MAX, MIN
ESTADISTICO Generic statistic SUM, AVG, MAX, MIN, COUNT

Categorical fields set to the ∅ marginal (no filter on that dimension) are omitted from the name entirely, so the name implicitly aggregates over all values of that dimension.

SUM__MTO                                  # global — all sectors, all channels
SUM__MTO__CANAL_DIGITAL                   # CANAL=DIGITAL, marginal over SECTOR
SUM__MTO__SECTOR_RETAIL                   # SECTOR=RETAIL, marginal over CANAL
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL    # CANAL=DIGITAL and SECTOR=RETAIL (alphabetical order)
SUM__TRX__CANAL_PRESENCIAL                # sum of TRX (CANTIDAD → only SUM is valid) for PRESENCIAL channel

Layer 2B — Distributional features

Pattern: {CATEGORICAL}__{MEASUREMENT}__{AGG}__{METRIC}

Segment Source Example
CATEGORICAL CategoricalField.name CANAL, SECTOR
MEASUREMENT MeasurementField.name MTO
AGG Layer2Aggregator enum SUM
METRIC DistributionalMetric enum ENTROPY, HHI, DOMINANT_PROPORTION, MODE, COUNT

These columns capture the shape of the value distribution of a categorical field, weighted by the aggregated measurement.

Metric What it measures
ENTROPY Shannon entropy of the category distribution — higher means more uniform spread
HHI Herfindahl-Hirschman Index — concentration; higher means more dominated by one value
DOMINANT_PROPORTION Share of the most common category value
MODE The most frequent category value (output type: categorical)
COUNT Number of distinct observed values
CANAL__MTO__SUM__ENTROPY            # entropy of channel distribution by amount
SECTOR__TRX__SUM__HHI               # HHI of sector distribution by transaction count (CANTIDAD → only SUM)
CANAL__MTO__SUM__MODE               # dominant channel by amount (categorical output)

Layer 2C — Ratio features

Pattern: {NUMERATOR}__over__{DENOMINATOR}

where NUMERATOR and DENOMINATOR are full Layer 2A pivot feature names. The denominator is always a proper marginal projection of the numerator: it has at least one categorical dimension set to ∅ that is non-∅ in the numerator, and no contradicting values.

The underlying value is numerator / NULLIF(denominator, 0) computed per entity per period.

Enabled by setting include_ratios=True (requires include_marginals=True). The ratio_mode parameter controls which denominators are paired with each numerator:

ratio_mode Denominators considered Ratios produced per numerator
RatioMode.ALL_PROJECTIONS (default) Every proper marginal projection (partial or fully marginalised) One per valid denominator
RatioMode.GLOBAL_TOTAL Only the fully-marginalised column (all fields ∅) Exactly one — the share of the grand total
# With RatioMode.ALL_PROJECTIONS (default):
# Numerator: DIGITAL channel + RETAIL sector → three denominators
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__over__SUM__MTO__SECTOR_RETAIL   # share within RETAIL
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__over__SUM__MTO__CANAL_DIGITAL   # share within DIGITAL
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__over__SUM__MTO                  # share of total

# With RatioMode.GLOBAL_TOTAL:
# Same numerator → only the grand-total denominator
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__over__SUM__MTO                  # share of total only

Layer 3 — Temporal features

Pattern: {L2_NAME}__{OPERATOR}__{DIRECTION}[__{WINDOW}]

L2_NAME is the full name of any Layer 2A, 2B, or 2C feature. The temporal segments are appended at the end.

Segment Source Notes
OPERATOR TemporalOperator enum See table below
DIRECTION TimeWindowDirection enum BACKWARD or FORWARD
WINDOW window_size (integer, number of periods) Omitted for point-in-time operators

Temporal operators

Operator Type Description
PROM_U Windowed Arithmetic mean of the monthly values over the window — each period contributes equally regardless of its volume
PROM_P Windowed Volume-proportional weighted mean — each period's contribution is weighted by its share of the total aggregated value across the window; weights are derived automatically from the data, no user configuration required
SUM_U Windowed Unweighted sum of the monthly values over the window
SUM_P Windowed Volume-weighted sum over the window (analogous weighting to PROM_P)
MIN_U Windowed Minimum value observed in the window
MAX_U Windowed Maximum value observed in the window
CREC Windowed Growth rate across the window
FREQ Windowed Count of periods in the window where the value was non-null and strictly greater than 0
XM Windowed 1 if every period in the window had a non-null and strictly positive value, 0 otherwise — an all-or-nothing activity indicator (e.g. 1 means the customer was active on every single month in the window)
MEDIA_ABS Windowed (composed) Mean absolute deviation over the window
RATIO Windowed (composed) Ratio of two sub-windows
ULT_MES Point-in-time Value at the most recent period (no window suffix)
PREV_MES Point-in-time Value at the immediately preceding period (no window suffix)
REC Point-in-time Recency — periods elapsed since last non-null / non-zero observation (no window suffix)

Valid operators per Layer 2 output type

Output type Valid operators
NUMERIC PROM_U, PROM_P, SUM_U, SUM_P, MIN_U, MAX_U, CREC, FREQ, XM, ULT_MES, PREV_MES, MEDIA_ABS, RATIO
FLAG ULT_MES, PREV_MES, FREQ, XM, REC
CATEGORICAL ULT_MES, PREV_MES, REC
TEMPORAL ULT_MES, PREV_MES, REC, MIN_U, MAX_U, CREC

Examples

# Average amount (DIGITAL + RETAIL) over the last 6 months
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__PROM_U__BACKWARD__6

# Total transaction sum for RETAIL sector in the last 3 months (CANTIDAD → only SUM valid)
SUM__TRX__SECTOR_RETAIL__SUM_U__BACKWARD__3

# Most recent value of the CANAL entropy (by amount)
CANAL__MTO__SUM__ENTROPY__ULT_MES__BACKWARD

# Share of DIGITAL/RETAIL in total portfolio, averaged over last 12 months
SUM__MTO__CANAL_DIGITAL__SECTOR_RETAIL__over__SUM__MTO__PROM_U__BACKWARD__12

# Recency of the dominant channel (MODE is categorical → only REC/ULT_MES/PREV_MES valid)
CANAL__MTO__SUM__MODE__REC__BACKWARD

Quick-reference: full name structure

┌─ Layer 2A pivot ──────────────────────────────────────────────────┐
│  AGG  __  MEASUREMENT  [__  FIELD_VALUE  …]                       │
└───────────────────────────────────────────────────────────────────┘

┌─ Layer 2B distributional ─────────────────────────────────────────┐
│  CATEGORICAL  __  MEASUREMENT  __  AGG  __  METRIC                │
└───────────────────────────────────────────────────────────────────┘

┌─ Layer 2C ratio ──────────────────────────────────────────────────┐
│  {Layer 2A name}  __over__  {Layer 2A name}                       │
└───────────────────────────────────────────────────────────────────┘

┌─ Layer 3 temporal (windowed) ─────────────────────────────────────┐
│  {Layer 2A/2B/2C name}  __  OPERATOR  __  DIRECTION  __  WINDOW   │
└───────────────────────────────────────────────────────────────────┘

┌─ Layer 3 temporal (point-in-time) ────────────────────────────────┐
│  {Layer 2A/2B/2C name}  __  OPERATOR  __  DIRECTION               │
└───────────────────────────────────────────────────────────────────┘

Architecture

See docs/general_plan.md for the full implementation plan.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

featkit-0.4.3.tar.gz (81.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

featkit-0.4.3-py3-none-any.whl (62.7 kB view details)

Uploaded Python 3

File details

Details for the file featkit-0.4.3.tar.gz.

File metadata

  • Download URL: featkit-0.4.3.tar.gz
  • Upload date:
  • Size: 81.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for featkit-0.4.3.tar.gz
Algorithm Hash digest
SHA256 37b16ff0b26f0388550c61412285758ffc4f32ba24e7a0acdbe984d3c15ed768
MD5 d10b2e4687490c4b286ea951e344cc55
BLAKE2b-256 e0420cd873c35df9697ada0938b228928d3b22dddb80e3fc489db11f3aa46cae

See more details on using hashes here.

Provenance

The following attestation bundles were made for featkit-0.4.3.tar.gz:

Publisher: publish.yml on Mirkiux/featkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file featkit-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: featkit-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 62.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for featkit-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 9a1d2b0a0c9529512d620c0e409f0656c3be31d9bc766ebb3a3aa984dccf1ed9
MD5 00a7a4e8c02d0986d414046d44918c51
BLAKE2b-256 e533e46e9a29cea85d74fc8e3be90ae7553fc518bdde0dc6e032d37992eecc00

See more details on using hashes here.

Provenance

The following attestation bundles were made for featkit-0.4.3-py3-none-any.whl:

Publisher: publish.yml on Mirkiux/featkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page