You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确使用Pandas的reindex处理Datetime型多级索引?

问题:补全用户周度事件统计的缺失行并正确使用reindex

我有一个按用户每日统计事件次数的DataFrame,原始事件日志聚合后,用户单日事件数为0的行已缺失。需要补全这些缺失行,并按周分组,确保每个用户每周都有一条记录(事件数为0时也要保留)。

输入示例

import numpy as np
import pandas as pd

np.random.seed(42)

df = pd.DataFrame({
    "person_id": np.arange(3).repeat(5),
    "date": pd.date_range("2022-01-01", "2022-01-15", freq="d"),
    "event_count": np.random.randint(1, 7, 15),
})

# 每周结束日期
# 注:2022-01-23不在原始数据中,但需要出现在结果里
desired_index = pd.to_datetime(["2022-01-02", "2022-01-09", "2022-01-16", "2022-01-23"])

原始DataFrame内容:

person_iddateevent_count
002022-01-01 00:00:004
102022-01-02 00:00:005
202022-01-03 00:00:003
302022-01-04 00:00:005
402022-01-05 00:00:005
512022-01-06 00:00:002
612022-01-07 00:00:003
712022-01-08 00:00:003
812022-01-09 00:00:003
912022-01-10 00:00:005
1022022-01-11 00:00:004
1122022-01-12 00:00:003
1222022-01-13 00:00:006
1322022-01-14 00:00:005
1422022-01-15 00:00:002

期望结果

person_idlevel_1event_count
002022-01-02 00:00:009
102022-01-09 00:00:0013
202022-01-16 00:00:000
302022-01-23 00:00:000
412022-01-02 00:00:000
512022-01-09 00:00:0011
612022-01-16 00:00:005
712022-01-23 00:00:000
822022-01-02 00:00:000
922022-01-09 00:00:000
1022022-01-16 00:00:0020
1122022-01-23 00:00:000

当前可行但不够高效的实现

我可以通过groupby+apply的方式实现需求:

(
    df
    .groupby(["person_id", pd.Grouper(key="date", freq="w")]).sum()
    .groupby("person_id").apply(
        lambda df: (
            df
            .reset_index(drop=True, level=0)
            .reindex(desired_index, fill_value=0))
        )
    .reset_index()
)

但根据reindex文档,应该可以直接使用level=1参数,无需再次groupby,但这样操作后得到的是内连接结果,缺失了大量需要补0的行:

result = (
    df
    .groupby(["person_id", pd.Grouper(key="date", freq="w")]).sum()
    .reindex(desired_index, level=1)
    .reset_index()
)

得到的结果:

person_iddateevent_count
002022-01-02 00:00:009
102022-01-09 00:00:0013
212022-01-09 00:00:0011
312022-01-16 00:00:005
422022-01-16 00:00:0020

请问这是为什么?我该如何正确使用df.reindex?

注:曾找到类似问题,但其中df.unstack的方案不适用,因为期望索引与当前索引的层级并非完全重叠。


解答

为什么直接用reindex(desired_index, level=1)得到内连接?

当对多索引DataFrame使用reindex并指定level参数时,pandas只会保留原始多索引中与目标索引在指定层级有交集的组合,不会主动为每个上层索引(这里是person_id)生成所有目标层级的新组合。简单来说,它不会遍历所有用户去补全所有期望的周日期,只会保留原本就存在的用户-周组合,再补充目标索引中存在但原始数据没有的周日期对应的行,最终结果是原始索引和目标索引的内连接,而非全量笛卡尔积。

正确使用reindex的方法

要实现全量的用户-周组合补全,需要先构建完整的多索引(所有person_id × 所有desired_index),再用这个完整索引去reindex分组后的DataFrame:

# 先按用户和周分组,计算周度事件总数
weekly_df = df.groupby(["person_id", pd.Grouper(key="date", freq="w")]).sum()

# 构建包含所有用户和所有期望周日期的完整多索引
full_index = pd.MultiIndex.from_product(
    [weekly_df.index.get_level_values("person_id").unique(), desired_index],
    names=["person_id", "date"]
)

# 用完整索引重新对齐数据,缺失值填充为0
result = weekly_df.reindex(full_index, fill_value=0).reset_index()

这种方式无需嵌套groupby+apply,直接通过构建全量索引实现补全,效率更高,且能得到完全符合需求的结果。


内容的提问来源于stack exchange,提问作者FirefoxMetzger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:45:34