scduck
Store time series of snapshots in a SCD Type 2 table.
13 days of data: 65 MB CSV -> 6.3 MB DuckDB (~10x compression)
How it works
Records are stored with valid_from / valid_to date ranges. When data doesn't change, no new rows are written. Only changes generate new records.
id | name | price | valid_from | valid_to
P001 | Widget| 9.99 | 2025-01-01 | 2025-03-15 # original price
P001 | Widget| 12.99 | 2025-03-15 | NULL # price changed
P002 | Gadget| 4.99 | 2025-01-01 | NULL # unchanged
valid_from: inclusive (>=)valid_to: exclusive (<), NULL = current
Usage
from scduck import SCDTable
# Define your schema
with SCDTable(
"products.duckdb",
table="products",
keys=["product_id"],
values=["name", "price", "category"]
) as db:
# Sync daily snapshots (pandas, polars, or pyarrow)
result = db.sync("2025-01-01", df_jan1) # returns SyncResult
db.sync("2025-01-02", df_jan2)
# Reconstruct any historical snapshot
snapshot = db.get_data("2025-01-01") # returns pyarrow Table
# Check synced dates
db.get_synced_dates() # ['2025-01-01', '2025-01-02']
Out-of-order sync
Dates can be synced in any order:
db.sync("2025-01-15", df) # sync Jan 15 first
db.sync("2025-01-01", df) # backfill Jan 1
db.get_data("2025-01-01") # returns correct snapshot
Example: SecurityMaster
import pandas as pd
from scduck import SCDTable
with SCDTable(
"security_master.duckdb",
table="securities",
keys=["security_id"],
values=["ticker", "mic", "isin", "description",
"sub_industry", "country", "currency", "country_risk"]
) as db:
df = pd.read_csv("SecurityMaster_20251201.csv")
db.sync("2025-12-01", df)
Installation
pip install scduck
# With pandas/polars support
pip install scduck[all]
Sync Logic
See SYNC_LOGIC.md for detailed operation cases.
Metadata
Release files for scduck 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| scduck-0.1.1.tar.gz | 48.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scduck-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.8 kB
Release files / scduck-0.1.1.tar.gz
| Download URL | scduck-0.1.1.tar.gz |
|---|---|
| Size | 48.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
025b5b2c291cf92bd29ac7984c03198070e9f673a910253d782e5cc8c1666030
|
|
BLAKE2b-256 checksum How to use checksums |
370d28a011cd3da89372c5b76656b68d31671cf52a2b7a995786b5e6b538dc5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 19, 2026.
Transparency logRelease files / scduck-0.1.1-py3-none-any.whl
| Download URL | scduck-0.1.1-py3-none-any.whl |
|---|---|
| Size | 6.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
55e35682a6c07a5201b3429758969e03c3d9f0099ae918e525f47d95976ea592
|
|
BLAKE2b-256 checksum How to use checksums |
53011ed4b5962a5153fdf1572d77578982bd044f468891c996cc7f01370c817b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 19, 2026.
Transparency log