多时间序列保留原始数据并插值填充NaN值的实现方法
问题描述
需求1:为每个ID确定时间范围
需要为每个ID找出00:00:00之前最早的测量时间和00:00:00之后最晚的测量时间,以此确定各ID数据的最大重叠起止时间。附带示例数据生成代码:
import pandas as pd import random # 创建示例数据框 df = pd.DataFrame({'timestamp': pd.date_range(start='2022-01-01', end='2022-01-01 23:59', freq='4H')}) df2 = pd.DataFrame({'timestamp': pd.date_range(start='2022-01-01', end='2022-01-01 23:59', freq='5H')}) df['value']= [random.randrange(10, 19) for n in range(len(df))] df['ID']= [random.randrange(1, 4) for n in range(len(df))] df2['value']= [random.randrange(10, 19) for n in range(len(df2))] df2['ID']= [random.randrange(1, 4) for n in range(len(df2))] merged_df = df.merge(df2, how='outer', on =['timestamp']) merged_df.reset_index(drop=True,inplace=True) merged_df.sort_values(by=['timestamp'],inplace=True) print(merged_df)
需求2:多时间序列合并后插值填充
现有多个不同采样间隔的时间序列(例如20分钟、15分钟间隔),合并后需实现:
- 保留
VALUE1列的原始测量值 - 为无原始记录的时间点插值填充数值
最小示例数据
| ID | VALUE1 | TIME |
|---|---|---|
| 25 | 50 | 00:04:00 |
| 25 | 80 | 00:04:30 |
| 25 | 100 | 00:05:00 |
| 25 | 100 | 00:05:30 |
| 25 | 100 | 00:06:00 |
| 18 | 45 | 00:04:02 |
| 18 | 33 | 00:04:22 |
| 18 | 75 | 00:04:42 |
| 18 | 57 | 00:05:02 |
| 18 | 99 | 00:05:22 |
预期输出
| ID | VALUE1 | TIME |
|---|---|---|
| 25 | 50 | 00:04:00 |
| 25 | 插值值=例如(50+80)/2 | 00:04:02 |
| 25 | 插值值 | 00:04:22 |
| 25 | 80 | 00:04:30 |
| 25 | 插值值 | 00:04:42 |
| 25 | 100 | 00:05:00 |
| 25 | 插值值 | 00:05:02 |
| 25 | 插值值 | 00:05:22 |
| 25 | 100 | 00:05:30 |
| 25 | 100 | 00:06:00 |
| 18 | 插值值=例如沿用最近值45 | 00:04:00 |
| 18 | 45 | 00:04:02 |
| 18 | 33 | 00:04:22 |
| 18 | 某插值值 | 00:04:30 |
| 18 | 75 | 00:04:42 |
| 18 | 某插值值 | 00:04:30 |
| 18 | 57 | 00:05:02 |
| 18 | 99 | 00:05:22 |
| 18 | 某插值值 | 00:05:00 |
| 18 | 某插值值 | 00:05:30 |
| 18 | 某插值值 | 00:06:00 |
编辑补充:两个序列时可通过outer merge保留时间点再插值,但需了解多序列场景下的实现方式。
解决方案
针对需求1:确定每个ID的时间范围
- 先确保时间列是
datetime类型,再按ID分组计算时间范围:
# 转换时间列为datetime类型 merged_df['timestamp'] = pd.to_datetime(merged_df['timestamp']) # 提取日期,构造当天0点时间 merged_df['date'] = merged_df['timestamp'].dt.date merged_df['midnight'] = pd.to_datetime(merged_df['date']) # 按ID分组,计算0点前最早、0点后最晚时间 def get_time_range(group): midnight = group['midnight'].iloc[0] before_midnight = group[group['timestamp'] < midnight]['timestamp'] after_midnight = group[group['timestamp'] >= midnight]['timestamp'] return pd.Series({ 'earliest_before_midnight': before_midnight.min() if not before_midnight.empty else None, 'latest_after_midnight': after_midnight.max() if not after_midnight.empty else None }) id_time_ranges = merged_df.groupby('ID').apply(get_time_range).reset_index() print(id_time_ranges)
针对需求2:多时间序列合并后插值填充
多序列场景核心是先构建完整时间轴,再按ID扩展记录,最后保留原始值并插值:
步骤1:合并所有原始数据
统一结构后合并多个时间序列:
# 假设有df1、df2、df3等多个结构为[ID, VALUE1, TIME]的DataFrame all_dfs = [df1, df2, df3] combined_df = pd.concat(all_dfs, ignore_index=True) # 转换时间列为datetime类型 combined_df['TIME'] = pd.to_datetime(combined_df['TIME'], format='%H:%M:%S')
步骤2:构建完整时间轴
提取所有序列的唯一时间点并排序:
all_times = combined_df['TIME'].unique() all_times.sort()
步骤3:扩展每个ID的时间记录
生成ID与时间点的笛卡尔积,合并原始数据保留真实值:
# 生成ID和时间点的全组合 ids = combined_df['ID'].unique() id_time_grid = pd.MultiIndex.from_product([ids, all_times], names=['ID', 'TIME']).to_frame(index=False) # 合并原始数据,缺失值标记为NaN full_df = id_time_grid.merge(combined_df, on=['ID', 'TIME'], how='left')
步骤4:插值填充缺失值
按ID分组选择插值方式,原始值会被保留:
# 基于时间间隔的线性插值(适合不规则时间间隔) full_df['VALUE1'] = full_df.sort_values('TIME').groupby('ID')['VALUE1'].transform( lambda x: x.interpolate(method='time') ) # 若需沿用最近前值,替换为: # full_df['VALUE1'] = full_df.sort_values('TIME').groupby('ID')['VALUE1'].ffill() # 若需沿用最近后值,替换为: # full_df['VALUE1'] = full_df.sort_values('TIME').groupby('ID')['VALUE1'].bfill()
步骤5:排序输出
按ID和时间排序得到最终结果:
full_df.sort_values(['ID', 'TIME'], inplace=True) print(full_df)
说明
- 该方法支持任意数量的时间序列,只需保证所有序列结构统一(ID、VALUE列、时间列)
- 插值方式可根据业务需求调整:
method='time'适合时间间隔不规则的线性插值,ffill()/bfill()适合沿用最近值的场景 - 若时间包含日期,只需确保
TIME列为完整datetime类型,代码逻辑无需改动
内容的提问来源于stack exchange,提问作者dspractician
相关产品推荐
相关产品推荐

