使用pandas DataFrame进行分组(GroupBy),并选择最常见的值。

191

我有一个数据框,其中有三列字符串。我知道第三列中只有一个值对于前两列的每个组合是有效的。为了清理数据,我需要按照前两列对数据框进行分组,并选择每个组合中第三列的最常见值。

我的代码:

import pandas as pd
from scipy import stats

source = pd.DataFrame({
    'Country': ['USA', 'USA', 'Russia', 'USA'], 
    'City': ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
    'Short name': ['NY', 'New', 'Spb', 'NY']})

source.groupby(['Country','City']).agg(lambda x: stats.mode(x['Short name'])[0])

代码的最后一行无法运行,它显示了一个KeyError: 'Short name'的错误,如果我仅按城市分组,则会得到一个断言失败的错误。我该怎么解决?

13个回答

0

这里的问题在于性能,如果有很多行,那将会是一个问题。

如果您遇到这种情况,请尝试使用以下方法:

import pandas as pd

source = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'], 
              'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
              'Short_name' : ['NY','New','Spb','NY']})

source.groupby(['Country','City']).agg(lambda x:x.value_counts().index[0])

source.groupby(['Country','City']).Short_name.value_counts().groupby['Country','City']).first()

0

如果想要使用.agg函数返回所有模式(无论是单个还是多个),可以创建一个返回模式列表的函数。

df.agg(lambda x: x.mode().to_list())

def lmode(x): return x.mode().to_list()
df.agg(lmode)

如果你希望单一模式返回标量,可以使用以下函数:

def lmode(x): a = x.mode(); return a.to_list() if len(a) > 1 else a.squeeze()

好处:

  • 返回所有模式
    • 单个模式作为标量,多个模式作为列表
  • groupbyagg 一起使用
  • 可以与其他聚合函数组合使用(例如:df.agg([lmode, 'nunique'])
  • 返回 lmode 而不是 lambda 作为聚合名称
  • 当分组模式为 np.nan 时不会触发错误,而是返回 []

多个聚合函数的示例

import numpy as np
import pandas as pd

df = pd.DataFrame({
    'i': [1, 3, 2, np.nan, 3, 1],
    's': ['a', 'a', 'b', 'c', 'c', np.nan],
})

def lmode(x): a = x.mode(); return a.to_list() if len(a) > 1 else a.squeeze()

# Combined aggregates with multiple modes
print(df.agg([lmode, 'nunique']))

                  i  s
lmode    [1.0, 3.0]  a
nunique           3  4

来自 OP 的示例

source = pd.DataFrame({
    'Country': ['USA', 'USA', 'Russia', 'USA'],
    'City': ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
    'Short name': ['NY', 'New', 'Spb', 'NY']})

source.groupby(['Country','City']).agg(lmode)

                         Short name
Country City                       
Russia  Sankt-Petersburg        Spb
USA     New-York                 NY

有没有一种方法可以编辑这段代码以删除平局的条目?比如纽约和New都出现了两次。不应该返回New York。 - keenan
有没有办法修改这段代码,以删除存在平局的条目?比如纽约和新都出现了两次。不应该返回"纽约"。 - keenan

0
一个比较笨重但是对于大型数据集而言更快的方法是先获取某一列的计数,将其按照由高到低的顺序排序,然后在子集上进行去重以仅保留最大的情况。以下是代码示例:
>>> import pandas as pd
>>> source = pd.DataFrame(
        {
            'Country': ['USA', 'USA', 'Russia', 'USA'], 
            'City': ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
            'Short name': ['NY', 'New', 'Spb', 'NY']
        }
    )
>>> grouped_df = source\
        .groupby(['Country','City','Short name'])[['Short name']]\
        .count()\
        .rename(columns={'Short name':'count'})\
        .reset_index()\
        .sort_values('count', ascending=False)\
        .drop_duplicates(subset=['Country', 'City'])\
        .drop('count', axis=1)
>>> print(grouped_df)
  Country              City Short name
1     USA          New-York         NY
0  Russia  Sankt-Petersburg        Spb

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