如何提高我在用Python填充时间序列和数据列表空缺的性能
我有一个时间序列的数据集,数据频率是每秒10次,持续了好几年。光是一年的数据就有大约3.1亿行(每行都有一个时间戳和8个浮点值)。我的数据中有一些空缺,我需要找出来并用'NaN'填补。下面的Python代码可以做到这一点,但性能太差了,处理这些数据的速度远远不够快,我根本无法在合理的时间内完成。
下面是一个简单的示例。我有一个时间序列(time-seris-data)和两个长度相同的数据列表:
series = [1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1]
data_a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]
我希望时间序列每次增加1,所以序列中的空缺是4.1、5.1、6.1、11.1、12.1、13.1、17.1、18.1、19.1。data_a和data_b这两个列表应该用浮点数(nan)填充。比如说,data_a应该变成:
[1.2, 1.2, 1.2, nan, nan, nan, 2.2, 2.2, 2.2, 2.2, nan, nan, nan, 3.2, 3.2, 3.2, nan, nan, nan, 4.2]
我用以下方法实现了这个:
d_max = 1.0 # Normal increment in series where no gaps shall be filled
shift = 0
for i in range(len(series)-1):
diff = series[i+1] - series[i]
if diff > d_max:
num_fills = round(diff/d_max)-1 # Number of fills within one gap
for it in range(num_fills):
data_a.insert(i+1+it+shift, float(nan))
data_b.insert(i+1+it+shift, float(nan))
shift = int(shift + num_fills) # Shift the index by the number of inserts from the previous gap filling
我还寻找了其他解决这个问题的方法,但只发现了使用find()函数来找出空缺的索引。这个find()函数比我的方法快吗?如果是的话,我该如何更有效地在data_a和data_b中插入NaN呢?
2 个回答
如果我没记错的话,往Python列表里插入东西是比较耗费资源的,尤其是当列表变大的时候。
我建议不要把你那些巨大的数据集都加载到内存里,而是用一个生成器函数来逐个处理它们,像这样:
from itertools import izip
series = [1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1]
data_a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]
def fillGaps(series,data_a,data_b,d_max=1.0):
prev = None
for s, a, b in izip(series,data_a,data_b):
if prev is not None:
diff = s - prev
if s - prev > d_max:
for x in xrange(int(round(diff/d_max))-1):
yield (float('nan'),float('nan'))
prev = s
yield (a,b)
newA = []
newB = []
for a,b in fillGaps(series,data_a,data_b):
newA.append(a)
newB.append(b)
比如,可以把数据读入到izip中,然后再写出来,而不是一个一个地添加到列表里。
首先,要明白你最里面的循环其实是没必要的:
for it in range(num_fills):
data_a.insert(i+1+it+shift, float(nan))
这和下面的代码是一样的:
data_a[i+1+shift:i+1+shift] = [float(nan)] * int(num_fills)
这样做可能会稍微快一点,因为减少了内存分配和移动数据的操作。
然后,对于大规模的数字计算问题,建议你使用 NumPy。虽然学习起来可能需要一些时间,但性能提升会非常显著。可以从这样的代码开始:
import numpy as np
series = np.array([1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1])
data_a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]
d_max = 1.0 # Normal increment in series where no gaps shall be filled
shift = 0
# the following two statements use NumPy's broadcasting
# to implicit run some loop at the C level
diff = series[1:] - series[:-1]
num_fills = np.round(diff / d_max) - 1
for i in np.where(diff > d_max)[0]:
nf = num_fills[i]
nans = [np.nan] * nf
data_a[i+1+shift:i+1+shift] = nans
data_b[i+1+shift:i+1+shift] = nans
shift = int(shift + nf)