Получить имена столбцов Top 'n' на основе порога для значений в строке - PullRequest
0 голосов
/ 04 марта 2020

Допустим, у меня есть следующие данные:

In [1]: df
Out[1]:
        Student_Name    Maths    Physics    Chemistry    Biology    English
0       John Doe        90       87         81           65         70
1       Jane Doe        82       84         75           73         77
2       Mary Lim        40       65         55           60         70
3       Lisa Ray        55       52         77           62         90

Я хочу добавить столбец к этому фрейму данных, в котором будут указаны «лучшие» n предметов учащихся, которые находятся выше порога, где имена субъектов доступны в именах столбцов. Давайте предположим, n=3 и threshold=80. Вывод будет выглядеть следующим образом:

In [3]: df
Out[3]:
        Student_Name    Maths    Physics    Chemistry    Biology    English  Top_3_above_80
0       John Doe        90       87         81           65         70       Maths, Physics, Chemistry
1       Jane Doe        82       84         75           73         77       Physics, Maths
2       Mary Lim        40       65         55           60         70       nan
3       Lisa Ray        55       52         77           62         90       English

Я пытался использовать решение, написанное @jezrael для этого вопроса, где они используют numpy .argsort чтобы получить позиции отсортированных значений для верхних 'n' столбцов, но я не могу установить пороговое значение, ниже которого ничего не следует рассматривать.

Ответы [ 2 ]

1 голос
/ 04 марта 2020

Альтернатива:

res = []
tmp = df.set_index('Student_Name').T
for col in list(tmp):
    res.append(tmp[col].nlargest(3)[tmp[col].nlargest(3) > 80].index.tolist())
res = [x if len(x) > 0 else np.NaN for x in res]
df['Top_3_above_80'] = res

Выход:

    Student_Name    Maths   Physics     Chemistry   Biology     English     Top_3_above_80
0   JohnDoe     90  87  81  65  70  [Maths, Physics, Chemistry]
1   JaneDoe     82  84  75  73  77  [Physics, Maths]
2   MaryLim     40  65  55  60  70  NaN
3   LisaRay     55  52  77  62  90  [English]
1 голос
/ 04 марта 2020

Идея состоит в том, чтобы сначала заменить несоответствующие значения отсутствующими значениями в DataFrame.where, а затем применить раствор на numpy.argsort. Фильтрация по количеству True s для подсчета не пропущенных значений в numpy.where для замены несоответствующих значений на пустые строки.

Последними являются значения, объединенные в понимании списка и отфильтрованные без совпавшие строки для пропущенных значений:

df1 = df.iloc[:, 1:]

m = df1 > 80
count = m.sum(axis=1)
arr = df1.columns.values[np.argsort(-df1.where(m), axis=1)]

m = np.arange(arr.shape[1]) < count[:, None]
a = np.where(m, arr, '')

L = [', '.join(x).strip(', ') for x in a]
df['Top_3_above_80'] = pd.Series(L, index=df.index)[count > 0]
print (df)
  Student_Name  Maths  Physics  Chemistry  Biology  English  \
0     John Doe     90       87         81       65       70   
1     Jane Doe     82       84         75       73       77   
2     Mary Lim     40       65         55       60       70   
3     Lisa Ray     55       52         77       62       90   

              Top_3_above_80  
0  Maths, Physics, Chemistry  
1             Physics, Maths  
2                        NaN  
3                    English  

Если производительность не важна, используйте Series.nlargest для строк, но это очень медленно, если большой DataFrame:

df1 = df.iloc[:, 1:]
m = df1 > 80
count = m.sum(axis=1)

df['Top_3_above_80'] = (df1.where(m)
                           .apply(lambda x: ', '.join(x.nlargest(3).index), axis=1)[count > 0])
print (df)
  Student_Name  Maths  Physics  Chemistry  Biology  English  \
0     John Doe     90       87         81       65       70   
1     Jane Doe     82       84         75       73       77   
2     Mary Lim     40       65         55       60       70   
3     Lisa Ray     55       52         77       62       90   

              Top_3_above_80  
0  Maths, Physics, Chemistry  
1             Physics, Maths  
2                        NaN  
3                    English  

Производительность :

#4k rows
df = pd.concat([df] * 1000, ignore_index=True)
#print (df)

def f1(df):
    df1 = df.iloc[:, 1:]
    m = df1 > 80
    count = m.sum(axis=1)
    arr = df1.columns.values[np.argsort(-df1.where(m), axis=1)]

    m = np.arange(arr.shape[1]) < count[:, None]
    a = np.where(m, arr, '')

    L = [', '.join(x).strip(', ') for x in a]
    df['Top_3_above_80'] = pd.Series(L, index=df.index)[count > 0]
    return df

def f2(df):
    df1 = df.iloc[:, 1:]
    m = df1 > 80
    count = m.sum(axis=1)

    df['Top_3_above_80'] = (df1.where(m).apply(lambda x: ', '.join(x.nlargest(3).index), axis=1)[count > 0])
    return df

In [210]: %timeit (f1(df.copy()))
19.3 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [211]: %timeit (f2(df.copy()))
2.43 s ± 61.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...