在pandas DataFrame中,查找每行跨多列的众数

4

我刚开始学Python和Pandas。我想要找到每行从Opt_1到Opt_7列中最常出现的项。需要注意的是,由于某些原因,有些空单元格看起来像NaN,在其他情况下则为None。

ID  Col_1   Col_2   Opt_1   Opt_2   Opt_3   Opt_4   Opt_5   Opt_6   Opt_7 
1   Game 1  Team 1  13                       
2   Game 1  Team 2  -13                      
3   Game 1  Team 1                           
4   Game 1  Team 2                           
5   Game 2  Team 1  -7.5    -7.5    -7.5    -7.5             
6   Game 2  Team 2  7.5     7.5     7.5     7.5          
7   Game 2  Team 1          -2.5    -1.5             
8   Game 2  Team 2          2.5     1.5          
9   Game 3  Team 1          3.5     3.5          
10  Game 3  Team 2          -3.5    -3.5             
11  Game 3  Team 1  -1      -1.5    -1       
12  Game 3  Team 2  1       1.5     1

我已尝试下列代码,对于大多数行都能如预期般运作,但不是全部行,而且速度有些慢。
def freq_value(series):
    return Counter(series).most_common()[0][0]

for row in df.iterrows():
     df['result'] = df.apply(lambda row: freq_value((row['Opt_1'], row['Opt_2'], row['Opt_3'], row['Opt_4'], row['Opt_5'], row['Opt_6'], row['Opt_7'])), axis=1)

以下是预期结果和实际结果:
ID  Expected    Actual Result
1   NaN         NaN
2   NaN         NaN
3   NaN         NaN
4   NaN         NaN
5   -7.5            -7.5
6   7.5         7.5
7   NaN         NaN
8   NaN         NaN
9   3.5         3.5
10  -3.5           -3.5
11  -1          NaN
12  1           NaN

有没有什么方法可以做到百分之百正确,并且不需要逐行迭代来完成呢?感谢您提前提供的任何建议。

哪些位置是None,哪些位置是NaN? - Kyle
2个回答

0
使用pandas.DataFrame.mode
>>> import numpy as np
>>> import pandas as pd
>>> df = pd.DataFrame({
...     'ID': range(1, 13),
...     'Col_1': [*(['Game 1'] * 4), *(['Game 2'] * 4), *(['Game 3'] * 4)],
...     'Col_2': ['Team 1', 'Team 2'] * 6,
...     'Opt_1': [13, -13, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, -1, 1],
...     'Opt_2': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -2.5, 2.5, 3.5, -3.5, -1.5, 1.5],
...     'Opt_3': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, -1.5, 1.5, 3.5, -3.5, -1, 1],
...     'Opt_4': [np.nan, np.nan, np.nan, np.nan, -7.5, 7.5, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
...     'Opt_5': [np.nan] * 12,
...     'Opt_6': [np.nan] * 12,
...     'Opt_7': [np.nan] * 12
... })
>>> df
    ID   Col_1   Col_2  Opt_1  Opt_2  Opt_3  Opt_4  Opt_5  Opt_6  Opt_7
0    1  Game 1  Team 1   13.0    NaN    NaN    NaN    NaN    NaN    NaN
1    2  Game 1  Team 2  -13.0    NaN    NaN    NaN    NaN    NaN    NaN
2    3  Game 1  Team 1    NaN    NaN    NaN    NaN    NaN    NaN    NaN
3    4  Game 1  Team 2    NaN    NaN    NaN    NaN    NaN    NaN    NaN
4    5  Game 2  Team 1   -7.5   -7.5   -7.5   -7.5    NaN    NaN    NaN
5    6  Game 2  Team 2    7.5    7.5    7.5    7.5    NaN    NaN    NaN
6    7  Game 2  Team 1    NaN   -2.5   -1.5    NaN    NaN    NaN    NaN
7    8  Game 2  Team 2    NaN    2.5    1.5    NaN    NaN    NaN    NaN
8    9  Game 3  Team 1    NaN    3.5    3.5    NaN    NaN    NaN    NaN
9   10  Game 3  Team 2    NaN   -3.5   -3.5    NaN    NaN    NaN    NaN
10  11  Game 3  Team 1   -1.0   -1.5   -1.0    NaN    NaN    NaN    NaN
11  12  Game 3  Team 2    1.0    1.5    1.0    NaN    NaN    NaN    NaN
>>> opts = ['Opt_{}'.format(i) for i in range(1, 8)]
>>> df[opts].mode(axis=1, dropna=False)
      0
0   NaN
1   NaN
2   NaN
3   NaN
4  -7.5
5   7.5
6   NaN
7   NaN
8   NaN
9   NaN
10  NaN
11  NaN

感谢 @Kyle Roth 的回复。我喜欢你如何组合数据框,特别是如何组合第一列和第二列。对于你的解决方案,我有点惊讶,因为对于ID 9:12,它没有返回3.5、-3.5、-1.0和1.0作为众数。顺便说一下,我不记得如何发现一些空白处有NaN,而其他地方则是None。 - dirkthepieman

0
使用filter选择列,并使用mode+mask仅找到唯一的模式。
(df.filter(like='Opt')
   .mode(axis=1)
   .set_axis(['a', 'b'], axis=1, inplace=False)
   .eval('a.mask(b.notna())', engine='python'))

0     13.0
1    -13.0
2      NaN
3      NaN
4     -7.5
5      7.5
6      NaN
7      NaN
8      3.5
9     -3.5
10    -1.0
11     1.0
Name: a, dtype: float64

mode会返回给定行的所有模式。这意味着如果有两个值出现的频率相同,输出中将会有两列。上面的解决方案处理了最多有两列的情况。

如果模式是唯一的,则可以简化解决方案为

df.filter(like='Opt').mode(axis=1).iloc[:, 0]

当输出中没有唯一模式时,另一个解决方案是将其推广到任意数量的列。

u = df.filter(like='Opt').mode(axis=1)
if len(u.columns) > 1:
    u = u.iloc[:, 0].where(u.iloc[:, 1:].isna().all(axis=1))

u
0     13.0
1    -13.0
2      NaN
3      NaN
4     -7.5
5      7.5
6      NaN
7      NaN
8      3.5
9     -3.5
10    -1.0
11     1.0
Name: 0, dtype: float64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接