按照绝对值对每行进行排序,与列名无关。

4

I have a data frame of similar format:

df = pd.DataFrame({
 'p1': [0, 0, 1, 1, -2],
 'p2': [9, 2, 3, -5, 3],
 'p3': [1, 3, 10, 3, 7],
 'p4': [4, 4, 7, 1, 10]})

    p1  p2  p3  p4
0   0   9   1   4
1   0   2   3   4
2   1   3   10  7
3   1   -5  3   1
4   -2  3   7   10

期望输出:

top1    top2
p2:9    p4:4
p4:4    p3:3
p3:10   p4:7
p2:-5   p3:3
p4:10   p3:7

经过深入的研究,我已经成功地对数组进行了排序并获得了排序后的索引。我也能用列代替这些索引。但是我无法将它们与行值连接起来。

nlargest = 3
order = np.argsort(-df.abs().values, axis=1)[:, :nlargest]
result = pd.DataFrame(df.columns[order], 
                      columns=['top{}'.format(i) for i in range(1, nlargest+1)])

  top1 top2 top3
0   p2   p4   p3
1   p4   p3   p2
2   p3   p4   p2
3   p2   p3   p1
4   p4   p3   p2

使用上述方法,我尝试对不同数据框中的行进行排序,并考虑将它们连接起来。但是我找不到正确的方法来实现这一点。我知道这不是最优的方式。

result2 = pd.DataFrame(np.sort(df.values, axis=0), index=df.index, columns=df.columns)
result2 = result2.iloc[:, 0:nlargest]
result2.columns = columns=['top{}'.format(i) for i in range(1, nlargest+1)]

   top1  top2  top3
0    -2    -5     1
1     0     2     3
2     0     3     3
3     1     3     7
4     1     9    10

请帮我纠正排序并找到最简洁的方法来达到预期的格式。

最优方式意味着最佳性能吗? - jezrael
3个回答

3

使用 DataFrame.transformDataFrame.lookup

result = result.transform(lambda s: s + ':' + df.lookup(s.index, s).astype(str))

# print(result)
    top1  top2  top3
0   p2:9  p4:4  p3:1
1   p4:4  p3:3  p2:2
2  p3:10  p4:7  p2:3
3  p2:-5  p3:3  p1:1
4  p4:10  p3:7  p2:3

3

仅使用numpy方案以获得最佳性能:

nlargest = 3
arr = df.to_numpy()
order = np.argsort(-np.abs(arr), axis=1)[:, :nlargest]
print (order)
[[1 3 2]
 [3 2 1]
 [2 3 1]
 [1 2 0]
 [3 2 1]]

Idea 是通过 order 数组,按照类似这个解决方案中的方式,改变 numpy 数组 arr 中原始数据的顺序。

a = arr[np.arange(arr.shape[0])[:, None], order]
print (a)
[[ 9  4  1]
 [ 4  3  2]
 [10  7  3]
 [-5  3  1]
 [10  7  3]]

因此,您可以添加转换为字符串的值:

result = pd.DataFrame(df.columns[order] + ':' + a.astype(str), 
                      columns=['top{}'.format(i) for i in range(1, nlargest+1)])

print (result)
    top1  top2  top3
0   p2:9  p4:4  p3:1
1   p4:4  p3:3  p2:2
2  p3:10  p4:7  p2:3
3  p2:-5  p3:3  p1:1
4  p4:10  p3:7  p2:3

感谢@jezrael的努力 - supreeth2812

1

由于您需要创建字符串,我们可以使用 .stack.groupby.cumcount 来获取前 n 个值。

largest_n = 3

s = (
    df.stack()
    .sort_values(ascending=False)
    .groupby(level=0)
    .head(largest_n)
    .reset_index(1)
    .astype(str)
    .agg(":".join, axis=1)
    .to_frame("vals")
)

df1 = (
    (
        s.assign(key=s.groupby(level=0).cumcount() + 1)
        .set_index("key", append=True)
        .unstack()
    )
    .add_prefix("Top_")
    .droplevel(level=0, axis=1)
)

print(df1)

key  Top_1 Top_2 Top_3
0     p2:9  p4:4  p3:1
1     p4:4  p3:3  p2:2
2    p3:10  p4:7  p2:3
3     p3:3  p1:1  p4:1
4    p4:10  p3:7  p2:3

1
@supreeth2812 没问题,我会选择Jezrael的解决方案,因为它适用于更大的数据集,并且比使用lambda更快。 - Umar.H
1
你是对的!@jazrael 的解决方案实际上更快,而且由于数据集很大并且需要多次运行它,因此使用它是有意义的。 - supreeth2812

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