【问题标题】:Finding top N columns for each row in data frame查找数据框中每一行的前 N ​​列
【发布时间】:2016-03-21 17:18:55
【问题描述】:

给定一个包含一个描述性列和 X 个数值列的数据框,对于每一行,我想识别具有较高值的​​前 N ​​列并将其保存为新数据框上的行。

例如,考虑以下数据框:

df = pd.DataFrame()
df['index'] = ['A', 'B', 'C', 'D','E', 'F']
df['option1'] = [1,5,3,7,9,3]
df['option2'] = [8,4,5,6,9,2]
df['option3'] = [9,9,1,3,9,5]
df['option4'] = [3,8,3,5,7,0]
df['option5'] = [2,3,4,9,4,2]

我想输出(假设 N 是 3,所以我想要前 3 个):

A,option3
A,option2
A,option4

B,option3
B,option4
B,option1

C,option2
C,option5
C,option4 (or option1 - ties arent really a problem)

D,option5
D,option1
D,option2

and so on....

知道如何轻松实现吗? 谢谢

【问题讨论】:

  • 你想要什么样的格式?
  • 由于 OP 从未回复,让我们合理假设他们想要一个数据框,而不是列表列表或其他任何东西。
  • 重命名,因为 OP 显然想要 "Finding top N columns" 而不是 "Selecting top N columns...",这将是带有 df 输出的 pandas 操作。

标签: python python-2.7 pandas dataframe top-n


【解决方案1】:
dfc = df.copy()
result = {}

#First, I would effectively transpose this

for key in dfc:
    if key != 'index':
        for i in xrange(0,len(dfc['index'])):
            if dfc['index'][i] not in result:
                result[dfc['index'][i]] = []
            result[dfc['index'][i]] += [(key,dfc[key][i])]


def get_topn(result,n):
    #Use this to get the top value for each option
    return [x[0] for x in sorted(result,key=lambda x:-x[1])[0:min(len(result),n)]]


#Lastly, print the output in your desired format.
n = 3
keys = sorted([k for k in result])
for key in keys:
      for option in get_topn(result[key],n):
         print str(key) + ',' + str(option)
      print

【讨论】:

  • 感谢 Adam,这真的很有帮助,唯一的问题是由于字典转换,id 的顺序最终发生了变化。我通过使用原始数据框对“键”进行排序来解决这个问题。有点hacky,但没关系
【解决方案2】:

假设

N = 3

首先,我将创建输入字段矩阵,并为每个字段记住该单元格的原始选项:

matrix = [[(j, 'option' + str(i)) for j in df['option' + str(i)]] for i in range(1,6)]

这一行的结果将是:

[
 [(1, 'option1'), (5, 'option1'), (3, 'option1'), (7, 'option1'), (9, 'option1'), (3, 'option1')],
 [(8, 'option2'), (4, 'option2'), (5, 'option2'), (6, 'option2'), (9, 'option2'), (2, 'option2')],
 [(9, 'option3'), (9, 'option3'), (1, 'option3'), (3, 'option3'), (9, 'option3'), (5, 'option3')],
 [(3, 'option4'), (8, 'option4'), (3, 'option4'), (5, 'option4'), (7, 'option4'), (0, 'option4')],
 [(2, 'option5'), (3, 'option5'), (4, 'option5'), (9, 'option5'), (4, 'option5'), (2, 'option5')]
]

然后我们可以使用 zip 函数轻松变换矩阵,按元组的第一个元素对结果行进行排序并取 N 个第一项:

transformed = [sorted(l, key=lambda x: x[0], reverse=True)[:N] for l in zip(*matrix)]

转换后的列表如下所示:

[
 [(9, 'option3'), (8, 'option2'), (3, 'option4')],
 [(9, 'option3'), (8, 'option4'), (5, 'option1')],
 [(5, 'option2'), (4, 'option5'), (3, 'option1')],
 [(9, 'option5'), (7, 'option1'), (6, 'option2')],
 [(9, 'option1'), (9, 'option2'), (9, 'option3')],
 [(5, 'option3'), (3, 'option1'), (2, 'option2')]
]

最后一步将通过以下方式连接列索引和结果元组:

for id, top in zip(df['index'], transformed):
    for option in top:
        print id + ',' + option[1]
    print ''

