Фрейм данных Pandas — итерация и назначение

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

У меня есть набор данных, как показано ниже, я хочу назначить нового ученика, если коэффициент оценки <= 0,05

 import pandas as pd
df = pd.DataFrame({'Teacher': ['P','P','N','N','N','N','P','N','N','P','P','N'],
    'Class': ['A','A','A','A','B','B','B','C','C','C','C','C'],
                   'Student': [1,2,3,4,1,2,3,1,2,3,4,5],
                   'Total Score': [75,10,10,5,75,20,5,60,20,10,6,4],
                  'Percent': [43,32,30,36,35,28,34,33,31,36,37,29]})
 

построен столбец коэффициента оценки, как показано ниже

 df_2 = df.groupby(['Teacher','Class']).agg({'Total Score': 'sum'}).reset_index()
final_data=pd.merge(df,df_2, on=['Teacher','Class'], how='inner')
final_data['score ratio']=final_data['Total Score_x']/final_data['Total Score_y']
 

Если коэффициент оценки учащихся <= 0,05, то мне нужно назначить нового ученика для того же учителя (например: N) в том же классе (например: C), процент которого следующий лучший (приведенный ниже пример ученика 2 имеет следующий лучший процент 31)
введите описание изображения здесь

Ожидаемый результат с новым столбцом — ‘new_assigned_student’

введите описание изображения здесь

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

1. Не совсем ясно, чего вы хотите достичь. Можете ли вы, пожалуйста, опубликовать ожидаемый результат?

2. Извините за это, я добавил ожидаемый результат

Ответ №1:

Вот решение с вложенными iterrows, которое работает, но неэффективно. Мне было бы интересно посмотреть, предложит ли кто-нибудь более эффективное векторизованное решение:

 for idx,row in final_data.iterrows():
    if row['score ratio'] < 0.05:
        min_distance = math.inf
        target_index = -1
        for idx2, row2 in final_data.iterrows():
            if row2['Teacher'] == row['Teacher'] and
                    row2['Class'] == row['Class'] and
                    row2['Percent'] > row['Percent'] and
                    row2['Percent'] - row['Percent'] < min_distance:
                min_distance = row2['Percent'] - row['Percent']
                target_index = idx2
        final_data.loc[idx,'new_assigned-student'] = final_data.loc[target_index,'Student'].astype(str)

#output:
   Teacher Class  Student  ...  Total Score_y  score ratio  new_assigned-student
0        P     A        1  ...             85     0.882353                   NaN
1        P     A        2  ...             85     0.117647                   NaN
2        N     A        3  ...             15     0.666667                   NaN
3        N     A        4  ...             15     0.333333                   NaN
4        N     B        1  ...             95     0.789474                   NaN
5        N     B        2  ...             95     0.210526                   NaN
6        P     B        3  ...              5     1.000000                   NaN
7        N     C        1  ...             84     0.714286                   NaN
8        N     C        2  ...             84     0.238095                   NaN
9        N     C        5  ...             84     0.047619                   2
10       P     C        3  ...             16     0.625000                   NaN
11       P     C        4  ...             16     0.375000                   NaN
 

Ответ №2:

Это должно сработать, просто используйте a shift . предполагается, что ваши оценки отсортированы по учителю / классу, как в вашем примере

 final_data['new_assigned_student'] = final_data.groupby(['Teacher','Class'])['Student'].shift()
final_data.loc[final_data['score ratio']>0.05,'new_assigned_student'] = np.nan
 

результат

     Teacher    Class      Student    Total Score_x    Percent    Total Score_y    score ratio    new_assigned_student
--  ---------  -------  ---------  ---------------  ---------  ---------------  -------------  ----------------------
 0  P          A                1               75         43               85       0.882353                     nan
 1  P          A                2               10         32               85       0.117647                     nan
 2  N          A                3               10         30               15       0.666667                     nan
 3  N          A                4                5         36               15       0.333333                     nan
 4  N          B                1               75         35               95       0.789474                     nan
 5  N          B                2               20         28               95       0.210526                     nan
 6  P          B                3                5         34                5       1                            nan
 7  N          C                1               60         33               84       0.714286                     nan
 8  N          C                2               20         31               84       0.238095                     nan
 9  N          C                5                4         29               84       0.047619                       2
10  P          C                3               10         36               16       0.625                        nan
11  P          C                4                6         37               16       0.375                        nan
 

Решение 2

Вот более надежное, хотя и несколько более сложное решение

 df3 = final_data
df_min_pct = (df3.groupby(['Teacher','Class'], 
                          as_index = False, 
                          sort = False)
                 .apply(lambda g: g.iloc[g.loc[g['score ratio']>0.05,'Percent'].argmin()])
)
 

Здесь df_min_pct показаны для каждой группы учителей/ классов сведения об ученике в этой группе, набравшем наименьший балл выше 0,05:

     Teacher    Class      Student    Total Score_x    Percent    Total Score_y    score ratio
--  ---------  -------  ---------  ---------------  ---------  ---------------  -------------
 0  P          A                2               10         32               85       0.117647
 1  N          A                3               10         30               15       0.666667
 2  N          B                2               20         28               95       0.210526
 3  P          B                3                5         34                5       1
 4  N          C                2               20         31               84       0.238095
 5  P          C                3               10         36               16       0.625
 

Теперь мы объединяемся с исходным df и удаляем детали из тех строк, где это не имеет значения

 df4 = df3.merge(df_min_pct[['Teacher', 'Class','Student']], on = ['Teacher', 'Class'], sort = False).rename(columns = {'Student_y':'new_assigned_student'})
df4.loc[df4['score ratio']>0.05,'new_assigned_student'] = np.nan
 

Это дает желаемый результат

     Teacher    Class      Student_x    Total Score_x    Percent    Total Score_y    score ratio    new_assigned_student
--  ---------  -------  -----------  ---------------  ---------  ---------------  -------------  ----------------------
 0  P          A                  1               75         43               85       0.882353                     nan
 1  P          A                  2               10         32               85       0.117647                     nan
 2  N          A                  3               10         30               15       0.666667                     nan
 3  N          A                  4                5         36               15       0.333333                     nan
 4  N          B                  1               75         35               95       0.789474                     nan
 5  N          B                  2               20         28               95       0.210526                     nan
 6  P          B                  3                5         34                5       1                            nan
 7  N          C                  1               60         33               84       0.714286                     nan
 8  N          C                  2               20         31               84       0.238095                     nan
 9  N          C                  5                4         29               84       0.047619                       2
10  P          C                  3               10         36               16       0.625                        nan
11  P          C                  4                6         37               16       0.375                        nan