为Pandas DataFrame补全缺失日期并填0:第一种方案失效原因?
补全缺失日期时reindex丢失数据的根本原因分析
场景描述
需要补全指定日期范围内的缺失日期并填充0,用于后续按日期统计绘图。参考某Stack Overflow方案时出现数据丢失,所有quantity值被清空为0,但另一方案可正常工作。
失效方案代码及输出
# (items_per_day is coming from DB) date_now = datetime.now().date() date_period = date_now-timedelta(weeks=1) data = list(items_per_day) print('data: ', data) idx = pd.period_range(date_period, date_now) df = pd.DataFrame(data) print('df: ', df) df.index = pd.DatetimeIndex(df.index) print('df index: ', df) df = df.reindex(idx, fill_value=0) print('df: ', df)
输出:
data: [{'date': datetime.date(2022, 8, 17), 'quantity': 1}, {'date': datetime.date(2022, 8, 18), 'quantity': 2}, {'date': datetime.date(2022, 8, 19), 'quantity': 3}] df: date quantity 0 2022-08-17 1 1 2022-08-18 2 2 2022-08-19 3 df index: date quantity 1970-01-01 00:00:00.000000000 2022-08-17 1 1970-01-01 00:00:00.000000001 2022-08-18 2 1970-01-01 00:00:00.000000002 2022-08-19 3 df: date quantity 2022-08-13 0 0 2022-08-14 0 0 2022-08-15 0 0 2022-08-16 0 0 2022-08-17 0 0 2022-08-18 0 0 2022-08-19 0 0 2022-08-20 0 0
有效方案代码及输出
date_now = datetime.now().date() date_period = date_now-timedelta(weeks=1) data = list(items_per_day) print('data: ', data) idx = pd.period_range(date_period, date_now) df = pd.DataFrame(data) print('df: ', df) df['date'] = pd.to_datetime(df['date']) dates = pd.date_range(start=date_period, freq='D', periods=7) df = df.set_index('date').reindex(dates.normalize().rename('date')).reset_index() df['quantity'] = df['quantity'].fillna(0) print('df: ', df)
输出:
data: [{'date': datetime.date(2022, 8, 17), 'quantity': 1}, {'date': datetime.date(2022, 8, 18), 'quantity': 2}, {'date': datetime.date(2022, 8, 19), 'quantity': 3}] df: date quantity 0 2022-08-17 1 1 2022-08-18 2 2 2022-08-19 3 df: date quantity 0 2022-08-13 0.0 1 2022-08-14 0.0 2 2022-08-15 0.0 3 2022-08-16 0.0 4 2022-08-17 1.0 5 2022-08-18 2.0 6 2022-08-19 3.0
失效根本原因
索引类型不匹配
第一种方案中,idx = pd.period_range(date_period, date_now)生成的是PeriodIndex类型的日期索引,但你把原DataFrame的索引转成了DatetimeIndex(df.index = pd.DatetimeIndex(df.index)),两种索引类型完全不兼容,reindex时找不到任何匹配项,所以所有行都用fill_value=0填充。索引未关联实际日期列
更关键的是,第一种方案错误地把原DataFrame的默认整数索引转成了DatetimeIndex(变成了1970年的时间戳,因为整数0、1、2被转成了Unix起始时间的微秒级偏移),而没有把数据中的date列设置为索引。这就导致原数据的索引和你要补全的日期范围完全没有交集,reindex自然无法匹配到原有数据,全部被替换成0。有效方案的核心差异
有效方案正确地将date列转为Datetime类型后设置为索引,然后用同类型的DatetimeIndex(pd.date_range生成)进行reindex,此时索引值能和原有数据的日期匹配,缺失的日期才会填充NaN,最后再转成0,保留了原有数据。
内容的提问来源于stack exchange,提问作者uzla
相关产品推荐
相关产品推荐

