Skip to main content

Lightweight feature store for small and medium teams

Project description

feature-forge

Lightweight feature store for small and medium ML teams. Define features in YAML, get point-in-time correct data with one method call. No infrastructure required.

Install

pip install feature-forge

With optional backends:

pip install feature-forge[databricks]   # Databricks Unity Catalog
pip install feature-forge[spark]        # PySpark engine
pip install feature-forge[all]          # Everything

What it does

  • Point-in-time correct joins: for each row in your data, features are computed using only past information. No data leakage.
  • YAML-based feature definitions: entities, sources, and features are declared in version-controlled YAML files.
  • Multiple data sources: Parquet (local), S3/GCS/Azure, SQLite/PostgreSQL, Databricks Unity Catalog.
  • Two query engines: DuckDB (default, zero-config) or PySpark (for Spark clusters).
  • One method for everything: get_features_table() handles training, inference, and historical backfill.
  • CLI included: init, validate, list, describe, materialize.

Quickstart

1. Initialize

feature-forge init my_features/

2. Define (edit the generated YAML files)

# entities.yml
entities:
  - name: customer
    join_keys: [customer_id]
# sources.yml
sources:
  - name: transactions
    backend: parquet
    path: data/transactions.parquet
    entity: customer
    timestamp_column: event_timestamp
    columns:
      - { name: customer_id, dtype: int64 }
      - { name: amount, dtype: float64 }
      - { name: event_timestamp, dtype: timestamp }
# features.yml
feature_views:
  - name: customer_features
    entity: customer
    source: transactions
    features:
      - name: txn_count_7d
        dtype: int64
        aggregation: { function: count, column: amount, window: 7d }
      - name: avg_amount_30d
        dtype: float64
        aggregation: { function: avg, column: amount, window: 30d }

3. Validate

feature-forge validate --repo my_features/

4. Use

from feature_forge import FeatureStore
import pandas as pd

store = FeatureStore("my_features/")

labels = pd.DataFrame({
    "customer_id": [101, 102],
    "event_timestamp": pd.to_datetime(["2025-03-15", "2025-03-20"]),
    "is_fraud": [1, 0],
})

df = store.get_features_table(
    entity_df=labels,
    feature_views=["customer_features"],
)
# customer_id | event_timestamp | is_fraud | txn_count_7d | avg_amount_30d
# Each row uses ONLY data before its timestamp

Example: training, inference, and backfill

from feature_forge import FeatureStore

store = FeatureStore("my_features/")

# TRAINING: pass labeled data with historical timestamps
training_df = store.get_features_table(
    entity_df=labels_df,
    feature_views=["customer_features"],
)

# INFERENCE: pass entity IDs, features computed as of now
inference_df = store.get_features_table(
    entity_ids={"customer_id": [101, 102, 103]},
    feature_views=["customer_features"],
)

# BACKFILL: entity IDs + date range for historical scoring
backfill_df = store.get_features_table(
    entity_ids={"customer_id": [101, 102]},
    feature_views=["customer_features"],
    start_date="2025-01-01",
    end_date="2025-06-01",
    interval="1d",
)

store.close()

Same method, same PIT guarantees. The behavior changes based on which parameters you pass.

Documentation

Guide Description
Getting Started 5-minute setup
Defining Features Entities, sources, aggregation vs passthrough
Retrieving Features Training, inference, backfill modes
Backends Parquet, S3, SQL, Databricks configuration
Engines DuckDB vs PySpark
Materialization Pre-compute and save to Parquet
CLI Reference All commands and flags

Development

git clone https://github.com/pydoni/feature-forge.git
cd feature-forge
uv sync --extra dev
uv run pytest

License

Apache 2.0

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

fforge-1.0.0.tar.gz (47.6 kB view details)

Uploaded Source

Built Distribution

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

fforge-1.0.0-py3-none-any.whl (36.5 kB view details)

Uploaded Python 3

File details

Details for the file fforge-1.0.0.tar.gz.

File metadata

  • Download URL: fforge-1.0.0.tar.gz
  • Upload date:
  • Size: 47.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fforge-1.0.0.tar.gz
Algorithm Hash digest
SHA256 ea9718a9102d5f5980fff3375419d76fc8d9f38149be312f9548724860a4af6b
MD5 e0ac2f738a8c3c936c17c7269611a3e4
BLAKE2b-256 4c2c45c22a8150e134a35577534d542256960ad09700f4e81b327c0b1528bef6

See more details on using hashes here.

File details

Details for the file fforge-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: fforge-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 36.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fforge-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0ffc97b2aca0609d89bbff3a427f4db848cc7f1a1211d9c49a7037b3124fc278
MD5 b677aebe57ae3ecf28fc1a2771588506
BLAKE2b-256 490430dd55c6d12af70fd3e67a3a936832f540a0f925270380fdb247aaf6a429

See more details on using hashes here.

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