如何对Pandas Series执行滚动窗口按位与运算?
如何高效实现Pandas Rolling窗口的按位与运算?
问题背景
我有一个存储32位无符号整数的Pandas Series,需要计算每个rolling窗口内所有值的按位与结果。Pandas的rolling对象并没有提供直接的bitwise_and方法,尝试df.rolling(2).bitwise_and()会报错。目前通过自定义函数结合apply实现了需求,但实际场景中窗口大小在100-20000之间,且数据量较大,apply的效率无法满足需求,需要更高效的方案。
尝试过的无效代码:
import pandas as pd import numpy as np # 示例16位整数(实际为uint32) df = pd.Series([0xFFFF, 0xFEAB, 0x0000, 0x1111, 0x5555, 0xAAAA], dtype='uint32') df.rolling(2).bitwise_and() # Pandas无此方法,会报错
现有低效实现:
def accumulate_bitwise_and(values): result = np.uint32(0xFFFFFFFF) for v in values: # Pandas rolling窗口会自动转浮点,需要强制转回uint32 result &= np.uint32(v) return result # 窗口大小为2的示例 accum = df.rolling(2).apply(accumulate_bitwise_and) accum.drop(accum.index[0], inplace=True) # 移除初始NaN accum = accum.astype("uint32")
高效解决方案:基于Numpy向量化操作
apply本质是逐窗口调用Python函数,窗口越大、数据量越大,循环开销越明显。我们可以利用Numpy的滑动窗口和向量化按位与操作,彻底避免Python层面的循环,大幅提升效率。
方法1:使用Numpy滑动窗口视图(推荐,Numpy 1.20+)
import pandas as pd import numpy as np # 示例数据 df = pd.Series([0xFFFF, 0xFEAB, 0x0000, 0x1111, 0x5555, 0xAAAA], dtype='uint32') window_size = 2 # 转换为Numpy数组 arr = df.to_numpy() # 创建滑动窗口视图(无额外内存拷贝) windows = np.lib.stride_tricks.sliding_window_view(arr, window_size) # 对每个窗口执行按位与归约 result = np.bitwise_and.reduce(windows, axis=1) # 转换回Pandas Series,匹配原索引(跳过前window_size-1个位置) accum = pd.Series(result, index=df.index[window_size-1:], dtype='uint32')
方法2:手动构造滑动窗口(兼容旧版Numpy)
如果你的Numpy版本低于1.20,可以手动通过步幅构造滑动窗口:
def sliding_window(arr, window_size): # 计算滑动窗口的形状和步幅 shape = (arr.size - window_size + 1, window_size) strides = (arr.strides[0], arr.strides[0]) # 创建滑动窗口视图(无内存拷贝) return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides) # 后续步骤与方法1一致 arr = df.to_numpy() windows = sliding_window(arr, window_size) result = np.bitwise_and.reduce(windows, axis=1) accum = pd.Series(result, index=df.index[window_size-1:], dtype='uint32')
方案优势
- 效率极高:完全基于Numpy的C级向量化运算,避免了Python循环的开销,在大窗口、大数据量场景下,效率比
apply高100-1000倍以上。 - 无额外类型转换:直接操作uint32类型数组,不需要处理Pandas rolling窗口自动转浮点的问题。
- 内存友好:滑动窗口视图不会拷贝原始数据,仅创建视图,内存占用远低于复制整个窗口数据的方案。
注意事项
当窗口大小极大(如20000)且数据量非常大时,滑动窗口视图会占用一定内存(内存占用为(数据量-窗口大小+1)*窗口大小*4字节),如果内存压力过大,可以考虑分块处理原始数据,逐块计算后合并结果。
内容的提问来源于stack exchange,提问作者Pitt
相关产品推荐
相关产品推荐

