多索引Pandas数据框-如何添加“其他”列从其余行

wtzytmuj  于 2023-01-11  发布在  其他
关注(0)|答案(2)|浏览(130)

我有一个2级索引的数据框,我想只留下2个第二级的顶部行,并添加一行“其他”为其余行。这是我的数据框:

import pandas as pd

df = pd.DataFrame()
df["Idx1"] = ["A", "A", "A", "A", "B", "B", "B", "B"]
df["Idx2"] = ["X", "Y", "Z", "Q", "X", "Y", "Z", "Q"]
df["Values"] = [1,2,3,4, 1,2,3,4]
df = df.set_index(["Idx1", "Idx2"])
df

这就是我如何提取第一级的顶部2行:

res = df.groupby(level = 0).head(2)
res

但是,我在添加“其他”列时遇到问题。我希望的输出是:
enter image description here
如何用最优雅的方式来做呢?

b1payxdu

b1payxdu1#

我认为您可以通过计数器OtherGroupBy.cumcount转换MultiIndex的第二级,然后聚合sum

idx = df.index.get_level_values(1).where(df.groupby(level = 0).cumcount().lt(2), 'Other')

df = df.groupby(['Idx1',idx], sort=False).sum()
print (df)
            Values
Idx1 Idx2         
A    X           1
     Y           2
     Other       7
B    X           1
     Y           2
     Other       7

性能适用于10k行数据中的1k行:

np.random.seed(2023)

N = 10000
L = list('abcdefghijklmno')

df = pd.DataFrame({'Idx1': np.random.randint(1000, size=N),
                   'Idx2': np.random.choice(L, size=N),
                   'Values':np.random.randint(1000, size=N)}).set_index(["Idx1", "Idx2"]).sort_index()
print (df)

In [230]: %%timeit
     ...: idx = df.index.get_level_values(1).where(df.groupby(level = 0).cumcount().lt(2), 'Other')
     ...: 
     ...: df.groupby(['Idx1',idx], sort=False).sum()
     ...: 
5.62 ms ± 76.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

mozway的另一个解决方案要慢382倍:

In [231]: %%timeit
     ...: (df.groupby(level='Idx1')
     ...:    .apply(lambda d: pd.concat([d.droplevel(0).head(2),
     ...:                                d.droplevel(0).tail(-2).sum().to_frame(name='Other').T,
     ...:                                ]).rename_axis('Idx2'))
     ...: )
     ...: 
2.15 s ± 139 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
py49o6xq

py49o6xq2#

一个选项是使用groupby.apply获取前两个值,concathead,其余值为sum

(df.groupby(level='Idx1')
   .apply(lambda d: pd.concat([d.droplevel(0).head(2),
                               d.droplevel(0).tail(-2).sum().to_frame(name='Other').T,
                               ]).rename_axis('Idx2'))
)

输出:

Values
Idx1 Idx2         
A    X           1
     Y           2
     Other       7
B    X           1
     Y           2
     Other       7

相关问题