如何基于xArray实现DataArray滚动窗口的分位数计算?
用xarray原生方式实现滚动窗口分位数计算
没错,xarray自带的rolling()接口目前确实没内置分位数/百分位数的计算功能,不过咱们不用非得转pandas,用xarray原生工具也能实现。下面给你两种原生方案,再和你现在的转pandas方法对比下:
方案1:用rolling().construct()搭配quantile()
这是最贴合xarray原生思路的做法:先把滚动窗口转换成一个新维度,然后在这个维度上计算分位数就行:
import xarray as xr import numpy as np times = np.arange(0, 30) locs = ['A', 'B', 'C', 'D'] signal = xr.DataArray( np.random.rand(len(times), len(locs)), coords=[times, locs], dims=['time', 'locations'] ) window = 5 # 生成滚动窗口,新增一个window_dim维度 rolled_windows = signal.rolling(time=window, center=True).construct("window_dim") # 在窗口维度上计算25分位数,之后删掉这个临时维度 quantile_result = rolled_windows.quantile(0.25, dim="window_dim") # 手动去掉首尾的无效值(因为center=True,首尾各window//2个值是NaN) clean_result = quantile_result.isel(time=slice(window//2, -window//2))
这个方法全程用xarray原生API,不用依赖pandas。不过正如你发现的,它在大数据集上性能不如转pandas的方法——毕竟多了一个窗口维度,内存和计算量都会上去。
方案2:用rolling().reduce()自定义聚合函数
xarray的rolling().reduce()支持传入自定义的聚合函数,咱们可以用这个来实现分位数计算:
def calc_rolling_quantile(arr, q=0.25): # arr的第一个轴是窗口维度,我们在这个轴上计算分位数,同时处理NaN return np.nanquantile(arr, q, axis=0) quantile_result = signal.rolling(time=window, center=True).reduce(calc_rolling_quantile, q=0.25) # 同样清理首尾的NaN clean_result = quantile_result.isel(time=slice(window//2, -window//2))
这个方法不用额外生成窗口维度,内存占用比第一个方案低,性能也更好一点,但还是比不过你现在用的转pandas方案——毕竟pandas的rolling实现是经过深度优化的。
三种方法的优劣对比
- 转pandas方案:你当前的方法性能最好,尤其是数据集大的时候优势明显;唯一的小缺点是需要来回转换数据结构,要是坐标比较复杂的话得注意对齐问题。
- construct()+quantile()方案:纯原生,代码好懂,适合小数据集或者需要完整保留xarray坐标元数据的场景,但性能拉胯。
- reduce()+自定义函数方案:原生实现,内存占用低,性能介于前两者之间,但自定义函数要处理好NaN的情况。
如果你的核心需求是性能,那你现在的转pandas方案其实非常靠谱;要是想尽可能用xarray原生API,上面两种方法都能满足你的要求。
内容的提问来源于stack exchange,提问作者davidr
相关产品推荐
相关产品推荐

