在Pandas DataFrame中找到每行第二大值的列名称

11

我正在尝试查找与DataFrame中最大值和第二大值相关联的列名,这是一个简化的示例(实际示例有超过500个列):

Date  val1  val2 val3 val4
1990   5     7    1    10
1991   2     1    10   3
1992   10    9    6    1
1993   50    10   2    15
1994   1     15   7    8

需要成为:

Date  1larg   2larg
1990  val4    val2
1991  val3    val4
1992  val1    val2
1993  val1    val4
1994  val2    val4

使用idxmax可以找到具有最大值(即1larg above)的列名,但如何找到第二大的值?

3个回答

12

您的行中没有任何重复的最大值,因此我猜测如果您有[1,1,2,2],您希望选择val3val4

一种方法是使用argsort的结果作为索引,将其应用于一个包含列名称的系列中。

df = df.set_index("Date")
arank = df.apply(np.argsort, axis=1)
ranked_cols = df.columns.to_series()[arank.values[:,::-1][:,:2]]
new_frame = pd.DataFrame(ranked_cols, index=df.index)

生产

         0     1
Date            
1990  val4  val2
1991  val3  val4
1992  val1  val2
1993  val1  val4
1994  val2  val4
1995  val4  val3

(我添加了一个额外的1995年 [1,1,2,2] 行。)

或者,您可能会将其转换为平面格式,选择每个日期组中最大的两个值,然后再将其转换回来。


如上所述,这个方法是可行的,但很快我就遇到了问题。我遇到了一个情况,其中除了一个值外,其他所有值都是零。似乎Python会选择最大的值和两个随机的零(或从右边选择)。有没有办法让非零列名出现两次(分别在第0列和第1列)? - AtotheSiv

1
我们可以使用 idxmax 来查找每行最高值的列名;然后在每行中使用 mask 来标记最高值,并再次使用 idxmax 来查找第二个值的列名:
g = df.filter(like='val')
df['1larg'] = g.idxmax(axis=1)
df['2larg'] = g.mask(g.eq(g.max(axis=1), axis=0)).idxmax(axis=1)

请注意,仅当每一行具有唯一的最高值时,此方法才有效。如果不是这种情况,因为第二个最高值与这些行的最高值相同,上述方法将无法工作。在这种情况下,请使用下面的代码,其中我们只在每一行中第一次出现的最大值处使用掩码
g = df.filter(like='val')
df['1larg'] = g.idxmax(axis=1)
df['2larg'] = g.mask(g.eq(g.max(axis=1), axis=0) & g.apply(lambda x: ~x.duplicated(), axis=1)).idxmax(axis=1)

输出:

   Date  val1  val2  val3  val4 1larg 2larg
0  1990     5     7     1    10  val4  val2
1  1991     2     1    10     3  val3  val4
2  1992    10     9     6     1  val1  val2
3  1993    50    10     2    15  val1  val4
4  1994     1    15     7     8  val2  val4

0

对我有效的方法:

def flatten(l):
    return [item for sublist in l for item in sublist]

df = df.set_index("Date")
arank = df.apply(np.argsort, axis=1)
ranked_cols = df.columns.to_series()[arank.values[:,::-1][:,:2]]
new_frame = pd.DataFrame(ranked_cols, index=df.index)

#highest value
first_val = df.columns.to_series()[flatten(arank.values[:,::-1][:,:1].tolist())].reset_index()

#2nd value
sec_val = df.columns.to_series()[flatten(arank.values[:,::-1][:,1:2].tolist())].reset_index()

#just pretty names
first_val.columns = ['first_cat1', 'first_cat2']
sec_val.columns = ['sec_cat1', 'sec_cat2']    

#combine into new df with both columns
new_df = pd.concat([first_val['first_cat1'], sec_val['sec_cat2']],axis=1))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接