M H*_*ley 7 python time-series vectorization series pandas
假设我有两个时间戳记,分别是5小时范围内的开始/结束时间对。它们不一定是顺序的,也不能量化为小时。
import pandas as pd
start = pd.Series(pd.date_range('20190412',freq='H',periods=25))
# Drop a few indexes to make the series not sequential
start.drop([4,5,10,14]).reset_index(drop=True,inplace=True)
# Add some random minutes to the start as it's not necessarily quantized
start = start + pd.to_timedelta(np.random.randint(59,size=len(start)),unit='T')
end = start + pd.Timedelta('5H')
Run Code Online (Sandbox Code Playgroud)
现在假设我们有一些以分钟为单位的时间戳数据,其范围涵盖了所有开始/结束对。
data_series = pd.Series(data=np.random.randint(20, size=(75*60)),
index=pd.date_range('20190411',freq='T',periods=(75*60)))
Run Code Online (Sandbox Code Playgroud)
我们希望从和时间data_series范围内获得的值。可以在循环中天真地完成此操作startend
frm = []
for s,e in zip(start,end):
frm.append(data_series.loc[s:e].values)
Run Code Online (Sandbox Code Playgroud)
如我们所见,这种幼稚的方法遍历每对start和end日期,并从数据中获取值。
但是,如果实现len(start)较大,则此实现速度很慢。有没有办法利用pandas矢量功能执行这种逻辑?
我觉得这几乎就像我想应用.loc矢量,pd.Series而不是单个应用pd.Timestamp?
编辑
使用.apply天真for循环比使用天真循环更有效。我希望能指出纯向量解决方案的方向
像往常一样,pandas 会花时间在 处搜索一个特定索引data_series.loc[s:e],其中s和e是日期时间索引。这在循环时成本很高,而这正是我们需要改进的地方。我们可以用向量化的方式找到所有这些索引searchsorted。然后,我们将提取值data_series作为数组并使用从searchsorted简单的基于整数的索引获得的索引。因此,将存在一个循环,只需最少的简单切片数组工作。
一般口头禅是 - 以矢量化方式进行大部分预处理工作,并在循环时进行最少处理。
\n\n实现看起来像这样 -
\n\ndef select_slices_by_index(data_series, start, end):\n idx = data_series.index.values\n S = np.searchsorted(idx,start.values)\n E = np.searchsorted(idx,end.values)\n ar = data_series.values\n return [ar[i:j] for (i,j) in zip(S,E+1)]\nRun Code Online (Sandbox Code Playgroud)\n\nNumPy-stridingstarts对于所有条目的和之间的时间段ends相同并且所有切片都被该长度覆盖的特定情况,即没有越界情况,我们可以使用NumPy\'s sliding window trick。
我们可以利用np.lib.stride_tricks.as_stridedbasedscikit-image\'s view_as_windows来获得滑动窗口。有关使用as_strided基于view_as_windows的更多信息。
from skimage.util.shape import view_as_windows\n\ndef select_slices_by_index_strided(data_series, start, end):\n idx = data_series.index.values\n L = np.searchsorted(idx,end.values[0])-np.searchsorted(idx,start.values[0])+1\n S = np.searchsorted(idx,start.values)\n ar = data_series.values\n w = view_as_windows(ar,L)\n return w[S]\nRun Code Online (Sandbox Code Playgroud)\n\nthis post如果您无权访问,请使用scikit-image.
让我们根据100x给定的示例数据扩展所有内容并进行测试。
设置 -
\n\nnp.random.seed(0)\nstart = pd.Series(pd.date_range(\'20190412\',freq=\'H\',periods=2500))\n\n# Drop a few indexes to make the series not sequential\nstart.drop([4,5,10,14]).reset_index(drop=True,inplace=True)\n\n# Add some random minutes to the start as it\'s not necessarily quantized\nstart = start + pd.to_timedelta(np.random.randint(59,size=len(start)),unit=\'T\')\n\nend = start + pd.Timedelta(\'5H\')\ndata_series = pd.Series(data=np.random.randint(20, size=(750*600)), \n index=pd.date_range(\'20190411\',freq=\'T\',periods=(750*600)))\nRun Code Online (Sandbox Code Playgroud)\n\n时间安排 -
\n\nIn [156]: %%timeit\n ...: frm = []\n ...: for s,e in zip(start,end):\n ...: frm.append(data_series.loc[s:e].values)\n1 loop, best of 3: 172 ms per loop\n\nIn [157]: %timeit select_slices_by_index(data_series, start, end)\n1000 loops, best of 3: 1.23 ms per loop\n\nIn [158]: %timeit select_slices_by_index_strided(data_series, start, end)\n1000 loops, best of 3: 994 \xc2\xb5s per loop\n\nIn [161]: frm = []\n ...: for s,e in zip(start,end):\n ...: frm.append(data_series.loc[s:e].values)\n\nIn [162]: np.allclose(select_slices_by_index(data_series, start, end),frm)\nOut[162]: True\n\nIn [163]: np.allclose(select_slices_by_index_strided(data_series, start, end),frm)\nOut[163]: True\nRun Code Online (Sandbox Code Playgroud)\n\n140x+并170x用这些加速!