我正在使用此代码通过滚动窗口在数据框中应用函数(funcX
)。主要问题是此数据帧(data
)的大小非常大,我正在寻找一种更快的方法来完成此任务。
import numpy as np
def funcX(x):
x = np.sort(x)
xd = np.delete(x, 25)
med = np.median(xd)
return (np.abs(x - med)).mean() + med
med_out = data.var1.rolling(window = 51, center = True).apply(funcX, raw = True)
使用此函数的唯一原因是,计算出的中位数是除去中间值后的中位数。因此,与在滚动窗口末尾添加.median()
不同。
答案 0 :(得分:5)
要有效,窗口算法必须链接两个重叠窗口的结果。
在这里,其中:med0
的中位数,med
的中位数,x \ med0
,xl
之前的med
元素和{{ {1}}在排序后的元素中,xg
可以看成是:
med
因此,建议保留一个缓冲区,该缓冲区代表已排序的当前窗口funcX(x)
和<|x-med|> + med = [sum(xg) - sum(xl) - |med0-med|] / windowsize + med
。即时使用Numba进行编译,可以产生很好的性能。
首先进行缓冲区管理:
sum(xg)
对第一个窗口进行排序,并计算left({sum(xl)
)和right(init
)的总和。
xls
xgs
是线性部分。它更新缓冲区,并保持其sorted。 import numpy as np
import numba
windowsize = 51 #odd, >1
halfsize = windowsize//2
@numba.njit
def init(firstwindow):
buffer = np.sort(firstwindow)
xls = buffer[:halfsize].sum()
xgs = buffer[-halfsize:].sum()
return buffer,xls,xgs
计算shift
中插入和删除的位置。由于np.searchsorted
和O(log(windowsize))
并非对称情况,因此是技术性的。
xin<xout
xout<xin
更新缓冲区和左右部分的总和。由于@numba.njit
def shift(buffer,xin,xout):
i_in = np.searchsorted(buffer,xin)
i_out = np.searchsorted(buffer,xout)
if xin <= xout :
buffer[i_in+1:i_out+1] = buffer[i_in:i_out]
buffer[i_in] = xin
else:
buffer[i_out:i_in-1] = buffer[i_out+1:i_in]
buffer[i_in-1] = xin
return i_in, i_out
和update
并非对称情况,因此是技术性的。
xin<xout
xout<xin
等同于缓冲区上的原始@numba.njit
def update(buffer,xls,xgs,xin,xout):
xl,x0,xg = buffer[halfsize-1:halfsize+2]
i_in,i_out = shift(buffer,xin,xout)
if i_out < halfsize:
xls -= xout
if i_in <= halfsize:
xls += xin
else:
xls += x0
elif i_in < halfsize:
xls += xin - xl
if i_out > halfsize:
xgs -= xout
if i_in > halfsize:
xgs += xin
else:
xgs += x0
elif i_in > halfsize+1:
xgs += xin - xg
return buffer, xls, xgs
。 func
。
funcX
O(1)
是全局函数。 @numba.njit
def func(buffer,xls,xgs):
med0 = buffer[halfsize]
med = (buffer[halfsize-1] + buffer[halfsize+1])/2
if med0 > med:
return (xgs-xls+med0-med) / windowsize + med
else:
return (xgs-xls+med-med0) / windowsize + med
。
med
性能:
O(data.size * windowsize)
速度快了250倍,窗口大小=51。一个小时变成15秒。