Найти режим по нескольким столбцам для каждой строки панды DataFrame - PullRequest
1 голос
/ 26 мая 2019

Я новичок в 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 голосов
/ 26 мая 2019

Используйте 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
0 голосов
/ 26 мая 2019

Использование 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
...