带DatetimeIndex的DataFrame用字典替换NaN失效,求高效实现方案
高效填充带DatetimeIndex的DataFrame缺失值
我有一个存储小时级测量数据的字典,其中部分条目缺失(存在数据间隙)。当前做法是创建一个带小时级DatetimeIndex、预填充NaN的DataFrame,再尝试用字典替换DataFrame中的值,后续还要插值消除NaN。但之前尝试的几种方法都无效,只有循环赋值能成功,但效率极低(字典约有10000条数据),求高效实现方案。
原代码及运行结果:
import pandas as pd import numpy as np dataRange = pd.date_range(pd.to_datetime('2023-01-01 01:00:00'), pd.to_datetime('2023-01-01 05:00:00'), freq='H') df = pd.DataFrame(np.nan, index=dataRange, columns=['gas']) df['gas'] = pd.to_numeric(df['gas'], errors='coerce') gasDict = {'2023-01-01 01:00:00' : 40, '2023-01-01 03:00:00' : 20 } # 以下3种方法均无效 # 方法来自stackoverflow:remap-values-in-pandas-column-with-a-dict-preserve-nans df1 = df['gas'].map(gasDict).fillna(df['gas']) print(df1) df2 = df['gas'].map(gasDict) print(df2) df3 = df.replace({'gas': gasDict}) print(df3) # 此方法正确但效率极低: for key, value in gasDict.items(): df.at[pd.to_datetime(key)] = value print(df)
运行结果:
2023-01-01 01:00:00 NaN 2023-01-01 02:00:00 NaN 2023-01-01 03:00:00 NaN 2023-01-01 04:00:00 NaN 2023-01-01 05:00:00 NaN Freq: H, Name: gas, dtype: float64 2023-01-01 01:00:00 NaN 2023-01-01 02:00:00 NaN 2023-01-01 03:00:00 NaN 2023-01-01 04:00:00 NaN 2023-01-01 05:00:00 NaN Freq: H, Name: gas, dtype: float64 gas 2023-01-01 01:00:00 NaN 2023-01-01 02:00:00 NaN 2023-01-01 03:00:00 NaN 2023-01-01 04:00:00 NaN 2023-01-01 05:00:00 NaN gas 2023-01-01 01:00:00 40.0 2023-01-01 02:00:00 NaN 2023-01-01 03:00:00 20.0 2023-01-01 04:00:00 NaN 2023-01-01 05:00:00 NaN
问题原因
之前的方法无效是因为:
map()和replace()都是基于列值进行匹配替换,但原DataFrame的gas列全是NaN,字典的键是日期字符串,和列值完全不匹配,所以无法替换。- 循环赋值效率低是因为每次
df.at[]都是单条操作,处理10000条数据时会产生大量IO开销。
高效解决方案
方法1:字典转Series后用combine_first()赋值
把字典转换成带DatetimeIndex的Series,利用pandas的索引对齐特性,通过combine_first()填充原DataFrame的NaN值:
import pandas as pd import numpy as np dataRange = pd.date_range(pd.to_datetime('2023-01-01 01:00:00'), pd.to_datetime('2023-01-01 05:00:00'), freq='H') df = pd.DataFrame(np.nan, index=dataRange, columns=['gas']) df['gas'] = pd.to_numeric(df['gas'], errors='coerce') gasDict = {'2023-01-01 01:00:00' : 40, '2023-01-01 03:00:00' : 20 } # 高效方法:字典转Series,利用索引对齐填充 gas_series = pd.Series(gasDict, index=pd.to_datetime(list(gasDict.keys()))) df['gas'] = df['gas'].combine_first(gas_series) print(df)
运行结果:
gas 2023-01-01 01:00:00 40.0 2023-01-01 02:00:00 NaN 2023-01-01 03:00:00 20.0 2023-01-01 04:00:00 NaN 2023-01-01 05:00:00 NaN
方法2:用update()直接更新
update()方法会根据索引匹配,用新数据覆盖原DataFrame对应位置的值,同样依赖索引对齐,效率极高:
# 另一种高效方法:使用update gas_series = pd.Series(gasDict, index=pd.to_datetime(list(gasDict.keys()))) df.update(gas_series) print(df)
运行结果与方法1完全一致。
两种方法的区别
combine_first():保留原DataFrame中非NaN的值,仅用新Series的值填充原NaN的位置(索引匹配时)。update():直接用新Series的值覆盖原DataFrame中对应索引的位置,不管原位置是否为NaN。
对于你的场景,两种方法效果相同,因为原DataFrame全是NaN。
内容的提问来源于stack exchange,提问作者monok
相关产品推荐
相关产品推荐

