在pandas DataFrame中为每个组选择1个True,1个False

时间:2018-06-26 15:46:44

标签: python pandas

我有一个有约100万行和约10万个唯一事件的DataFrame。有1列Won,其中每个事件的每一行都设置为True,事件中的每一行都设置为False。

Event ID  Runner ID  Won
 E1        R1        True
 E1        R2        False
 E1        R3        False
 E2        R4        True
 E2        R5        False
 E2        R6        False

最后我想得到一个平衡的DataFrame,每个组只有1个获胜者,只有1个非获胜者。

Event ID  Runner ID  Won
 E1        R1        True
 E1        R3        False
 E2        R4        True
 E2        R5        False

我不在乎每个事件选择哪个非赢家,只要有1个赢家,一个非赢家即可。

对于大熊猫,我已经尝试了一些方法,选择了获胜者和未获胜者,

_won = df.Won
winners = df[_won]

non_winners = df[~_won]

但是我所见过的每个种族试图选择1个非获胜者的每个过程都非常缓慢-每个事件只需几秒钟(如果您有10万个事件,这是IMO的不合理)。

groupapply一起拍摄,

new_df = winners.append(
    non_winners
    .groupby('Event ID')
    .apply(lambda grp: grp.sample(1))

遍历groupby,

for event_id, grp in non_winners.groupby('Event ID'):
    winners.append(grp.sample(1))

遍历获胜者的活动ID,

event_ids = set(winners['Event ID'].drop_duplicates())
for event_id in event_ids:
    winners.append(
        non_winners[non_winners['Event ID'] == event_id].sample(1))

但是在处理约100万个事件和约10万个事件时,每个选项似乎都非常慢。

2 个答案:

答案 0 :(得分:5)

使用groupbyhead

df.groupby(['Event ID', 'Won']).head(1)

  Event ID Runner ID    Won
0       E1        R1   True
1       E1        R2  False
3       E2        R4   True
4       E2        R5  False

只要您对输出保持平衡,就不会对输出中保留的内容保持谨慎。


还有drop_duplicates

df.drop_duplicates(subset=['Event ID', 'Won'], keep='last') 
# or keep='first', it doesn't matter

  Event ID Runner ID    Won
0       E1        R1   True
2       E1        R3  False
3       E2        R4   True
5       E2        R6  False

最后,如果您想实施改组,请事先致电sample

(df.sample(frac=1)
   .sort_values(by=['Event ID'])
   .drop_duplicates(['Event ID', 'Won'])
)

  Event ID Runner ID    Won
2       E1        R3  False
0       E1        R1   True
4       E2        R5  False
3       E2        R4   True

答案 1 :(得分:0)

我想出了一个让我对速度足够满意的选择,就是将混洗和重复复制到非赢家的行上,

_won = df.Won
winners = df[_won]
_non_winners = shuffle(df[~_won])

non_winners = _non_winners[~_non_winners.duplicated('Event ID')]

new_df = winners.append(non_winners)

在其他选项中,每个组花费大约一秒钟的时间,这对于它在整个数量的组中运行是不可持续的,而据我所知,此解决方案给出的结果完全相同,但是大约10秒,相比之下我不知道要多久。

如果您希望每个分组多于1个,这很奇怪,但可能的话,您只需再次进行基本相同的操作即可,

...
_non_winners = shuffle(df[~_won])

dupeys = _non_winners.duplicated('Event ID')
new_df = pandas.concat([
    winners,
    _non_winners[~dupeys]])

_non_winners = _non_winners[dupeys]
new_df = pandas.concat([
    new_df,
    _non_winners[~_non_winners.duplicated('race_event_id')]])