如何在 Pandas 数据框中选择每个重复集合中倒数第二个元素的最有效方法?
例如,我想执行以下操作:
df = df.drop_duplicates(['Person','Question'],take_last=True)
但这个:
df = df.drop_duplicates(['Person','Question'],take_second_last=True)
提炼的问题:如果重复项既不是最大值也不是最小值,如何选择要保留哪个重复项?
如何在 Pandas 数据框中选择每个重复集合中倒数第二个元素的最有效方法?
例如,我想执行以下操作:
df = df.drop_duplicates(['Person','Question'],take_last=True)
但这个:
df = df.drop_duplicates(['Person','Question'],take_second_last=True)
提炼的问题:如果重复项既不是最大值也不是最小值,如何选择要保留哪个重复项?
使用groupby.apply:
df = pd.DataFrame({'A': [1, 1, 1, 1, 2, 2, 2, 3, 3, 4],
'B': np.arange(10), 'C': np.arange(10)})
df
Out:
A B C
0 1 0 0
1 1 1 1
2 1 2 2
3 1 3 3
4 2 4 4
5 2 5 5
6 2 6 6
7 3 7 7
8 3 8 8
9 4 9 9
(df.groupby('A', as_index=False).apply(lambda x: x if len(x)==1 else x.iloc[[-2]])
.reset_index(level=0, drop=True))
Out:
A B C
2 1 2 2
5 2 5 5
7 3 7 7
9 4 9 9
使用不同的 DataFrame,筛选出两列:
df = pd.DataFrame({'A': [1, 1, 1, 1, 2, 2, 2, 3, 3, 4],
'B': [1, 1, 2, 1, 2, 2, 2, 3, 3, 4], 'C': np.arange(10)})
df
Out:
A B C
0 1 1 0
1 1 1 1
2 1 2 2
3 1 1 3
4 2 2 4
5 2 2 5
6 2 2 6
7 3 3 7
8 3 3 8
9 4 4 9
(df.groupby(['A', 'B'], as_index=False).apply(lambda x: x if len(x)==1 else x.iloc[[-2]])
.reset_index(level=0, drop=True))
Out:
A B C
1 1 1 1
2 1 2 2
5 2 2 5
7 3 3 7
9 4 4 9
groupby/tail(2)
来获取最后两个元素,然后使用 groupby/head(1)
从最后两个元素中获取第一个元素:df.groupby(['A','B']).tail(2).groupby(['A','B']).head(1)
tail(2)
仅返回该项。
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(10, size=(10**2, 3)), columns=list('ABC'))
result = df.groupby(['A','B']).tail(2).groupby(['A','B']).head(1)
expected = (df.groupby(['A', 'B'], as_index=False).apply(lambda x: x if len(x)==1 else x.iloc[[-2]]).reset_index(level=0, drop=True))
assert expected.sort_index().equals(result)
tail
和head
)通常比使用自定义Python函数的groupby/apply
要快得多。如果有很多组,尤其如此:In [96]: %timeit df.groupby(['A','B']).tail(2).groupby(['A','B']).head(1)
1000 loops, best of 3: 1.7 ms per loop
In [97]: %timeit (df.groupby(['A', 'B'], as_index=False).apply(lambda x: x if len(x)==1 else x.iloc[[-2]]).reset_index(level=0, drop=True))
100 loops, best of 3: 17.9 ms per loop
另外, Ayhan建议一项不错的改进:
alt = df.groupby(['A','B']).tail(2).drop_duplicates(['A','B'])
assert expected.sort_index().equals(alt)
In [99]: %timeit df.groupby(['A','B']).tail(2).drop_duplicates(['A','B'])
1000 loops, best of 3: 1.43 ms per loop
.drop_duplicates(['A', 'B'])
比 .groupby(['A','B']).head(1)
稍微快一些。 - ayhan