【讨论】:

  • 这是一个有趣的解决方案,但它依赖于预定义的列命名。我使用了 option1, option2,... ofr 简单,名称不遵循逻辑,可以根据情况而有所不同。但是感谢您的帮助
【解决方案3】:

如果您只想配对:

from operator import itemgetter as it
from itertools import repeat
n = 3

 # sort_values = order pandas < 0.17
new_d = (zip(repeat(row["index"]), map(it(0),(row[1:].sort_values(ascending=0)[:n].iteritems())))
                 for _, row in df.iterrows())
for row in new_d:
    print(list(row))

输出:

[('B', 'option3'), ('B', 'option4'), ('B', 'option1')]
[('C', 'option2'), ('C', 'option5'), ('C', 'option1')]
[('D', 'option5'), ('D', 'option1'), ('D', 'option2')]
[('E', 'option1'), ('E', 'option2'), ('E', 'option3')]
[('F', 'option3'), ('F', 'option1'), ('F', 'option2')]

这也维持秩序。

如果你想要一个列表列表:

from operator import itemgetter as it
from itertools import repeat
n = 3

new_d = [list(zip(repeat(row["index"]), map(it(0),(row[1:].sort_values(ascending=0)[:n].iteritems()))))
                 for _, row in df.iterrows()]

输出:

[[('A', 'option3'), ('A', 'option2'), ('A', 'option4')],
[('B', 'option3'), ('B', 'option4'), ('B', 'option1')], 
[('C', 'option2'), ('C', 'option5'), ('C', 'option1')], 
[('D', 'option5'), ('D', 'option1'), ('D', 'option2')], 
[('E', 'option1'), ('E', 'option2'), ('E', 'option3')],
[('F', 'option3'), ('F', 'option1'), ('F', 'option2')]]

或者使用 python 排序:

new_d = [list(zip(repeat(row["index"]), map(it(0), sorted(row[1:].iteritems(), key=it(1) ,reverse=1)[:n])))
                     for _, row in df.iterrows()]

这实际上是最快的,如果你真的想要字符串,那么你可以随意格式化输出。

【讨论】:

  • 这会产生值而不是列名。
  • @lied,OP 可能想要也可能不想要这些名字,如果他们想要改变它是微不足道的,我在评论中要求澄清
  • 感谢 Padraic,我有一个关于该问题所需输出的示例。尽管如此,任何想法为什么你的代码给我这个错误: AttributeError: 'Series' object has no attribute 'items' n the "pd.DataFrame(map(it(0), sorted(row[1:].items(), key=it(1) ,reverse=1)[:n]) for _, row in df.iterrows())" 行?
  • @Diego,您可能需要 python 2 中的 iteritems,您的输出未显示您是否真的想要另一个数据帧或究竟是什么,您接受的答案正在做什么可以在一两行代码中完成
  • 嘿,感谢您的回复,我会非常乐意尝试您的方法,所以我使用了“iteritems”。然后它在 print(list(row)) 上失败,并带有“KeyError:'index'”。如果我删除“列表”并只使用打印(行)它不会失败但不会打印任何东西。你认为这可能是另一个 python 2.7 X 3 的区别吗?再次感谢您的宝贵时间
【解决方案4】:

这可能不是那么优雅,但我认为它几乎可以满足您的需求:

n = 3
df.index = pd.Index(df['index'])
del df['index']
df = df.transpose().unstack()
for i, g in df.groupby(level=0):
    g = g.sort_values(ascending=False)
    print i, list(g.index.get_level_values(1)[:n])

【讨论】:

  • 这改变了原始数据框,我不确定这是 OP 想要的东西
【解决方案5】:

又一个疯狂的单线,给定n = 3

{index:option for (index, option) in zip(df['index'], 
    [df.columns[pd.notnull(x[1].where(x[1][1:].sort_values()[-n:]))].tolist()
        for x in df.iterrows()])}

{'A': ['option2', 'option3', 'option4'],
 'C': ['option2', 'option4', 'option5'],
 'B': ['option1', 'option3', 'option4'],
 'E': ['option1', 'option2', 'option3'],
 'D': ['option1', 'option2', 'option5'],
 'F': ['option1', 'option3', 'option5']}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多