通过聚合在熊猫组中查找常见项目的最有效方法是什么

时间:2018-07-15 13:00:48

标签: python python-3.x pandas pandas-groupby

我试图在按熊猫进行汇总时在每列中找到最常出现的值。为了找到最常使用的值,我按照建议的here使用了SGDClassifier,但遇到了性能问题(请参见下面的代码段代码)

value_counts

结果:

import random
import time

import pandas as pd
df = pd.DataFrame({'Country_ID': [random.randint(1000, 100001) for i in
                                   range(100000)],
                  'City': [random.choice(['NY', 'Paris', 'London',
                                   'Delhi']) for i in range(100000)]})
agg_col = {'City': lambda x: x.value_counts().index[0]}
start = time.time()
df_agg = df.groupby('Country_ID').agg(agg_col)
print("Time Taken: {0}".format(time.time() - start))
print("Data: ", df_agg.head(5))

有什么方法可以改善上述效果?

2 个答案:

答案 0 :(得分:2)

在熊猫中进行某些操作比所需的要慢得多(例如,即使max本身很快,groupby上的idxmax也会很痛苦)。有时会退回到理论上效率不高的操作(例如,当我们需要的是最大数量时进行排序),但沿优化路径行进会有所帮助。 [好吧,我们实际上可以使用transform(max)然后使用过滤器。]

def orig(df):
    agg_col = {'City': lambda x: x.value_counts().index[0]}
    df_agg = df.groupby('Country_ID').agg(agg_col)
    return df_agg.reset_index()

def via_sort(df):
    size = df.groupby(["Country_ID", "City"]).size().reset_index()
    size = size.sort_values(["City", 0])  # sort City to break ties
    df_agg = size.groupby("Country_ID")["City"].last()
    return df_agg.reset_index()

这给了我

In [33]: %time orig_out = orig(df.iloc[:10000])
Wall time: 4.87 s

In [34]: %time sort_out = via_sort(df.iloc[:10000])
Wall time: 31.2 ms

我太耐心了,不能等待完整的100000完成原始代码,但是:

In [39]: %time sort_out = via_sort(df)
Wall time: 93.6 ms

现在应该注意的是,两者并没有给出完全相同的结果-它们在处理纽带的方式上有所不同。例如:

In [48]: orig_out.loc[(orig_out != sort_out).any(axis=1)].head(1)
Out[48]: 
   Country_ID    City
9        1093  London

In [49]: sort_out.loc[(orig_out != sort_out).any(axis=1)].head(1)
Out[49]: 
   Country_ID   City
9        1093  Paris

In [50]: df.query('Country_ID == 1093')
Out[50]: 
       Country_ID    City
1758         1093  London
7378         1093   Paris
29188        1093   Delhi

但是您可以根据需要自定义它。

答案 1 :(得分:1)

以下给出了几乎瞬时的结果(在我的计算机上大约为0.1s):

使用多索引('Country_ID', 'City')

获取计数系列
df_agg = df.groupby('Country_ID')['City'].value_counts()
Country_ID  City  
1000        London    6
            Delhi     4
            Paris     3
            NY        2
1001        NY        6
            Delhi     4
            Paris     4
            London    3
1002        Delhi     2
            Paris     2
            London    1
            NY        1

将多索引的一部分移至列

df_agg = df_agg.reset_index(level='City', name='Counts')
              City  Counts
Country_ID                
1000        London       6
1000         Delhi       4
1000         Paris       3
1000            NY       2
1001            NY       6
1001         Delhi       4
1001         Paris       4
1001        London       3
1002         Delhi       2
1002         Paris       2
1002        London       1
1002            NY       1

由于value_counts()返回排序后的结果,我们只需要删除重复项即可保留每个索引的第一行

df_agg = df_agg[~df_agg.index.duplicated(keep='first')]
              City  Counts
Country_ID                
1000        London       6
1001            NY       6
1002         Delhi       2

现在只需删除计数

df_agg = df_agg[['City']]
              City
Country_ID        
1000        London
1001            NY
1002         Delhi