Присвоение значения значениям фрейма данных pandas для уникальных значений в другом столбце

#python #python-3.x #pandas

Вопрос:

У меня есть следующий фрейм данных:

 df = pd.DataFrame({"marks": [40, 60, 90, 20, 100, 10, 30, 70 ], "students": 
                   ["Jack", "Jack", "Jack", "Jack", "John", "John", "John", "John"]}
        )

   marks  students
0  40      Jack
1  60      Jack
2  90      Jack
3  20      Jack
4  100     John
5  10      John
6  30      John
7  70      John
 

Я пытаюсь присвоить среднюю оценку студента его отметкам ниже 40 (среднее значение будет включать самую низкую отметку).

Я знаю о назначении отметки на основе < 40 условия (в этом случае я присвоил самую низкую отметку df всем отметкам ниже 40), примерно так:

 df.loc[df["marks"] < 40, "marks"] = df["marks"].min()
 

Но я не понимаю, как потенциально применить лямбда-функцию к уникальным student именам. Любая помощь будет признательна.

Ответ №1:

Попробуйте с np.where

 df['marks'] = np.where(df['marks'] <40, 
                       df.groupby('students')['marks'].transform('mean'), 
                       df['marks'])
    
df
Out[18]: 
   marks students
0   40.0     Jack
1   60.0     Jack
2   90.0     Jack
3   52.5     Jack
4  100.0     John
5   52.5     John
6   52.5     John
7   70.0     John
 

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

1. разве это не более или менее то же самое, что и мой ответ? 😉

2. @mozway , если ты так думаешь, я могу удалить

3. Нет, для меня это не имеет значения, я просто немного подумал, не упустил ли я какой-нибудь хитрый трюк 😉

Ответ №2:

Вы можете объединить a groupby и where :

 df['corrected_marks'] = df['marks'].where(df['marks']>=40,
                                          (df.groupby('students')
                                             ['marks']
                                             .transform('mean'))
                                          )
 

выход:

    marks students  corrected_marks
0     40     Jack             40.0
1     60     Jack             60.0
2     90     Jack             90.0
3     20     Jack             52.5
4    100     John            100.0
5     10     John             52.5
6     30     John             52.5
7     70     John             70.0
 

Ответ №3:

@mozway ответ правильный. Вы могли бы сделать это за два шага:

 df['mean'] = df.groupby('students')['marks'].transform('mean')

df['final_marks'] = df.apply(lambda x: x['mean'] if (x['marks'] < 40) else x['marks'], axis=1)

print(df)
 

выход:

    marks students  mean  final_marks
0     40     Jack  52.5         40.0
1     60     Jack  52.5         60.0
2     90     Jack  52.5         90.0
3     20     Jack  52.5         52.5
4    100     John  52.5        100.0
5     10     John  52.5         52.5
6     30     John  52.5         52.5
7     70     John  52.5         70.0
 

Ответ №4:

Чтобы применить свою логику к уникальным student именам, вы можете сгруппировать их по student именам .groupby() и получить среднее значение по каждому студенту (каждой группе) transform() 'mean' . Затем вы можете присвоить средние значения marks , используя тот же механизм в коде, который вы пробовали, как показано ниже:

 df.loc[df["marks"] < 40, "marks"] = df.groupby('students')['marks'].transform('mean')
 

Результат:

 print(df)

   marks students
0   40.0     Jack
1   60.0     Jack
2   90.0     Jack
3   52.5     Jack
4  100.0     John
5   52.5     John
6   52.5     John
7   70.0     John
 

Если вы действительно хотите присвоить самую низкую отметку (вместо «средней» отметки) каждой student из них всем отметкам ниже 40 для этого учащегося, вы можете использовать transform() 'min' вместо этого:

 df.loc[df["marks"] < 40, "marks"] = df.groupby('students')['marks'].transform('min')
 

Результат:

 print(df)

   marks students
0     40     Jack
1     60     Jack
2     90     Jack
3     20     Jack
4    100     John
5     10     John
6     10     John
7     70     John
 

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

1. Спасибо вам за дополнительную информацию @SeaBean.