1. 写在前面:这个库到底能不能碰字符串
前几天有个读者在社区里问我,项目里遇到一批带经纬度的站点观测数据,其中站点名称、所属区域这一类的字段都是字符串,他想用 xarray 统一管理这批数据,但不确定 xarray 到底能不能存字符串。我当时的回答是:能存,而且能存得挺好,但你要是拿它去做字符串处理,那大概率会踩坑。
先把结论放在这里,免得你看到一半还在猜:xarray 可以存储字符串类型的数据,不管是 DataArray 还是 Dataset 都能装下字符串。但在字符串的逐元素操作、正则匹配、分词、替换这类“真正意义上的字符串处理”上,它并不擅长,甚至可以说是短板。 它是为带标签的多维数值数组设计的,字符串在里面更多是作为“标签”“维度坐标”“辅助描述列”存在,而不是作为被计算的主体。
这篇内容我会用实际可跑的代码,把“xarray 能不能用于字符串”这件事彻底讲透。会涉及的场景包括:把字符串放进 DataArray 和 Dataset、字符串在文件读写时的表现、xarray 能对字符串做的有限操作、哪些操作必须回到 pandas 或纯 Python 处理、以及我实测下来最顺手的工具切换方法。不管你是刚开始接触 xarray 的新手,还是已经用它处理过不少气象、海洋、遥感数据的熟手,这篇应该都能给你一点参考。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先认识 xarray 的数据模型,再谈字符串
2.1 xarray 到底存储的是什么
xarray 的核心数据模型是带标签的多维数组。你可以把它理解为“NumPy 数组 + pandas 索引”的杂交体。一个 DataArray 由以下几部分组成:values(真正的数据,底层是 NumPy 数组)、dims(维度名称)、coords(坐标,也就是每个维度上的标签)、attrs(元数据)。Dataset 则是多个 DataArray 的集合,它们共享部分或全部维度坐标。
这个设计的目标非常明确:让多维数组的切片、聚合、对齐操作不再依赖“第 0 维第 1 维”这种容易出错的位置索引,而是直接通过维度名和坐标值来操作。比如 ds.sel(time="2024-01-01") 就是按时间坐标取值,不用关心时间在第几维。
那字符串在这里面扮演什么角色?三种典型用途:一是坐标值,比如站点名 station=["北京", "上海", "广州"];二是辅助描述列,比如某个变量对应的单位、数据说明;三是数据本身,比如一列文本内容。前两种是 xarray 的“主场”,第三种就得看具体需求了。
2.2 为什么很多人默认 xarray 不处理字符串
这跟 xarray 的底层实现有关。xarray 在大多数场景下把数据交给 NumPy 来存储和计算,而 NumPy 的字符串支持一直有点“二等公民”的味道。在较老版本的 NumPy 中,字符串数组是固定长度的字节串(dtype='S')或 Unicode 字符串(dtype='U'),对长度不一的字符串支持不太友好,比如 dtype='U10' 就表示最多存 10 个字符,超过会被截断。
另一个原因是,xarray 的很多核心操作,像 mean()、sum()、roll()、interp(),天然是为数值数组设计的。你对一个字符串数组求平均?这在数学上没有意义。所以在 xarray 的官方文档和大多数教程里,字符串示例非常少,导致大家形成了“xarray 只处理数值”的刻板印象。
但刻板印象不等于事实。随着 NumPy 2.0 引入了可变宽度的字符串 dtype(dtype='T',基于 UTF-8),xarray 对字符串的存储能力也有了明显进步。现在我们完全可以把一列站点名、一列文本描述甚至一列较长的句子放进 Dataset 里,不会出现以前那种“字符串被截断”的尴尬。
3. 字符串在 xarray 中的三种存储方式与实测
3.1 直接把字符串放进 DataArray
最直接的操作是创建一个包含字符串数据的 DataArray。比如我有三个站点的名称,可以这样写:
python复制import xarray as xr
import numpy as np
stations = xr.DataArray(
["北京", "上海", "广州"],
dims="station",
coords={"station": [0, 1, 2]},
name="station_name"
)
print(stations)
输出结果会是一个维度为 station、数据为字符串数组的 DataArray。你可以正常索引、切片、按坐标选择:
python复制print(stations.sel(station=1).item()) # 输出:上海
这里注意一个点:在创建时我用了 dims="station",同时坐标也叫 station,这个写法表示维度名和坐标名一致,是 xarray 推荐的做法。如果你有一个维度,但不想给它坐标标签,也可以只写 dims="station" 不传 coords,xarray 会自动生成从 0 开始的整数坐标。
3.2 在 Dataset 中存储字符串列
实际项目中更多是 Dataset 场景。假设我有一批站点观测数据,包含温度、降水和站点名:
python复制ds = xr.Dataset(
{
"temp": (("time", "station"), np.random.rand(4, 3) * 30),
"precip": (("time", "station"), np.random.rand(4, 3) * 10),
},
coords={
"time": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"],
"station": [0, 1, 2],
"station_name": ("station", ["北京", "上海", "广州"]),
"station_type": ("station", ["基准站", "基本站", "一般站"]),
},
)
注意 station_name 和 station_type 这两个坐标,它们的维度是 station,值是字符串。这就是 xarray 存储字符串最典型的姿势——作为坐标标签。好处是:当你要选择某个站点的数据时,可以直接用字符串名称,而不是记数字:
python复制# 用站点名选数据,而不是 station=0
ds.sel(station=1).station_name.item() # 输出:上海
等一下,这里就会有一个天然的限制,也是新手经常犯迷糊的地方:如果你的 station_name 是坐标(coord),那么 xarray 会要求它的长度必须与 station 维度一致,并且它本质上是这个维度的一个“别名”,不能独立变化。 换句话说,坐标中的字
