DataFrame重采样的定制化处理
对于时间序列数据,有时候我们期望的采样间隔和实际的采样间隔不一致,将采样频率较高的数据汇总处理成采样频率较低的数据的过程称为降采样,在Pandas中通过df.resample()实现。
降采样首先需要指定采样频率,将原数据按目标频率划分成若干序列,并对序列中的数据进行一定操作,生成目标频率下的表征值。
Pandas提供了包括求和、均值、方差等在内的常规方法用于计算表征值,这些方法通常能满足绝大多数的应用场景,但有时候,尤其是数据中存在缺失值的情况下,用常规方法计算出的表征值可能不够合理。此时,用户可以定义一个函数来处理采样序列的数据,以使表征值达到想要的效果。
下面是一个示例场景:假设处理高速公路检测器流量数据,原始数据每30s收集一次,数据有缺失情况。现考虑统计每2min的流量总和:在每个2min的数据中,如果缺失2个以内数据,就用均值补全再求和;否则,认为这2min的数据缺失。
首先构造数据集
# 生成20个数据点
index = pd.date_range('1/1/2000 00:00:00', periods=20, freq='30s')
series = pd.Series(range(20), index=index)
# 设置一些缺失值
series.iloc[[1,5,6,7,-1,-2,-3,-4]] = np.nan
series
Out[16]:
2000-01-01 00:00:00 0.0
2000-01-01 00:00:30 NaN
2000-01-01 00:01:00 2.0
2000-01-01 00:01:30 3.0
2000-01-01 00:02:00 4.0
2000-01-01 00:02:30 NaN
2000-01-01 00:03:00 NaN
2000-01-01 00:03:30 NaN
2000-01-01 00:04:00 8.0
2000-01-01 00:04:30 9.0
2000-01-01 00:05:00 10.0
2000-01-01 00:05:30 11.0
2000-01-01 00:06:00 12.0
2000-01-01 00:06:30 13.0
2000-01-01 00:07:00 14.0
2000-01-01 00:07:30 15.0
2000-01-01 00:08:00 NaN
2000-01-01 00:08:30 NaN
2000-01-01 00:09:00 NaN
2000-01-01 00:09:30 NaN
Freq: 30S, dtype: float64
首先看一下直接用sum的效果:
series_2min = series.resample('2min').sum()
series_2min
Out[17]:
2000-01-01 00:00:00 5.0
2000-01-01 00:02:00 4.0
2000-01-01 00:04:00 38.0
2000-01-01 00:06:00 54.0
2000-01-01 00:08:00 0.0
Freq: 2T, dtype: float64
显然这是直接对每4个数据求和,nan被视为0,全部为nan的情况下认为和为0而不是nan,这不太合理。
我们可以通过用户自定义的函数来按合理的方法处理集计的数据。按如下方式定义和调用:
def custom_resampler(array_like):
# 定义一个函数来处理resample后的每一部分数据
no_of_nan = array_like.isnull().sum(axis=0) # nan的个数
lens = len(array_like) # 总元素个数
if no_of_nan/lens>=0.5: # 如果nan占了五成以上
return_value = np.nan # 返回nan
else:
# 否则用均值填充缺失值,并返回和
array_like[array_like.isnull()] = array_like.mean()
return_value = array_like.sum()
return return_value
series_2min = series.resample('2min').apply(custom_resampler)
series_2min
Out[20]:
2000-01-01 00:00:00 6.666667
2000-01-01 00:02:00 NaN
2000-01-01 00:04:00 38.000000
2000-01-01 00:06:00 54.000000
2000-01-01 00:08:00 NaN
Freq: 2T, dtype: float64
通过定义custom_resampler函数并用.apple(custom_resampler)调用,实现了我们的目标,在默认方法无法满足我们的需求时,自定义的方法很好用。