Repository navigation
Python Versions
Each commit makes a new version of the table. The old versions stay on disk, so you can read them, undo a change and free the disk space when you no longer need them.
Example files and table for this page
The examples use small NetCDF files of CTD profiles, see
Getting started. Run this
block first. It writes the files and makes the table profiles.lance
from the six files in profiles/.
from pathlib import Path
import numpy as np
import pandas as pd
import xarray as xr
def write_profile(path, station, cruise, lat, lon, levels, time, temp_type="float64", extra=None):
"""Write one CTD profile as a NetCDF file."""
depth = np.arange(levels, dtype="float64") * 10.0
ds = xr.Dataset(
{
"TEMP": ("depth", (15.0 - depth / 100.0).astype(temp_type), {"units": "degree_Celsius"}),
"PSAL": ("depth", 35.0 + depth / 1000.0, {"units": "psu"}),
"LATITUDE": ((), lat),
"LONGITUDE": ((), lon),
"TIME": ((), pd.Timestamp(time)),
},
coords={"depth": ("depth", depth, {"units": "m"})},
attrs={"station": station, "cruise": cruise},
)
if extra:
ds["DOXY"] = ("depth", np.full(levels, 250.0), {"units": "umol/kg"})
Path(path).parent.mkdir(exist_ok=True)
ds.to_netcdf(path)
# Six profiles of two cruises.
write_profile("profiles/A1.nc", "A1", "cruise-1", 52.5, 4.0, 50, "2026-06-01")
write_profile("profiles/A2.nc", "A2", "cruise-1", 53.0, 4.5, 80, "2026-06-02")
write_profile("profiles/A3.nc", "A3", "cruise-1", 60.0, 2.0, 120, "2026-06-03")
write_profile("profiles/B1.nc", "B1", "cruise-2", 54.0, 3.0, 60, "2026-07-01")
write_profile("profiles/B2.nc", "B2", "cruise-2", 55.5, 3.5, 70, "2026-07-02")
write_profile("profiles/B3.nc", "B3", "cruise-2", 57.0, 5.0, 90, "2026-07-03")
# Three new profiles of a third cruise.
write_profile("new/C1.nc", "C1", "cruise-3", 58.0, 6.0, 40, "2026-08-01")
write_profile("new/C2.nc", "C2", "cruise-3", 58.5, 6.5, 45, "2026-08-02")
write_profile("new/C3.nc", "C3", "cruise-3", 59.0, 7.0, 50, "2026-08-03")
# Corrected profiles, with other depth levels.
write_profile("corrected/A1.nc", "A1", "cruise-1", 52.5, 4.0, 55, "2026-06-01")
write_profile("corrected/A2.nc", "A2", "cruise-1", 53.0, 4.5, 90, "2026-06-02")
write_profile("corrected/B1.nc", "B1", "cruise-2", 54.0, 3.0, 65, "2026-07-01")
write_profile("corrected/B2.nc", "B2", "cruise-2", 55.5, 3.5, 75, "2026-07-02")
# Files that differ from the others.
write_profile("other/int_temp.nc", "D1", "cruise-4", 50.0, 1.0, 10, "2026-09-01", temp_type="int32")
write_profile("other/with_doxy.nc", "E1", "cruise-5", 51.0, 2.0, 20, "2026-10-01", extra=True)
write_profile("other/float32_temp.nc", "F1", "cruise-6", 52.0, 2.0, 10, "2026-11-01", temp_type="float32")
import lance_nd
def stations(table):
"""The station of each dataset in the table, in table order."""
return [batch.to_xarray().attrs["station"] for batch in table.scan(project=[])]
with lance_nd.create("profiles.lance", mode="overwrite", stats=True) as writer:
with xr.open_dataset("profiles/A1.nc") as ds:
writer.write(ds)
with xr.open_dataset("profiles/A2.nc") as ds:
writer.write(ds)
with xr.open_dataset("profiles/A3.nc") as ds:
writer.write(ds)
with xr.open_dataset("profiles/B1.nc") as ds:
writer.write(ds)
with xr.open_dataset("profiles/B2.nc") as ds:
writer.write(ds)
with xr.open_dataset("profiles/B3.nc") as ds:
writer.write(ds)
table = lance_nd.open("profiles.lance")versions() gives a (version, timestamp) pair for each version that
still has its files, oldest first.
table.delete("__global_attrs__.station = 'A1'")
table.delete("__global_attrs__.cruise = 'cruise-2'")
print([version for version, _ in table.versions()])
print(table.version, stations(table))[1, 2, 3]
3 ['A2', 'A3']
The timestamps are datetime objects in UTC.
checkout() gives the table at an earlier version.
old = table.checkout(1)
print(old.version, stations(old))1 ['A1', 'A2', 'A3', 'B1', 'B2', 'B3']
- Scan the old table as any other table.
- Use a table from
checkout()to read. Adelete()on it changes the latest version, not the old version.
restore() makes the content of an earlier version the latest version, in
a new commit.
table.restore(1)
print(table.version, stations(table))4 ['A1', 'A2', 'A3', 'B1', 'B2', 'B3']
-
restore()removes no version. Versions 2 and 3 stay, so you can also restore them. - Readers of the table see the restored content after the commit.
Deletes, overwrites and compactions free no disk space, because the old
versions still use the old files. cleanup_old_versions() removes the
files of old versions.
from datetime import timedelta
removed = table.cleanup_old_versions(older_than=timedelta(0))
print(removed["old_versions"], removed["bytes_removed"] > 0)
print([version for version, _ in table.versions()])3 True
[4]
-
older_thanis the minimum age of a version to remove. The default is 14 days. The latest version always stays. -
timedelta(0)keeps only the latest version. Use it only when no other process reads the table: a reader of a removed version fails. - A removed version cannot be read or restored.
- When the commits only added data, the old versions use no files of their own, so a cleanup frees little space.
| Operation | New versions | Files |
|---|---|---|
create() or append(), then finish()
|
1 | Adds fragments. |
finish() without datasets on an append |
0 | None. |
delete() |
1, also when nothing matches | Adds deletion files. |
compact() |
2, or 0 without work | Adds fragments. The old files stay. |
restore() |
1 | None. |
widen() |
1 | Adds files for the variable. |
cleanup_old_versions() |
0 | Removes the files of old versions. |
Concepts
Rust
Python