Панды Python Подсчитывают уникальные значения столбцов на основе другого столбца

#python #pandas

Вопрос:

У меня есть таблица (пример ниже)

     kick_result kick_yards  kicker
50     MADE       28.0       X1
64     MADE       30.0       X2
75     MADE       27.0       X2
158    MADE       32.0       X2
259    MISS       46.0       X3
 

Для каждого значения кикера —

Я хочу подсчитать, сколько полевых целей было сделано и пропущено (в %)

Для каждого значения кикера —

Я хочу найти количество забитых и пропущенных полевых мячей на каждом расстоянии ярдов

Ответ №1:

По вашему требованию состоит из 2 частей:

  1. цели на местах %, и
  2. полевые цели во дворах,

давайте решим это один за другим.

Часть 1. Цели на местах %

Мы можем использовать df.groupby() вместе с .value_counts(normalize=True) , чтобы получить его:

 (df.groupby('kicker')['kick_result']
   .value_counts(normalize=True).mul(100).round(2)
   .sort_index()
   .to_frame(name='Result_%')
).reset_index()
 

Тестовый запуск

Построение Тестовых Данных:

Чтобы провести полное тестирование различных требований, я добавил тестовые данные следующим образом:

     kick_result kick_yards  kicker
49     MADE       18.0       X1 
50     MADE       28.0       X1
51     MADE       38.0       X1
52     MISS       48.0       X1
53     MISS       58.0       X1
64     MADE       30.0       X2
75     MADE       27.0       X2
158    MADE       32.0       X2
159    MISS       32.0       X2
160    MISS       42.0       X2
259    MISS       46.0       X3
260    MISS       26.0       X3
261    MADE       56.0       X3
 

Запустите код:

 (df.groupby('kicker')['kick_result']
   .value_counts(normalize=True).mul(100).round(2)
   .sort_index()
   .to_frame(name='Result_%')
).reset_index()
 

Результат:

   kicker kick_result  Result_%
0     X1        MADE     60.00
1     X1        MISS     40.00
2     X2        MADE     60.00
3     X2        MISS     40.00
4     X3        MADE     33.33
5     X3        MISS     66.67
 

Часть 2: Полевые цели во дворах

Мы можем использовать pd.crosstab() вместе с pd.cut() для построения таблицы с диапазонами ярдов.

Также включено общее количество попыток для всех диапазонов.

 pd.crosstab(index=[df['kicker'], pd.cut(df['kick_yards'],[0, 20, 30, 40, 50, np.inf])], 
            columns=df['kick_result'], 
            margins=True, margins_name='Total_Attempts')
 

Результат (с использованием обогащенных тестовых данных):

                         kick_result  MADE  MISS  Total_Attempts
        kicker           kick_yards                              
            X1          (0.0, 20.0]     1     0               1
                       (20.0, 30.0]     1     0               1
                       (30.0, 40.0]     1     0               1
                       (40.0, 50.0]     0     1               1
                        (50.0, inf]     0     1               1
            X2         (20.0, 30.0]     2     0               2
                       (30.0, 40.0]     1     1               2
                       (40.0, 50.0]     0     1               1
            X3         (20.0, 30.0]     0     1               1
                       (40.0, 50.0]     0     1               1
                        (50.0, inf]     1     0               1
Total_Attempts                          7     6              13
 

Ответ №2:

Давайте свяжемся цепью с cut и crosstab

 out = pd.crosstab([df.kicker,pd.cut(df.kick_yards,[20,30,40,50,np.Inf],include_lowest=True)]
                   ,df.kick_result,normalize='index')
 
out
Out[228]: 
kick_result            MADE  MISS
kicker kick_yards                
X1     (19.999, 30.0]   1.0   0.0
X2     (19.999, 30.0]   1.0   0.0
       (30.0, 40.0]     1.0   0.0
X3     (40.0, 50.0]     0.0   1.0
 

Комментарии:

1. Это работает, но мне это нужно для каждого значения кикера — так что это даст мне только итоги.

2. есть ли также способ добавить общее количество попыток в этот кадр?

3. @Gamecocks20 проверьте поля в перекрестной таблице pandas.pydata.org/docs/reference/api/pandas.crosstab.html

4. @BENY Не должен ли X2 иметь 3, а не 2?

5. @BENY — Я ищу, но продолжаю получать ошибки при попытке реализовать поля. Я просто хочу добавить соответствующее количество (попыток) для каждой категории в группе

Ответ №3:

Используйте рычаги get_dummies cut и создайте результирующую DataFrame :

 df['Att'] = 1

dfmm = pd.get_dummies(df['kick_result'])

cols_A = ['A20','A21-30','A31-40','A41-50','A51 ']
cols_M = [x.replace('A','M') for x in cols_A]

df_att = pd.DataFrame(pd.get_dummies(pd.cut(df.kick_yards,[0,20,30,40,50,np.Inf],include_lowest=True)))
df_att.columns = df_att.columns.to_list()
df_att.columns = cols_A

df_made = df_att.multiply(dfmm['MADE'], axis=0)
df_made.columns=cols_M
    
dff = pd.concat([df,dfmm,df_att,df_made], axis=1).drop(['kick_result','kick_yards'], axis=1)
 

Результирующий кадр данных:

   kicker  Att  MADE  MISS  A20  A21-30  A31-40  A41-50  A51   M20  M21-30  
0     X1    1     1     0    0       1       0       0     0    0       1   
1     X2    1     1     0    0       1       0       0     0    0       1   
2     X2    1     1     0    0       1       0       0     0    0       1   
3     X2    1     1     0    0       0       1       0     0    0       0   
4     X3    1     0     1    0       0       0       1     0    0       0   

   M31-40  M41-50  M51   
0       0       0     0  
1       0       0     0  
2       0       0     0  
3       1       0     0  
4       0       0     0  
 

Агрегация из этого фрейма данных:

 dff.groupby('kicker').agg(['sum'])

       Att MADE MISS A20 A21-30 A31-40 A41-50 A51  M20 M21-30 M31-40 M41-50  
       sum  sum  sum sum    sum    sum    sum  sum sum    sum    sum    sum   
kicker                                                                        
X1       1    1    0   0      1      0      0    0   0      1      0      0   
X2       3    3    0   0      2      1      0    0   0      2      1      0   
X3       1    0    1   0      0      0      1    0   0      0      0      0   

       M51   
        sum  
kicker       
X1        0  
X2        0  
X3        0