如何在巨大数据帧的每一行中查找前 n 个值的列索引

Posted

技术标签:

【中文标题】如何在巨大数据帧的每一行中查找前 n 个值的列索引【英文标题】:How to find column-index of top-n values within each row of huge dataframe 【发布时间】:2017-11-02 15:19:48 【问题描述】:

我有一个格式为:(示例数据)的数据框

      Metric1  Metric2  Metric3  Metric4  Metric5
ID    
1     0.5      0.3      0.2      0.8      0.7    
2     0.1      0.8      0.5      0.2      0.4    
3     0.3      0.1      0.7      0.4      0.2    
4     0.9      0.4      0.8      0.5      0.2    

其中分数范围在 [0,1] 之间,我希望生成一个函数,对于每个 id(行),计算前 n 个指标,其中 n 是函数的输入以及原始数据帧。

我的理想输出是:(例如 n = 3)

      Top_1     Top_2     Top_3
ID    
1     Metric4   Metric5   Metric1    
2     Metric2   Metric3   Metric5    
3     Metric3   Metric4   Metric1    
4     Metric1   Metric3   Metric4  

现在我写了一个可以工作的函数:

def top_n_partners(scores,top_n=3):
metrics = np.array(scores.columns)
records=[]
for rec in scores.to_records():
    rec = list(rec)
    ID = rec[0]
    score_vals = rec[1:]
    inds = np.argsort(score_vals)
    top_metrics = metrics[inds][::-1]
    dic = 
        'top_score_%s' % (i+1):top_metrics[i]
        for i in range(top_n)
    
    dic['ID'] = ID
    records.append(dic)
top_n_df = pd.DataFrame(records)
top_n_df.set_index('ID',inplace=True)
return top_n_df

但它似乎相当低效/缓慢,尤其是对于我要运行的数据量(具有数百万行的数据框),我想知道是否有更聪明的方法来解决这个问题?

【问题讨论】:

你可以做row.nlargest(3).index。只需将其应用于每一行。 【参考方案1】:

你可以使用numpy.argsort:

print (np.argsort(-df.values, axis=1)[:,:3])
[[3 4 0]
 [1 2 4]
 [2 3 0]
 [0 2 3]]

print (df.columns[np.argsort(-df.values, axis=1)[:,:3]])

Index([['Metric4', 'Metric5', 'Metric1'], ['Metric2', 'Metric3', 'Metric5'],
       ['Metric3', 'Metric4', 'Metric1'], ['Metric1', 'Metric3', 'Metric4']],
      dtype='object')

df = pd.DataFrame(df.columns[np.argsort(-df.values, axis=1)[:,:3]], 
                               index=df.index)
df = df.rename(columns = lambda x: 'Top_'.format(x + 1))
print (df)
      Top_1    Top_2    Top_3
ID                           
1   Metric4  Metric5  Metric1
2   Metric2  Metric3  Metric5
3   Metric3  Metric4  Metric1
4   Metric1  Metric3  Metric4 

感谢Divakar改进:

n = 3
df = pd.DataFrame(df.columns[df.values.argsort(1)[:,-n+2:1:-1]], 
                               index=df.index)

df = df.rename(columns = lambda x: 'Top_'.format(x + 1))
print (df)
      Top_1    Top_2    Top_3
ID                           
1   Metric4  Metric5  Metric1
2   Metric2  Metric3  Metric5
3   Metric3  Metric4  Metric1
4   Metric1  Metric3  Metric4                

【讨论】:

使用df.values.argsort(1)[:,-n+2:1:-1]可以获得更多性能。【参考方案2】:

使用 Pandas 重塑的不同方式:

df.set_index('ID', inplace=True)
df_out = df.rank(axis=1, ascending=False).astype(int).reset_index().melt(id_vars='ID').query('value <= 3').pivot(index='ID',columns='value')
df_out.columns = df_out.columns.droplevel().astype(str)
df_out = df_out.add_prefix('Top_')
print(df_out)

输出:

value    Top_1    Top_2    Top_3
ID                              
1      Metric4  Metric5  Metric1
2      Metric2  Metric3  Metric5
3      Metric3  Metric4  Metric1
4      Metric1  Metric3  Metric4

【讨论】:

以上是关于如何在巨大数据帧的每一行中查找前 n 个值的列索引的主要内容,如果未能解决你的问题,请参考以下文章

具有相同查找值的自动递增列索引 (VLOOKUP)

将数据帧返回函数应用于基础数据帧的每一行

删除每个索引的多索引熊猫数据帧的最低五个值

熊猫将第一个多索引转换为行索引,将第二个多索引转换为列索引

pandas 对数据帧DataFrame中数据的索引及切片操作

pandas将某一行设置为列索引(python)