我有一个数据帧,其中包含来自陀螺仪的时间序列数据,以 20 Hz(每 50 毫秒)采样一次。我需要使用 4 秒的移动窗口来计算来自引用 4 秒信号的 DTW 距离。
我正在使用此代码:
df['Gyro_Z_DTW']=df['Gyro_Z'].rolling(window='4s',min_periods=80).apply(DTWDistanceWindowed,raw=False)
其中函数DTWDistanceWindowed()如下:
def DTWDistanceWindowed(entry):
w=10
s1=entry
s2=reference
DTW={}
w = max(w, abs(len(s1)-len(s2)))
print('window = ',w)
for i in range(-1,len(s1)):
for j in range(-1,len(s2)):
DTW[(i, j)] = float('inf')
DTW[(-1, -1)] = 0
for i in range(len(s1)):
for j in range(max(0, i-w), min(len(s2), i+w)):
dist= (s1[i]-s2[j])**2
DTW[(i, j)] = dist + min(DTW[(i-1, j)],DTW[(i, j-1)], DTW[(i-1, j-1)])
return math.sqrt(DTW[len(s1)-1, len(s2)-1])
# adapted method from #http://alexminnaar.com/2014/04/16/Time-Series-Classification-and-Clustering-with-Python.html
它可以工作,但是如果移动窗口每次可以滑动 500 毫秒(而不是 50 毫秒),我可以节省一些时间。
有办法做到这一点吗?
如果您知道更好的方法,我愿意接受其他建议,而不是滚动。
最佳答案
一种方法是检查 entry
的第一个(或任何索引)是否是 500ms 的倍数,如果不是,则返回 np.nan
。那么“复杂”的计算只会每 500ms 发生一次。所以函数是
def DTWDistanceWindowed(entry):
if bool(entry.index[0].microsecond%500000):
return np.nan
w=10
s1=entry
....# same as your function after
有趣的是,pd.Timestamp (entry.index[0]
的类型)具有微秒属性,但没有毫秒属性,因此使用%500000
。
编辑:现在如果你想加速该功能,你可以使用 numpy 数组,如下所示:
#sample data
np.random.seed(6)
nb = 200
df = pd.DataFrame({'Gyro_Z':np.random.random(nb)},
index=pd.date_range('2020-05-15', freq='50ms', periods=nb))
reference = np.random.random(10)
# create a for reference with your function
a = df['Gyro_Z'].rolling(window='4s',min_periods=80).apply(DTWDistanceWindowed,raw=False)
使用 numpy 定义函数
def DTWDistanceWindowed_np(entry):
if bool(entry.index[0].microsecond%500000):
return np.nan
w=10
s1=entry.to_numpy()
l1 = len(s1) # calcualte once the len of s1
# definition of s2 and its length
s2 = np.array(reference)
l2 = len(s2)
w = max(w, abs(l1-l2))
# create an array of inf and initialise
DTW=np.full((l1+1,l2+1), np.inf)
DTW[0, 0] = 0
# avoid calculate some difference several times
s1ms2 = (s1[:,None]-s2)**2
# do the loop same way, note the small change in bounds
for i in range(1,l1+1):
for j in range(max(1, i-w), min(l2+1, i+w)):
DTW[i, j] = s1ms2[i-1,j-1] + min(DTW[i-1, j],DTW[i, j-1], DTW[i-1, j-1])
return math.sqrt(DTW[l1, l2])
# use it to create b
b = df['Gyro_Z'].rolling(window='4s',min_periods=80).apply(DTWDistanceWindowed_np,raw=False)
# compare a every 10 rows and b and mot the nan rows
print ((b.dropna() == a.dropna()[::10]).all())
# True
时间安排:
#original solution
%timeit df['Gyro_Z'].rolling(window='4s',min_periods=80).apply(DTWDistanceWindowed,raw=False)
3.31 s ± 422 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# with numpy and 1 out of 10 rows
%timeit df['Gyro_Z'].rolling(window='4s',min_periods=80).apply(DTWDistanceWindowed_np,raw=False)
41.7 ms ± 9.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
这样做 if bool(...
已经快了近 10 倍,但使用 numpy
又快了 9 倍。速度可能取决于关于引用尺寸,我还没有真正检查过。
关于python - 如何向 pandas dataframe 滚动窗口添加步骤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61830275/