关于 xolars
xolars 是将 xarray.Dataset 与按维度组织的 Polars 数据框对齐的不可变容器,适用于需要同时管理多维数据集和维度元数据的 Python 用户。它支持惰性或即时数据框,并提供选择、赋值、合并及磁盘往返操作。
主要功能
- 01按维度关联 Polars 数据框
- 02校验坐标集合并自动对齐行序
- 03支持 DataFrame 与 LazyFrame
- 04按位置或标签同步选择
- 05赋值与合并时保持固定坐标
- 06通过 Zarr 和 Parquet 读写
适用场景
- 01为 xarray 维度附加 Polars 元数据
- 02按索引或坐标筛选数据及关联框架
- 03在数据集中分配或合并变量
- 04将数据写入磁盘后重新打开
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ d-laub/ xolars --skill xolars安装后在 Claude Code 中输入/xolars确认已加载。
详细介绍
xolars 公共 API
xolars 是一个小型的冻结容器:由一个 xarray.Dataset 和每个维度对应的一个 Polars
框架组成,并在选择和磁盘往返过程中保持与 Dataset 坐标顺序对齐。
公共接口
import xolars 仅公开一个名称:
xolars.Xolars— 容器
使用 from xolars import Xolars。xolars._core 下的内容都是内部实现。
Xolars 使用 attrs 冻结,并且是 Generic[F],其中 F = pl.DataFrame | pl.LazyFrame
——每个维度对应的框架类型都相同。它是冻结的,因此 isel/sel/collect
会返回新实例;它们不会修改原实例。
构造
from xolars import Xolars
xo = Xolars(ds=dataset, df={"sample": sample_frame, "gene": gene_frame})
# positional also works: Xolars(dataset, {"sample": sample_frame})
ds: xr.Datasetdf: Mapping[Hashable, pl.DataFrame | pl.LazyFrame]— 一个以维度名称为键的字典,每个需要元数据的维度对应一个框架(不必包含每个维度)。它不是单个框架,也不是列表。
构造时强制执行的不变量(每项不满足时都会引发 ValueError)
对于每个 dim -> frame:
dim必须是ds的实际维度。frame必须包含一个名称恰好为dim的列(连接键)。- 该列的值必须与
ds[dim]坐标构成的集合相同——长度相同,成员相同。缺少或多出的 ID 都会引发错误;部分重叠也会引发错误。
行顺序无关紧要:框架会自动重排(通过对 dim 列执行右连接),使其与 Dataset 的坐标顺序一致。不要预先排序。
选择 — isel / sel
签名与 xarray.Dataset.isel / .sel 相同(索引器字典或关键字参数、
drop、missing_dims;sel 还接受 method/tolerance):
xo.isel(sample=slice(0, 10)) # positional
xo.sel(sample=["S1", "S2"]) # label-based
两者都会对子集化 Dataset,并且筛选每个索引维度对应的框架,以保持框架对齐。会丢弃维度的标量索引器也会丢弃该维度对应的框架。未被索引的维度对应的框架保持不变。
若要按元数据筛选(例如 age > 50),请自行筛选框架以获取 ID,然后将这些 ID 传给 sel——Xolars 没有元数据谓词方法:
frame = xo.df["sample"] # may be DataFrame or LazyFrame
ids = frame.filter(pl.col("age") > 50).select("sample")
ids = ids.collect() if isinstance(ids, pl.LazyFrame) else ids
xo2 = xo.sel(sample=ids["sample"].to_list())
添加数据 — assign / merge
两者都是函数式操作(返回一个新的 Xolars);一维数据存放在对应维度的 Polars 框架中,多维数据存放在 Dataset 中。
# assign one or more named variables (xarray.Dataset.assign idiom)
xo2 = xo.assign(gc=("gene_id", gc_array)) # 1-D -> gene_id frame
xo2 = xo.assign(counts=(("gene_id", "sample_id"), mat)) # 2-D -> Dataset
xo2 = xo.assign(score=some_named_dataarray) # aligned by label
# passing an xarray.Dataset value is an error -> use merge()
# merge whole objects; 1-D vars in xarray objects move to Polars
xo2 = xo.merge(other_dataset, gene_frame) # bare frame: dim inferred
xo2 = xo.merge(frames={"gene_id": [f1, f2]}) # explicit, aligned to gene_id
名称冲突时会静默覆盖。Polars/xarray 输入会与固定坐标左对齐(子集 → null,多出的部分 → 丢弃);现有 ds 坐标集合不会扩展(但 ≥2 维变量可以引入新维度)。
磁盘往返 — write / open
xo.write(path, mode="w") # mode is REQUIRED (a zarr write mode)
reopened = Xolars.open(path) # classmethod -> Xolars[pl.LazyFrame]
write(path, mode)会创建path/,其中包含dataset.zarr/,以及每个框架对应的一个{dim}.parquet。mode没有默认值——请传入"w"(覆盖)、"w-"、"a"等(xarrayZarrWriteModes)。open(path)会读取dataset.zarr,并对其旁边的每个*.parquet执行scan_parquet——返回的框架为惰性(pl.LazyFrame)。- 两个
path参数的类型都是pathlib.Path;请传入Path,而不是普通字符串。
惰性与急切 — collect
open 会返回 LazyFrame。若要将每个框架都物化为 pl.DataFrame:
eager = reopened.collect() # -> Xolars[pl.DataFrame]
构造、验证和选择都支持这两种类型,因此只有在确实需要急切框架时才调用
collect()。
常见错误
| 错误 | 修正 |
|---|---|
Xolars(dataset, metadata=df) | Xolars(ds=dataset, df={dim: frame}) |
将单个普通框架或列表作为 df 传入 | df 是一个以维度名称为键的字典 |
| 框架缺少以维度命名的列 | 添加名称恰好为 dim 的列;它是连接键 |
框架 ID 与 ds[dim] 只有部分重叠 | 必须构成相同的集合(长度和成员都相同),否则会引发错误 |
| 预先排序框架行以匹配坐标 | 无需这样做——构造时会自动重排 |
xo.to_zarr(...) / Xolars.open_zarr(...) | xo.write(path, mode) / Xolars.open(path) |
xo.write(path) | 必须提供 mode,例如 mode="w" |
期待 open 返回急切框架 | 返回的是 LazyFrame;请调用 .collect() |