Pandas重采样(上采样):如何避免缺失日期?能否跳过零值/缺失数据日期?
好的,咱们来一步步解决你关于Pandas重采样的这两个问题:
首先得明确:常规的resample上采样会生成完整的时间网格,哪怕某些日期没有原始数据,这就是你说的“缺失日期”。要避免这种情况,核心思路是只针对有原始数据的日期进行上采样,而不是填充整个时间范围。这里有两个实用的方法:
方法一:用explode生成目标时间点
先给每个原始数据的日期生成你需要的高频时间序列,再通过explode展开,这样只会保留有原始数据的日期对应的高频时间点:
import pandas as pd # 示例原始数据:只有2023-01-01和2023-01-03有数据 df = pd.DataFrame( {'value': [10, 20]}, index=pd.to_datetime(['2023-01-01', '2023-01-03']) ) # 给每个原始日期生成当天的每6小时时间点 df['high_freq_timestamps'] = df.index.apply( lambda x: pd.date_range(start=x, end=x + pd.Timedelta(days=1), freq='6H', closed='left') ) # 展开时间序列并设置为索引 expanded_df = df.explode('high_freq_timestamps').set_index('high_freq_timestamps') # 清理多余列 expanded_df = expanded_df.drop(columns=['high_freq_timestamps'])
运行后,你会发现结果里只有2023-01-01和2023-01-03的6小时时间点,完全不会出现2023-01-02的任何行。
方法二:按日期分组后组内重采样
把原始数据按日期分组,然后在每个组内单独重采样,这样只会对有数据的日期进行上采样:
# 按索引的日期部分分组,组内重采样为6小时频率并填充 grouped_resampled = df.groupby(df.index.date).resample('6H').ffill() # 移除多余的分组索引层级 grouped_resampled = grouped_resampled.reset_index(level=0, drop=True)
这个方法的结果和上面完全一致,而且代码更简洁,适合处理较大的数据集。
很遗憾,Pandas的resample本身没有直接跳过这类行的内置参数——因为resample的核心设计就是生成完整的时间网格,再进行聚合或填充。不过我们可以用更高效的方式处理,不需要事后单独过滤(或者说,把过滤和重采样做成链式操作,性能和内置选项差不多):
处理缺失数据(NaN)
如果重采样后某些时间间隔因为没有原始数据生成了NaN,直接在聚合后链式调用dropna()即可,这是向量化操作,效率很高:
# 重采样为日频率并求和,同时移除NaN行 daily_sum = df.resample('D').sum().dropna()
注意:不同聚合函数对空组的处理不同,比如sum()会返回0而不是NaN,这时候需要用下面的零值过滤方法。
处理零值日期
如果重采样后某些行的值为0,用query()直接过滤,同样是向量化操作,性能优异:
# 重采样为日频率求和,移除值为0的行 daily_sum = df.resample('D').sum().query('value != 0')
更高效的进阶技巧
如果你提前知道哪些日期有数据,可以先提取这些有效日期,再针对性地重采样,避免生成多余的行:
# 先获取有数据的日期(通过count()判断) valid_dates = df.resample('D').count().query('value > 0').index # 只在有效日期上进行重采样聚合 daily_sum = df.resample('D').sum().loc[valid_dates]
这个方法在处理超大型数据集时,能稍微减少内存占用,因为不会生成那些注定要被过滤的行。
内容的提问来源于stack exchange,提问作者agarg

