Pandas DataFrame,获取行中的3个最大值及其列名

use*_*490 4 max dataframe pandas argmax

论坛上有很多例子,如何找到具有相应列名的行的最大值。一些例子在这里这里

我想做的是对上面的例子进行一些具体的修改。我的数据框看起来像这样,其中所有列都是从左到右编号的(这个顺序非常重要):

x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9 x_10
  0   0   1   2   2   0   0   0   0    0
  4   4   0   4   4   1   0   0   0    0
  0   0   1   2   3   0   0   0   0    0
Run Code Online (Sandbox Code Playgroud)

现在,我想在每行末尾创建 6 个新列,其中包含列名称和行中的最大值。

x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9 x_10 Max1 ValMax1 Max2 ValMax2 Max3 ValMax3
  0   0   1   2   2   0   0   0   0    0
  4   4   0   4   4   1   0   0   0    0
  0   0   1   2   3   0   0   0   0    0
Run Code Online (Sandbox Code Playgroud)

如果某行的 max 个数超过 1(例如第一行中的值 2),我想在 Max1 列中仅保存一个具有最小索引的列名。在这种情况下,第二大值也是2,但相应的列有更大的索引。这意味着,“Max(y)”列中只需要保存一个列名。这是主要条件。在这种情况下,如果某行有超过 3 个最大值,则只需保存索引最小的 3 个列名。所以最终的输出应该是这样的 DF:

x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9 x_10 Max1 ValMax1 Max2 ValMax2 Max3 ValMax3
  0   0   1   2   2   0   0   0   0    0  x_4       2  x_5       2  x_3       1
  4   4   0   4   4   1   0   0   0    0  x_1       4  x_2       4  x_4       4
  0   0   1   2   3   0   0   0   0    0  x_5       3  x_4       2  x_3       1
Run Code Online (Sandbox Code Playgroud)

总结一下,我们得到了下一个结果:在第一行中 4 < 5,这意味着 4 首先出现(无论如何,第二个 2 立即出现在下一列中)。在第二行 1 < 2 < 4 < 5 中,我们只有 3 列,因此最终结果中缺少 5。在第三行中,索引不起作用,因为该行中的值完全不同。这也是主要条件。

wja*_*rea 6

在 NumPy 中执行此操作似乎更有意义,然后在末尾获取列名称。

我编写了一个函数,可用于获取n数组的顶部索引。它的工作原理是先使用np.nanargmax然后将值屏蔽为 NaN,然后​​再执行一次。(可能有更好的方法来做到这一点,但这只是我首先想到的。)

def argmax_n(arr: np.array, n: int, axis=None):
    arr = arr.astype('float')
    argmaxes = []
    for _ in range(n):
        argmax = np.nanargmax(arr, axis=axis, keepdims=True)
        argmaxes.append(argmax)
        np.put_along_axis(arr, argmax, np.NAN, axis=axis)
    return argmaxes
Run Code Online (Sandbox Code Playgroud)

像这样使用:

a = df.to_numpy()
argmax_3 = argmax_n(a, 3, axis=1)
Run Code Online (Sandbox Code Playgroud)

然后你可以构建你想要的 DataFrame,.join如果需要的话可以使用原始的 DataFrame。

max_data = {}
for i, arg in enumerate(argmax_3, start=1):
    max_data[f'Max{i}'] = df.columns[arg.flatten()]
    max_data[f'ValMax{i}'] = np.take_along_axis(a, arg, axis=1).flatten()

pd.DataFrame(max_data)
Run Code Online (Sandbox Code Playgroud)
  Max1  ValMax1 Max2  ValMax2 Max3  ValMax3
0  x_4        2  x_5        2  x_3        1
1  x_1        4  x_2        4  x_4        4
2  x_5        3  x_4        2  x_3        1
Run Code Online (Sandbox Code Playgroud)


moz*_*way 6

为了获得有效的方法,您需要向量化,为此使用和索引argpartition

import numpy as np

N = 3

# convert to arrays
# and reverse to preserve order
# of min index in case of a tie
cols = df.columns.to_numpy()[::-1]
a = df.loc[:, ::-1].to_numpy()

# get the top N indices
idx = np.argpartition(a, -N)[:, :-N-1:-1]

# get the top names 
names = cols[idx]

# get the top values
values = np.take_along_axis(a, idx, axis=1)
# or
values = a[np.arange(len(a))[:,None], idx]

# assign to new columns
df[[f'{x}{i+1}' for i in range(N) for x in ['Max', 'ValMax']]
  ] = (np.dstack([names,  values])
         .reshape(len(df), -1)
       )
Run Code Online (Sandbox Code Playgroud)

输出:

   x_1  x_2  x_3  x_4  x_5  x_6  x_7  x_8  x_9  x_10 Max1 ValMax1 Max2 ValMax2 Max3 ValMax3
0    0    0    1    2    2    0    0    0    0     0  x_4       2  x_5       2  x_3       1
1    4    4    0    4    4    1    0    0    0     0  x_1       4  x_2       4  x_4       4
2    0    0    1    2    3    0    0    0    0     0  x_5       3  x_4       2  x_3       1
Run Code Online (Sandbox Code Playgroud)