Skip to main content

Writing

SigilYX provides multiple ways to write YXDB files from Python. All write paths use the same Rust writer with pipelined LZF compression.

Write a Polars DataFrame

import sigilyx as yx
import polars as pl

df = pl.DataFrame({"id": [1, 2, 3], "name": ["Alice", "Bob", "Charlie"]})

# Direct API
yx.write_yxdb("output.yxdb", df)

# Or via the Polars namespace plugin
import sigilyx
df.yxdb.write("output.yxdb")

With Spatial Columns

If your DataFrame contains Binary columns with WKB geometry data, mark them as spatial so they are written as SpatialObj fields:

yx.write_yxdb("output.yxdb", df, spatial_columns=["geometry"])

Write a LazyFrame

Collect and write a LazyFrame in one step:

import sigilyx as yx
import polars as pl

lf = pl.scan_csv("large_file.csv").filter(pl.col("status") == "active")

# Direct API
yx.sink_yxdb("output.yxdb", lf)

# Or via the Polars namespace plugin
lf.yxdb.sink("output.yxdb")
note

Unlike Polars' native sink_parquet which streams directly to disk, sink_yxdb collects the LazyFrame first because the YXDB header requires the record count upfront. For datasets larger than available RAM, use write_yxdb_batches with a batched data source instead.

Write a PyArrow Table

import sigilyx as yx
import pyarrow as pa

table = pa.table({"id": [1, 2, 3], "name": ["a", "b", "c"]})
yx.write_yxdb_arrow("output.yxdb", table)

Write a Pandas DataFrame

import sigilyx as yx
import pandas as pd

df = pd.DataFrame({"id": [1, 2, 3], "name": ["Alice", "Bob", "Charlie"]})
yx.write_yxdb_pandas("output.yxdb", df)

Streaming Batch Writes

For datasets too large to hold in memory, write_yxdb_batches writes from an iterator of DataFrames:

import sigilyx as yx
import polars as pl

def read_chunks():
reader = pl.read_csv_batched("huge_file.csv", batch_size=100_000)
while True:
batch = reader.next_batches(1)
if batch is None:
break
yield batch[0]

n_written = yx.write_yxdb_batches("output.yxdb", read_chunks())
print(f"Wrote {n_written:,} records")

Only one batch is in memory at a time. The Rust streaming writer writes LZF-compressed record blocks incrementally and updates the header record count upon finalization.

ETL Pipeline Example

import sigilyx as yx
import polars as pl

def transform(batch: pl.DataFrame) -> pl.DataFrame:
return (
batch
.filter(pl.col("amount") > 0)
.with_columns(pl.col("amount").cast(pl.Float64).alias("amount_f"))
)

def pipeline():
for batch in yx.read_yxdb_batches("input.yxdb", batch_size=100_000):
yield transform(batch)

n = yx.write_yxdb_batches("output.yxdb", pipeline())
print(f"Transformed {n:,} records")

Write API Summary

FunctionInputUse Case
yx.write_yxdb(path, df)polars.DataFrameDefault write path
df.yxdb.write(path)polars.DataFramePolars namespace plugin
yx.sink_yxdb(path, lf)polars.LazyFrameCollect + write
lf.yxdb.sink(path)polars.LazyFrameNamespace plugin sink
yx.write_yxdb_arrow(path, table)pyarrow.TableArrow ecosystem
yx.write_yxdb_pandas(path, df)pandas.DataFramePandas ecosystem
yx.write_yxdb_batches(path, iter)Iterator[polars.DataFrame]Streaming / large files
yx.write_yxdb_geo(path, gdf)geopandas.GeoDataFrameGeospatial (see Spatial)