Pandas处理夏令时导致时间戳重复无法设为索引问题
问题:夏令时导致重复时间戳,无法设置索引并补全时间序列
我用pandas读取CSV文件,文件包含时间戳(格式如31/12/2022 21:30)和浮点能耗值两列,数据由每15分钟记录一次的仪器生成。设备故障会导致部分时段记录缺失,我需要补全这些缺口(用0值或后续均值分摊)。但2022年10月30日夏令时切换时出现重复时间戳,无法将时间列设为索引;转换时区到Europe/Madrid后仍无效,执行代码时在set_index步骤报错cannot reindex a non-unique index with a method or limit,且时间列不显示时区标识(+01/+02)。
原代码:
telemedida_df=pd.read_csv("filename.csv",delimiter=";",decimal=",") telemedida_df["Fecha/hora"]=pd.to_datetime(telemedida_df["Fecha/hora"],format='%d/%m/%Y %H:%M',utc=True) telemedida_df["Fecha/hora"] = telemedida_df["Fecha/hora"].dt.tz_convert('Europe/Madrid') df_new = (telemedida_df.assign(date=telemedida_df["Fecha/hora"].dt.date) #create new col 'date' from the timestamp .set_index("Fecha/hora") #Error here .groupby('date') .apply(lambda x: x.resample('15Min') .ffill()) .reset_index('date', drop=True) .drop('date',1) .reset_index() )
CSV示例数据(西班牙逗号作为小数分隔符):
Fecha/hora;Consumo 30/10/2022 0:15;6,9 30/10/2022 0:30;6,6 30/10/2022 0:45;6,7 30/10/2022 1:00;6,7 30/10/2022 1:15;6,6 30/10/2022 1:30;6,9 30/10/2022 1:45;6,8 30/10/2022 2:00;6,7 30/10/2022 2:15;6,6 30/10/2022 2:30;6,7 30/10/2022 2:45;6,7 30/10/2022 2:00;6,7 30/10/2022 2:15;6,7 30/10/2022 2:30;6,8 30/10/2022 2:45;6,7 30/10/2022 3:00;6,8 30/10/2022 3:15;6,8 30/10/2022 3:30;6,6 30/10/2022 3:45;7 30/10/2022 4:00;6,6 30/10/2022 4:15;6,8 30/10/2022 4:30;6,6 30/10/2022 4:45;6,7 30/10/2022 5:00;6,6
解决方案:处理夏令时重复时间戳并补全序列
1. 正确解析带时区的时间戳
原代码错误在于直接用utc=True解析本地时间,应先解析为无时区的本地时间,再指定时区为Europe/Madrid,同时用ambiguous='infer'让pandas自动推断夏令时切换的重复时间:
import pandas as pd # 读取CSV,正确处理分隔符和小数格式 telemedida_df = pd.read_csv("filename.csv", delimiter=";", decimal=",") # 先解析为本地时间,再绑定Europe/Madrid时区,自动处理夏令时重复 telemedida_df["Fecha/hora"] = pd.to_datetime(telemedida_df["Fecha/hora"], format='%d/%m/%Y %H:%M') telemedida_df["Fecha/hora"] = telemedida_df["Fecha/hora"].dt.tz_localize('Europe/Madrid', ambiguous='infer')
2. 手动处理残留重复项(可选)
如果ambiguous='infer'仍有重复时间戳,可合并重复项的能耗值(比如取均值):
# 检查并处理重复时间戳 duplicate_mask = telemedida_df.duplicated(subset="Fecha/hora", keep=False) if duplicate_mask.any(): # 合并重复时间的能耗值,取均值 telemedida_df = telemedida_df.groupby("Fecha/hora").agg({"Consumo": "mean"}).reset_index()
3. 生成完整时间序列并补全缺失值
现在可以设置索引,生成15分钟间隔的完整序列,按需补全缺失值:
# 设置时间索引 telemedida_df = telemedida_df.set_index("Fecha/hora") # 生成覆盖数据起止时间的完整15分钟序列 full_time_index = pd.date_range( start=telemedida_df.index.min(), end=telemedida_df.index.max(), freq='15min', tz='Europe/Madrid' ) # 补全缺失值:这里用0填充,也可替换为ffill()/bfill()或滚动均值 df_completed = telemedida_df.reindex(full_time_index, fill_value=0) # 示例:用后续4个时段的均值分摊缺失值 # df_completed['Consumo'] = df_completed['Consumo'].fillna(df_completed['Consumo'].rolling(4, min_periods=1).mean()) # 重置索引(可选,将时间列恢复为普通列) df_completed = df_completed.reset_index().rename(columns={"index": "Fecha/hora"})
关键说明
tz_localizevstz_convert:tz_localize是给无时区时间戳添加时区信息,tz_convert是转换已有时区的时间到其他时区,原代码用反导致时区识别错误。ambiguous='infer':夏令时切换(西班牙10月最后一个周日)会出现重复本地时间,该参数让pandas根据序列顺序推断时间对应的实际UTC值,生成唯一时区索引。- 补全方式可灵活选择:直接填0、向前填充、滚动均值、线性插值等,按需调整。
内容的提问来源于stack exchange,提问作者Estraviz
相关产品推荐
相关产品推荐

