pandas:使用MultiIndex级别的子集而不是列来标记重复行

7tofc5zh  于 2023-05-27  发布在  其他
关注(0)|答案(2)|浏览(150)

我有一个df和一个多级MultiIndex。早期我需要标记某些行来保存;在随后的分类和处理中,这些行将总是被保留。
我有工作代码,但它不是很有吸引力,我想知道是否有一个更漂亮/更有效的方法来做到这一点。
给定一个df,其中MultiIndex的级别为3+,列数为任意数量,我运行以下代码来检查MultiIndex的前2个级别中是否存在重复项,并将第一个出现的项标记为keeper:

df['keeper'] = df.index.isin(df.assign(check=df.index.get_level_values(0), check2=df.index.get_level_values(1)).drop_duplicates(subset=['check', 'check2']).index)

下面是一个玩具df,其结果为keeper列:

0  keeper
lev0 lev1 lev2                  
1    1    1     0.696469    True
          2          NaN   False
     2    3     0.719469    True
          2     0.980764   False
     3    1          NaN    True

我尝试了reset_index,但最后我需要MultiIndex保持不变,并且将这些级别移动到列中,然后再次重新创建非常大的MultiIndex,这似乎比我的效率更低。

lf5gs5x2

lf5gs5x21#

这应该也可以。它的灵感来自于mozway在链接上的回答。

keep = ['lev0','lev1']

~df.index.droplevel(df.index.names.difference(keep)).duplicated()

OP编辑:

我在一个10级MultiIndex的1000x1000 df上对各种解决方案(以及我的原始代码)进行了计时,这个解决方案(使用droplevelnames.difference)是明显的赢家,超过了一个数量级。从最快到最慢:

%timeit ~df.index.droplevel(df.index.names.difference(['lev0', 'lev1'])).duplicated()
72.1 µs ± 248 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

%timeit ~df[[]].reset_index(level=['lev0', 'lev1']).duplicated().to_numpy()
816 µs ± 3.21 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

%timeit ~df.reset_index(level=['lev0', 'lev1']).duplicated(subset=['lev0', 'lev1']).to_numpy()
12.3 ms ± 38.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit ~df.assign(check=df.index.get_level_values(0), check2=df.index.get_level_values(1)).duplicated(subset=['check', 'check2'])
12.7 ms ± 47.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.index.isin(df.assign(check=df.index.get_level_values('lev0'), check2=df.index.get_level_values('lev1')).drop_duplicates(subset=['check', 'check2']).index)
15.7 ms ± 59.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8aqjt8rx

8aqjt8rx2#

感谢@rhug123和the answer he posted,我改进了我的原始代码,使用duplicated代替drop_duplicates,否定了对.index.isin的需求。我选择不使用to_frame,因为我的MultiIndex是10级深,复制所有的级别只是为了使用2似乎效率低下。这两种解决方案都只提取所需的索引级别。
3种不同的选择,从最慢到最快:

df['keeper'] = ~df.assign(check=df.index.get_level_values(0), check2=df.index.get_level_values(1)).duplicated(subset=['check', 'check2'])
df['keeper2'] = ~df.reset_index(level=['lev0', 'lev1']).duplicated(subset=['lev0', 'lev1']).to_numpy()
df['keeper3'] = ~df[[]].reset_index(level=['lev0', 'lev1']).duplicated().to_numpy()

reset_index之后,需要to_numpy,因为索引不再与原始索引匹配。第三种解决方案首先删除所有使用df[[]]的列,因此不需要subset

相关问题