在熊猫DataFrame的每一行中跨多列查找模式

时间:2019-05-26 02:14:37

标签: python pandas dataframe mode

我是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

是否有这样做的方法,所以它是100%正确的,并且可能不需要一次又一次遍历每一行吗?预先感谢您的任何建议。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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将为给定的行返回 all 模式。这意味着,如果有两个频率相等的值,则输出中将有两列。上面的解决方案可以处理最多两列的情况。

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

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