之前我们尝试把研究中的数据处理尽可能集成到 Python 中让 Python 成为整个科研工作流的载体。从原始数据整理、统计分析和可视化到表格、图形与报告生成尽量在同一个环境中完成减少在不同软件之间反复切换。关于这套工作流可以参考此前的文章Python一站式科研工作流从数据分析到报告生成。随着数据量增加数据的结构也开始变得复杂。研究对象可能同时具有时间、地区、行业、指标、情景和重复实验等维度。如果继续使用 CSV 或 Excel往往需要把不同变量拆分到多个文件或工作表中使用 Python 字典虽然灵活但缺少统一的结构约束SQLite 适合关系型数据的存储和查询不过在分析过程中需要频繁地编写 SQL并在数据库表和 Python 对象之间转换。这些工具并没有失去价值。SQLite 仍然适合关系型查询CSV 和 Excel 仍然适合交换二维表格pandas 也依然是处理表格数据的首选。问题在于当数据天然具有三个或更多维度时再把它强行压缩成二维表格很多原本清晰的结构就需要通过列名、索引或者额外的说明来维持。xarray 处理的正是这类问题。它为多维数组增加了维度名称、坐标标签、变量名称和元数据并提供 NetCDF、Zarr 等多维数据格式的读写接口。它不是简单的文件读取工具也不是一种新的数据库而是一套面向多维数据的数据模型。从“数组的位置”转向“数据的含义”NumPy 是 xarray 的重要基础。它提供高效的多维数组和数值计算能力但 NumPy 数组本身只知道形状和数据类型并不知道每个轴代表什么。例如valuedata[2,10,30]仅看这行代码很难判断三个数字分别代表时间、地区、经纬度还是其他分类。我们必须在别处记住数组的维度顺序例如(time, latitude, longitude)一旦数组被转置或者某个处理步骤改变了维度顺序原来依赖axis0、axis1的代码就可能失去原本的含义。xarray 的基本想法是让这些信息直接成为数据对象的一部分。相同的操作可以写成temperature.sel(time2026-07-11,latitude39.9,longitude116.4,methodnearest,)计算时间平均值时不再写data.mean(axis0)而是写temperature.mean(dimtime)这里的区别不只是代码更易读。使用维度名称以后计算表达的是“沿时间维度求平均”而不是“沿第0个轴求平均”。即使维度顺序发生变化这段代码的含义仍然保持不变。因此xarray 的核心并不是重新实现一套 NumPy而是在 NumPy 的多维数组之上建立一个带有语义的数据层多维数组中的轴、位置和变量都应当具有明确含义计算应当按照这些含义进行而不是完全依赖数组位置。维度、坐标和索引理解 xarray需要区分三个容易混淆的概念维度、坐标和索引。维度dimension是数组轴的名称。例如一个城市气温数组可以具有两个维度(city, date)坐标coordinate是这些维度上的标签。例如city: 北京、上海、广州 date: 2026-07-01、2026-07-02索引index则是 xarray 用来高效查找和对齐坐标的数据结构。多数情况下xarray 会自动为一维维度坐标创建索引因此我们可以使用.sel()按标签选择数据。维度和坐标并不总是一一对应。一个维度可以具有多个辅助坐标。例如观测站维度station除了站点编号还可以具有站点名称、经度、纬度和海拔等坐标station ├── station_id ├── station_name ├── longitude ├── latitude └── elevation数据的逻辑维度仍然是station但每个站点可以同时拥有多种描述信息。这种结构对于地理、气象、实验和面板数据都很实用。按名称广播和按坐标对齐xarray 的另一个重要设计是按照维度名称进行广播并按照坐标标签进行对齐。假设有两个数组importxarrayasxr axr.DataArray([10,20],dimscity,coords{city:[北京,上海]},)bxr.DataArray([1,2],dimscity,coords{city:[上海,北京]},)两个数组的城市顺序不同。如果直接使用普通数组按位置相加得到的是10 1 20 2但 xarray 会先根据城市名称对齐再进行计算北京10 2 上海20 1因此ab得到的是北京12、上海21。这种自动对齐在处理时间序列、地区数据和不同来源的数据集时尤其重要。很多数据错误并不会导致程序报错例如两个表中的地区顺序不同代码依然能够运行但结果已经错位。xarray 把坐标纳入计算规则可以在数据模型层面减少这类错误。需要注意的是自动对齐也意味着坐标本身参与运算。两个对象坐标不完全一致时xarray 会根据具体操作使用交集、并集或产生缺失值。因此在组合数据之前应当检查坐标并在需要时明确使用xr.align()、reindex()或join参数控制对齐方式。数据模型与底层计算相分离xarray 主要管理数据的逻辑结构包括变量、维度、坐标、索引、属性以及它们之间的对齐关系。真正保存数值的底层数组不一定是 NumPy。一个 xarray 对象的底层数据可以来自NumPy 数组Dask 分块数组CuPy GPU 数组稀疏数组其他兼容 NumPy 接口的数组对象。这是一种有意的分层设计。xarray 负责描述“数据是什么”和“沿哪个维度计算”底层数组负责决定“数值如何存储和执行”。小型数据通常由 NumPy 直接计算。大型数据可以交给 Dask 分块处理dsxr.open_dataset(large_data.nc,chunks{time:100},)此时 xarray 不必立即把整个文件载入内存。很多操作只会生成计算任务resultds[temperature].mean(dimtime)真正执行计算时再调用result.compute()这让同一套 xarray API 可以同时用于内存中的小数据和磁盘上的大型数据。使用者主要关注变量、维度和坐标而不必因为数据规模变化就完全重写分析逻辑。xarray 可以完成哪些工作xarray 的功能围绕多维数据的生命周期展开。首先是数据读取和保存。它可以读取单个或多个 NetCDF 文件也可以处理 Zarr、OPeNDAP 等数据源xr.open_dataset(data.nc)xr.open_mfdataset(data_*.nc)xr.open_zarr(data.zarr)处理完成后可以重新写入文件ds.to_netcdf(result.nc)ds.to_zarr(result.zarr)其次是数据选择。xarray 同时支持按位置和按标签选择ds.isel(time0)ds.sel(time2026-07-11)ds.sel(latitude39.9,methodnearest)还可以根据条件过滤ds.where(ds[temperature]30)多维统计计算可以直接指定维度ds.mean(dimtime)ds.max(dim(latitude,longitude))ds.quantile(0.95,dimtime)对于时间和分类数据xarray 提供了类似 pandas 的分组、重采样和滚动窗口ds.groupby(time.month).mean()ds.resample(time1ME).mean()ds.rolling(time7,centerTrue).mean()此外它还支持插值、加权计算和微积分等操作ds.interp(latitudenew_latitude)ds.weighted(weights).mean(dimlatitude)ds.differentiate(time)ds.integrate(latitude)在数据结构调整方面可以进行转置、堆叠、拆分和重命名ds.transpose(time,latitude,longitude)ds.stack(location(latitude,longitude))ds.unstack(location)ds.rename({latitude:lat})多个数据对象之间可以拼接或合并xr.concat(datasets,dimtime)xr.merge([temperature,humidity])xr.combine_by_coords(datasets)xarray 还提供基于 Matplotlib 的快速绘图接口。对于具有坐标信息的二维数据通常只需要ds[temperature].plot()xarray 会根据维度和坐标自动选择横轴、纵轴和颜色映射。它的绘图功能主要适合数据检查和科研分析复杂地图可以进一步结合 Cartopy地理栅格数据则可以结合 rioxarray。三种主要数据类型当前 xarray 的高层数据模型可以分为三个层次DataArray、Dataset和DataTree。它们不是彼此独立的三个容器而是从单个变量到完整数据集再到分层数据集合的递进关系。DataTree └── 一个或多个树节点 └── 每个节点保存 Dataset 形式的数据 └── Dataset 包含多个 DataArray └── DataArray 包装底层数组DataArray一个带标签的多维变量DataArray是最基本的高层数据结构表示一个带有名称、维度、坐标和属性的多维变量。可以把它概括为DataArray data dims coords name attrs例如importpandasaspdimportxarrayasxr temperaturexr.DataArray(data[[30.0,31.0],[28.0,29.0],],dims(city,date),coords{city:[北京,上海],date:pd.date_range(2026-07-01,periods2),},nametemperature,attrs{units:°C,description:每日最高气温,},)temperature这里的data是实际数值dims描述数组的轴coords为轴上的位置提供标签name表示变量名称attrs保存单位和说明等元数据。attrs主要用于描述数据不应默认认为它会参与数值计算。例如xarray 不会因为两个变量的units分别是摄氏度和华氏度就自动完成单位换算。需要严格单位计算时可以结合pint-xarray。此外xarray 还有一个encoding属性用来保存文件序列化相关的信息例如压缩、数据类型、缺失值编码和分块方式。可以简单地区分为attrs 描述数据的科学或业务含义 encoding 描述数据如何写入文件Dataset共享坐标的多变量数据集Dataset是多个DataArray的字典式集合。它类似于{temperature:temperature,humidity:humidity,precipitation:precipitation,}但它不是普通字典。Dataset 内部的变量共享维度和坐标并受到一致性约束同名维度在同一个 Dataset 中必须具有相同长度相关坐标也会参与变量之间的对齐。例如humidityxr.DataArray(data[[60.0,58.0],[75.0,72.0],],dims(city,date),coords{city:[北京,上海],date:pd.date_range(2026-07-01,periods2),},namehumidity,attrs{units:%},)weatherxr.Dataset(data_vars{temperature:temperature,humidity:humidity,},attrs{title:城市天气数据,source:示例数据,},)weather这个 Dataset 包含两个数据变量但它们共享city和date坐标Dataset: weather ├── Coordinates │ ├── city │ └── date ├── Data variables │ ├── temperature(city, date) │ └── humidity(city, date) └── Attributes ├── title └── source选择北京时选择操作会同时应用于所有相关变量weather.sel(city北京)Dataset 很接近 NetCDF 的数据模型也是实际使用 xarray 时最常见的对象。一个 NetCDF 文件通常会被读取为一个 Dataset而文件中的各个变量表现为 DataArray。从关系上看DataArray 适合表示一个变量Dataset 适合表示一组共享坐标、可以共同分析的变量。DataTree具有层级结构的 Dataset 集合并不是所有数据都适合放进一个 Dataset。Dataset 要求同名维度和坐标能够对齐但一些研究数据本身具有分组和层级结构。例如研究项目 ├── 原始数据 │ ├── 气象数据 │ └── 经济数据 ├── 清洗数据 │ ├── 年度数据 │ └── 月度数据 └── 模型结果 ├── 基准模型 └── 稳健性检验这些分组中的数据可能具有不同维度、不同坐标和不同变量强行把它们合并到一个 Dataset 中并不合适。DataTree就是为这种情况设计的。DataTree 是 xarray 最高层的数据结构。它由一个根节点和若干子节点组成每个节点本身都是一个DataTree对象并且可以保存与单个 Dataset 等价的数据。节点还可以继续包含子节点从而形成树状结构。例如raw_dataxr.Dataset({temperature:temperature,humidity:humidity,})processed_dataraw_data.mean(dimdate)research_treexr.DataTree(nameresearch,datasetxr.Dataset(attrs{title:城市天气研究}),children{raw:xr.DataTree(datasetraw_data,),processed:xr.DataTree(datasetprocessed_data,),},)research_tree它的逻辑结构是research ├── raw │ ├── temperature │ └── humidity └── processed ├── temperature └── humidity可以通过路径访问节点research_tree[raw]访问节点中的变量research_tree[raw][temperature]每个节点的.dataset属性提供一个不可变的 Dataset 视图如果需要取得一个可以独立修改的 Dataset可以使用raw_dataset_copyresearch_tree[raw].to_dataset()DataTree 还支持坐标从父节点向子节点继承。公共坐标可以定义在上层节点中下层节点直接使用而不必在每个分组中重复保存。不过如果子节点使用了与父节点同名的维度或索引它们必须能够对齐。DataTree 对应 NetCDF 和 Zarr 中的 group 概念因此特别适合读取和表达带有分组结构的文件。一个节点可以理解为一个数据组一个子树则可以理解为一组相关的 Dataset。三种数据结构之间的关系DataArray、Dataset 和 DataTree 分别解决不同层次的问题。DataArray 解决的是单个变量的语义问题它让一个数组拥有维度、坐标、名称和属性。Dataset 解决的是多个变量的组织问题它让一组变量共享并对齐维度和坐标。DataTree 解决的是多个异构数据集的分层问题当不同数据组无法合理地放进同一个 Dataset 时可以将它们组织为具有父子关系的节点。用一个研究项目来类比DataArray一项指标 例如城市气温 Dataset一张具有共同索引的数据表 例如同时包含气温、湿度和降水的天气数据集 DataTree一个包含多个数据组的项目目录 例如原始数据、清洗数据和模型结果它们共同构成了从底层数组到复杂科研数据组织的完整层次底层数组 NumPy / Dask / CuPy ↓ DataArray 单个带标签变量 ↓ Dataset 多个相互对齐的变量 ↓ DataTree 多个具有层级关系的数据集因此xarray 的价值并不只在于能够读写 NetCDF。更重要的是它提供了一种稳定的方式来描述多维数据变量是什么维度是什么每个位置对应什么坐标不同数据之间应当如何对齐以及数据规模扩大以后应当如何交给 Dask 分块计算。NumPy 解决了多维数值“如何计算”的问题xarray 在此基础上进一步解决了这些数值“代表什么”以及“应当如何按照含义组织和计算”的问题。参考资料xarray 数据结构、xarray 术语说明、xarray 层级数据与 DataTree。