在pandas数据框架中贡献最大值的列



我使用python 3。我有熊猫数据帧df。Df看起来像

Name Maths Science Social studies
abc   80     70      90
cde   90     60      80 
xyz   100    80      85
...
...

我想生成一个pandas数据框架,它将存储学生姓名,最大分数&这门课得分最高。如果最高分数是100,那么它将考虑下一个最高分数,而不是100。所以我的输出数据帧看起来像

Name Highest_Marks Subject_contributed_Max
abc   90              Social Studies 
cde   90              Maths 
xyz   85              Social Studies
你能建议我怎么做吗?

您可以使用:

df2 = df.drop(columns='Name').mask(df.eq(100))
df['Highest_Marks'] = df2.max(axis=1)
df['Subject_contributed_Max'] = df2.idxmax(axis=1)

输出:

Name  Maths  Science  Social studies  Highest_Marks Subject_contributed_Max
0  abc     80       70              90           90.0          Social studies
1  cde     90       60              80           90.0                   Maths
2  xyz    100       80              85           85.0          Social studies

为了提高效率,避免计算max/idxmax的两倍,您可以计算idxmax并使用查找

s = (df
.drop(columns='Name')
.mask(df.eq(100))
.idxmax(axis=1)
)
idx, cols = pd.factorize(s)
df['Highest_Marks'] = df.reindex(cols, axis=1).to_numpy()[np.arange(len(df)), idx]
df['Subject_contributed_Max'] = s

可以了

df_melt = df.melt('Name')
df_melt = df_melt.loc[df_melt['value'] < 100]
df_melt['RN'] = df_melt.sort_values(['value'], ascending=False).groupby(['Name']).cumcount() + 1
df_melt.loc[df_melt['RN'] == 1].sort_values('Name')

可以这样做:

df = df.set_index('Name').stack().reset_index().rename(columns={
'level_1':'Subject_contributed_Max', 0:'Highest_Marks'}).sort_values(
['Name','Highest_Marks'])
df = df[df['Highest_Marks'] != 100].groupby('Name').last().reset_index()[[
'Name', 'Highest_Marks', 'Subject_contributed_Max']]

输入:

Name  Maths  Science  Social studies
0  abc     80       70              90
1  cde     90       60              80
2  xyz    100       80              85

输出:

Name  Highest_Marks Subject_contributed_Max
0  abc             90          Social studies
1  cde             90                   Maths
2  xyz             85          Social studies

更新:

这里有一个比我原来的答案更快的方法。它类似于@mozway的答案中的一个建议,尽管它使用where()而不是mask()(并且它也只返回高分,而不是单个标记列)。

df2 = df.set_index('Name')
df2 = df2.where(df2 < 100)
df2 = df2.assign(**{'Highest_Marks':df2.max(axis=1).astype(int), 
'Subject_contributed_Max':df2.idxmax(axis=1)}).reset_index()[[
'Name', 'Highest_Marks', 'Subject_contributed_Max']]

在OP的评论下,我对@mozway和我的答案进行了一些基准测试(我还尝试添加@ArchAngelPwn的答案,但它似乎没有在当前形式下给出可比的输出)。

下面是一个1000行9000列的数据框的结果:

Timeit results:
foo_1 (orig) ran in 2.4102477666456252 seconds using 3 iterations
foo_2 (refined) ran in 2.256996333327455 seconds using 3 iterations
foo_3 (where) ran in 1.1588773333545153 seconds using 3 iterations
foo_4 (mozway mask) ran in 1.17148056665125 seconds using 3 iterations
foo_5 (mozway mask lookup) ran in 1.1049298333236948 seconds using 3 iterations

最新更新