Skip to main content

polars-etl-kit

High-performance ETL toolkit for Python — Polars + DuckDB powered.

License: MIT Python 3.10+

Overview

polars-etl-kit is a collection of battle-tested ETL building blocks extracted from real-world financial data pipelines processing 1500+ Excel files per run. Each module is framework-agnostic and composable — use one or use them all.

Modules

Module What it does Core deps
excel Parallel Excel parser with MD5 caching polars, fastexcel
schema YAML-driven schema registry → DDL generation pyyaml
warehouse DuckDB star-schema builder (dim/fact) duckdb, polars
matching Multi-tier name matching engine pyyaml
tree Hierarchical tree from flat data polars
geo Multi-provider geocoder (AMap/Baidu/Tencent) aiohttp, polars
db DuckDB connection with retry duckdb
kedro Custom Kedro datasets & dynamic hooks kedro

Install

# Core (polars + duckdb)
pip install polars-etl-kit

# With Excel support
pip install polars-etl-kit[excel]

# With Kedro integration
pip install polars-etl-kit[kedro]

# With geocoding
pip install polars-etl-kit[geo]

# Everything
pip install polars-etl-kit[all]

Quick start

Parallel Excel processing

from polars_etl_kit.excel import ExcelProcessor, FileCache, scan_files

# Scan for files
files = scan_files("data/01_raw", patterns=[r"(?P<code>\w+)_(?P<month>\d{6})\.xlsx"])

# Set up cache
cache = FileCache("cache/excel")

# Define your sheet handler
def handle_sheet(sheet_name, df, file_meta):
    # Your parsing logic here
    return df.with_columns(pl.lit(file_meta["code"]).alias("source_code"))

# Process in parallel
processor = ExcelProcessor(max_workers=8, file_cache=cache)
results = processor.process_files(
    files,
    process_sheet=handle_sheet,
    cache_suffixes=["parsed"],
)

Schema registry + DuckDB warehouse

from polars_etl_kit import SchemaRegistry, WarehouseBuilder

# Load schema from YAML
registry = SchemaRegistry("schemas/dimensions.yaml", "schemas/facts.yaml")

# Generate DDL
print(registry.generate_all_ddl())

# Build warehouse
wh = WarehouseBuilder("analytics.duckdb", schema="analytics")
wh.create_schema()
wh.load_dimension("dim_customer", "data/dim_customer.parquet", pk="customer_id")
wh.load_fact("fact_sales", "data/fact_sales.parquet", indexes=["customer_id", "date"])
df = wh.query("SELECT * FROM analytics.v_summary")

Name matching (with fuzzy fallback)

from polars_etl_kit.matching import NameMatcher

# enable_fuzzy=True: Levenshtein 4th-tier fallback for typos
matcher = NameMatcher("mappings/standard_names.yaml", enable_fuzzy=True)
result = matcher.match({
    "raw_text": "营业总收入",
    "clean_name": "营业总收人",       # 错字: "入"→"人"
    "category": "利润表",
})
# → {"code": "B001", "name": "营业收入", "fuzzy_score": 0.89, ...}

Schema inference

from polars_etl_kit.schema import infer_schema_from_polars
import polars as pl

df = pl.DataFrame({"id": [1, 2], "name": ["Alice", "Bob"], "score": [95.0, 82.5]})
schema = infer_schema_from_polars(df, "dim_students", "学生维度")
print(schema.generate_duckdb_ddl())
# → CREATE TABLE IF NOT EXISTS dim_students (
#     id BIGINT NOT NULL, name VARCHAR(255), score DOUBLE);

Tree building

from polars_etl_kit.tree import build_tree, find_parent_by_prefix
import polars as pl

nodes = pl.DataFrame([
    {"node_id": "A_9", "parent_id": "#", "node_name": "Group"},
    {"node_id": "A_1", "parent_id": "A_9", "node_name": "Sub A"},
    {"node_id": "B_9", "parent_id": "A_9", "node_name": "Sub B"},
])

tree = build_tree(nodes, node_id_col="node_id", parent_id_col="parent_id",
                  node_name_col="node_name", find_parent=find_parent_by_prefix)

Multi-provider geocoding

from polars_etl_kit.geo import BatchGeocoder, get_geocoder
import polars as pl

entities = pl.DataFrame({
    "code": ["E001"], "suffix": ["9"], "address": ["北京市朝阳区"],
})

# Switch providers: "amap" / "baidu" / "tencent"
geocoder = BatchGeocoder(
    api_key="your_key",
    cache_path="geo_cache.parquet",
    provider="baidu",     # 百度地图
    key_cols=["code", "suffix"],
)
results = geocoder.run(entities, address_col="address")

# Or use factory for single queries
client = get_geocoder("tencent", api_key="your_key")  # 腾讯地图
# async with client as c:
#     result = await c.geocode("北京市朝阳区")

Philosophy

  • Polars-first: all DataFrame operations use Polars for speed
  • Framework-agnostic: modules work standalone; Kedro is optional
  • Composable: each module has minimal internal dependencies
  • Battle-tested: extracted from production pipelines processing 1500+ files
  • Zero hardcoded paths: everything is parameterized

License

MIT — see LICENSE.

Metadata

Release files for polars-etl-kit 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for polars-etl-kit 0.2.0
File Size Uploaded Provenance Actions
polars_etl_kit-0.2.0.tar.gz 32.6 kB Without provenance Details for polars_etl_kit-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for polars-etl-kit 0.2.0
File Interpreter ABI Platform Actions
polars_etl_kit-0.2.0-py3-none-any.whl Python 3 none any Details for polars_etl_kit-0.2.0-py3-none-any.whl

Total release size: 68.1 kB

Release files / polars_etl_kit-0.2.0.tar.gz

Download URL polars_etl_kit-0.2.0.tar.gz
Size 32.6 kB
Tags Source
SHA-256 checksum
How to use checksums
09e0032eee80fc778a85c3fab73f5a680e6d9e22bdff8708ae7649111ce0133f
BLAKE2b-256 checksum
How to use checksums
9df99e86b11a9a3d04a00bc72068b36a79cc8ce65cb076187ab30228c28e778b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / polars_etl_kit-0.2.0-py3-none-any.whl

Download URL polars_etl_kit-0.2.0-py3-none-any.whl
Size 35.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
418f1ad3694c5decb414f3e3281e48f5b926bc27949bd01761d9cb759efb1207
BLAKE2b-256 checksum
How to use checksums
32d1bb3bd23f14796c2bddb4a8e722ebf814b4dbd18724ac29da7cda90ebae18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Security for polars-etl-kit 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page