首页 > 解决方案 > Python:如果有平局,则从 nsmallest 获取第二个 idxmax

问题描述

我有DataFrame如下。我想要得到的是 mininim 1st, 2nd, ... , n Valuefor eachArticle并且知道Name每个的Value来源。

df
   Article  Name  Value
0     A_01  P_01    360
1     A_03  P_01    625
2     A_01  P_07    360
3     A_01  P_09    370
4     A_02  P_09    847
5     A_03  P_09    685
6     A_03  P_18    650
7     A_02  P_22    935
8     A_03  P_22    625
9     A_02  P_25    750
10    A_03  P_25    600
11    A_01  P_26    500
12    A_02  P_26    750
13    A_03  P_26    600
14    A_01  P_33    480
15    A_03  P_33    750

我正在使用此代码n minimum value查找n minimum name每个Article. 首先,我转动我df的以获得:

list_articles = df['Article'].drop_duplicates()
list_names = list(df['Name'].drop_duplicates())

pivot_df = df.pivot(index='Article', columns='Name', values='Value').reset_index()
pivot_df
Name Article   P_01   P_07   P_09   P_18   P_22   P_25   P_26   P_33
0       A_01  360.0  360.0  370.0    NaN    NaN    NaN  500.0  480.0
1       A_02    NaN    NaN  847.0    NaN  935.0  750.0  750.0    NaN
2       A_03  625.0    NaN  685.0  650.0  625.0  600.0  600.0  750.0

然后我运行了 lambda 函数来查找minimum_value和对应minimum_name

for i in range(1, 4):  # minimum 3
    pivot_df[f'Min_{i}_Value'] = pivot_df[list_names].T.apply(lambda x: x.nsmallest(i).max())
    pivot_df[f'Min_{i}_Name'] = pivot_df[list_names].T.apply(lambda x: x.nsmallest(i).idxmax())

这给了我另外 6 列:

pivot_df
Name Article   P_01   P_07  ...  Min_2_Name  Min_3_Value  Min_3_Name
0       A_01  360.0  360.0  ...        P_01        370.0        P_09
1       A_02    NaN    NaN  ...        P_25        847.0        P_09
2       A_03  625.0    NaN  ...        P_25        625.0        P_01

并且仅过滤到我感兴趣的那些列之后:

ColsToKeep = [x for x in pivot_df.columns.to_list() if x not in list_names]
ColsToKeep = [x for x in ColsToKeep if x[:3] == 'Min']
ColsToKeep.sort()
ColsToKeep = ['Article'] + ColsToKeep

final_df = pivot_df[ColsToKeep]

我得到final df

final_df
Name Article Min_1_Name  Min_1_Value Min_2_Name  Min_2_Value  Min_3_Name Min_3_Value
0       A_01       P_01        360.0       P_01        360.0        P_09       370.0
1       A_02       P_25        750.0       P_25        750.0        P_09       847.0
2       A_03       P_25        600.0       P_25        600.0        P_01       625.0

整个代码:

import pandas as pd

df = pd.DataFrame(
      [['A_01', 'P_01', 360],
       ['A_03', 'P_01', 625],
       ['A_01', 'P_07', 360],
       ['A_01', 'P_09', 370],
       ['A_02', 'P_09', 847],
       ['A_03', 'P_09', 685],
       ['A_03', 'P_18', 650],
       ['A_02', 'P_22', 935],
       ['A_03', 'P_22', 625],
       ['A_02', 'P_25', 750],
       ['A_03', 'P_25', 600],
       ['A_01', 'P_26', 500],
       ['A_02', 'P_26', 750],
       ['A_03', 'P_26', 600],
       ['A_01', 'P_33', 480],
       ['A_03', 'P_33', 750]],
      columns=['Article', 'Name', 'Value'])

list_articles = df['Article'].drop_duplicates()
list_names = list(df['Name'].drop_duplicates())

pivot_df = df.pivot(index='Article', columns='Name', values='Value').reset_index()

for i in range(1, 4):
    pivot_df[f'Min_{i}_Value'] = pivot_df[list_names].T.apply(lambda x: x.nsmallest(i).max())
    pivot_df[f'Min_{i}_Name'] = pivot_df[list_names].T.apply(lambda x: x.nsmallest(i).idxmax())

ColsToKeep = [x for x in pivot_df.columns.to_list() if x not in list_names]
ColsToKeep = [x for x in ColsToKeep if x[:3] == 'Min']
ColsToKeep.sort()
ColsToKeep = ['Article'] + ColsToKeep

final_df = pivot_df[ColsToKeep]

最后,我的问题是什么?如果你仔细观察,你会发现Min_1_ValueandMin_2_Value是一样的(这是正确的),但是Min_1_NameMin_2_Name也是一样的,这是不正确的。为什么?因为在原始数据中,同一篇文章有​​两个具有相同值的名称,所以这是平局。我的代码正在通过 min_n_value 的索引查找 min_n 的名称,因此如果匹配超过 1 个,则不考虑平局的可能性。但是如何Min_2_Name正确分配不是的Min_1_Name呢?可以按字母顺序选择,没关系。你有什么主意吗?

标签: pythonpandasdataframe

解决方案


我希望您的解决方案应该简化 - 首先DataFrame.sort_values是 2 列:

df = df.sort_values(['Article','Value'])
print (df)
   Article  Name  Value
0     A_01  P_01    360
2     A_01  P_07    360
3     A_01  P_09    370
14    A_01  P_33    480
11    A_01  P_26    500
9     A_02  P_25    750
12    A_02  P_26    750
4     A_02  P_09    847
7     A_02  P_22    935
10    A_03  P_25    600
13    A_03  P_26    600
1     A_03  P_01    625
8     A_03  P_22    625
6     A_03  P_18    650
5     A_03  P_09    685
15    A_03  P_33    750

然后创建计数器 Series byGroupBy.cumcount并通过 过滤 top3 值boolean indexing,添加MultiIndex并重塑 by Series.unstack,最后MultiIndex按 s 在列中展平f-string

g = df.groupby('Article').cumcount().add(1)
mask = g < 4
df = df[mask].set_index(['Article',g[mask]]).unstack().sort_index(axis=1, level=1)
df.columns = df.columns.map(lambda x: f'Min_{x[1]}_{x[0]}')
df = df.reset_index()

print (df)
  Article Min_1_Name  Min_1_Value Min_2_Name  Min_2_Value Min_3_Name  \
0    A_01       P_01          360       P_07          360       P_09   
1    A_02       P_25          750       P_26          750       P_09   
2    A_03       P_25          600       P_26          600       P_01   

   Min_3_Value  
0          370  
1          847  
2          625  

推荐阅读