【问题标题】:Pandas: How to create subindex efficiently?Pandas:如何有效地创建子索引?
【发布时间】:2025-12-01 17:05:02
【问题描述】:

我想根据索引为我的数据框创建一个子索引。例如,我有一个这样的数据框:

      Content        Date
ID                       
Bob  birthday  2010.03.01
Bob    school  2010.04.01
Tom  shopping  2010.02.01
Tom      work  2010.09.01
Tom   holiday  2010.10.01

我想为我的ID 创建一个子索引,生成的数据框如下所示:

               Content        Date
ID  subindex                      
Bob 1         birthday  2010.03.01
    2           school  2010.04.01
Tom 1         shopping  2010.02.01
    2             work  2010.09.01
    3          holiday  2010.10.01

为此,我需要首先创建我的subindex 列表。我在帮助文档中搜索,似乎最简洁的方法是使用transform

subindex = df['Date'].groupby(df.index).transform(lambda x: np.arange(1, len(x) + 1))

但是,它真的很慢。我环顾四周,发现apply 也可以完成这项工作:

subindex = df['Date'].groupby(df.index).apply(lambda x: np.arange(1, len(x) + 1))

当然,subindex 需要展平,因为它是这里的列表列表。这比transform 方法工作得快得多。然后我用自己的for loop 进行了测试:

subindex_size = df.groupby(df.index, sort = False).size()
subindex = []
for i in np.arange(len(subindex_size)):
    subindex.extend(np.arange(1,subindex_size[i]+1))

它甚至更快。对于我更大的数据集(大约 90k 行),transform 方法在我的计算机上大约需要 44 秒,apply 需要大约 2 秒,for loop 只需要大约 1 秒。我需要处理更大的数据集,所以即使applyfor loop 之间的时间差异对我来说也会有所不同。但是,for loop 看起来很难看,如果我需要创建其他基于组的变量,可能不容易应用。

所以我的问题是,为什么应该做正确的事情的内置函数更慢?我在这里遗漏了什么还是有原因的?有没有其他方法可以改进这个过程?

【问题讨论】:

    标签: python performance pandas


    【解决方案1】:

    您可以使用cumcount 来执行此操作:

    In [11]: df.groupby(level=0).cumcount()
    Out[11]: 
    ID
    Bob    0
    Bob    1
    Tom    0
    Tom    1
    Tom    2
    dtype: int64
    
    In [12]: df['subindex'] = df.groupby(level=0).cumcount()  # possibly + 1 here.
    
    In [13]: df.set_index('subindex', append=True)
    Out[13]: 
                   Content        Date
    ID  subindex                      
    Bob 0         birthday  2010.03.01
        1           school  2010.04.01
    Tom 0         shopping  2010.02.01
        1             work  2010.09.01
        2          holiday  2010.10.01
    

    要从 1(而不是 0)开始,只需在 cumcount 的结果中加 1。

    【讨论】:

    • 我认为应该有更好的 API 来将级别附加到 MultiIndex...不认为有(还)。
    • 谢谢! cumcount 效果很好!但是set_index 似乎对我不起作用。我从字面上复制了你的行 1213set_index 被程序简单地忽略了,这很奇怪。我必须使用 df.index 来重置索引。但是cumcount 的工作速度比上述任何一种方法都快!
    • @ZhenSun 你需要做:df = df.set_index('subindex', append=True),或者df.set_index('subindex', append=True, inplace=True)才能真正改变df!
    • 谢谢!抱歉这个愚蠢的问题!
    • @ZhenSun 这是个好问题,比 atm 更棘手。如果有帮助,您可以“接受这个答案”:meta.stackexchange.com/questions/5234/…