【问题标题】:groupby this or thatgroupby 这个或那个
【发布时间】:2019-03-22 17:33:30
【问题描述】:

请耐心等待,我已经为此烦恼了好几个小时。

考虑这些数据

np.random.seed(2)
apples = np.random.randint(10,20,9)
df = pd.DataFrame({'name':list('aabbcdeee'), 'addr':list('mmznjjkkx'), 'apples':apples})[['name','addr','apples']]

如果name 是同一个人,那么它就是同一个人,如果addr 是同一个人,那么它也是同一个人。我想数一数每个人有多少个苹果。通常,这很简单:

In [50]: df[['apples', 'name']].groupby('name').sum()
    Out[50]:
      apples
name
a         36
b         28
c         18
d         17
e         38

df[['apples', 'addr']].groupby('addr').sum(),因为它们应该返回相同的输出。 但是,地址j输入了她的名字cd,而名字b输入了她的地址zn,而e输入了两次正确的地址但第三次搞砸了。结果,上述groupby 操作低估了某些人拥有的苹果数量。理想的输出是:

In [52]: %paste
pd.DataFrame({'name':list('aabbcceee'), 'addr':list('mmnnjjkkk'), 'apples':apples}).groupby('name').apples.sum()

## -- End pasted text --
Out[52]:
name
a    36
b    28
c    35
e    38
Name: apples, dtype: int32

我可以使用集合识别具有错误地址或名称的索引:

sameNames = df.name[df.name.duplicated()].index
sameAddr = df.addr[df.addr.duplicated()].index
sameNameORaddr = df.name[(df.name.duplicated() | df.addr.duplicated())].index

所以错误就在这里:

In [47]: sameNameORaddr.difference(sameNames).union(sameNameORaddr.difference(sameAddr))
Out[47]: Int64Index([2, 3, 4, 5, 8], dtype='int64')

但我不知道如何使用它来执行groupby。我正在考虑尝试分配可以正确识别重复名称或地址但无法弄清楚如何做到这一点的新名称。任何帮助表示赞赏。

【问题讨论】:

    标签: python pandas group-by conditional-statements pandas-groupby


    【解决方案1】:

    另一种方法:

    df['group'] = df.groupby('addr').ngroup()
    
    d = {'name':'first','apples':'sum'}
    df1 = df.groupby('name',as_index=False).sum().groupby('group').agg(d)
    
    df1 = df1.sort_values('name').reset_index(drop=True)
    
    print (df1)
    
    # Output:
    
      name  apples
    0   a    36
    1   b    28
    2   c    35
    3   e    38
    

    说明:

    首先使用groupby.ngroupaddr 列中的每个组进行编号

    df['group'] = df.groupby('addr').ngroup()
    
      name addr apples group
    0   a   m    18     2
    1   a   m    18     2
    2   b   z    16     5
    3   b   n    12     3
    4   c   j    18     0
    5   d   j    17     0
    6   e   k    12     1 
    7   e   k    11     1 
    8   e   x    15     4
    

    然后 groupby 与 name 和 sum df.groupby('name',as_index=False).sum() 它返回

       name  apples group
     0  a     36     4
     1  b     28     8
     2  c     18     0
     3  d     17     0
     4  e     38     6
    

    现在相同的地址行将具有相同的组号,因此您可以再次对 group 列进行分组,并使用 groupby.agg 函数与 apples = 'sum'name = first or last 来保留名称的第一个/最后一个实例。

    d = {'name':'first','apples':'sum'}
    df1 = df.groupby('name',as_index=False).sum().groupby('group').agg(d)
    

    然后只需对值进行排序并重置索引即可获得输出。

    【讨论】:

    • 你能详细说明一下这个机制吗?
    • 调用ngroup()时一直失败:'DataFrameGroupBy' object has no attribute 'ngroup'
    • 也许是我的熊猫:pd.__version__ Out[8]: '0.18.1'?
    • @GeneBurinsky 是的。它只能从version 0.20.2 获得。你可以更新。
    • @GeneBurinsky 使用 df['group'] = df.groupby('addr').grouper.group_info[0] 而不是 df.groupby('addr').ngroup()
    【解决方案2】:

    如果我理解正确,您可以创建从地址到名称的映射。然后使用此映射覆盖名称并照常执行您的GroupBy

    s = df.drop_duplicates('addr').set_index('addr')['name']
    df['name'] = df['addr'].map(s)
    
    res = df.groupby('name', as_index=False)['apples'].sum()
    
    print(res)
    
      name  apples
    0    a      36
    1    b      28
    2    c      35
    3    e      38
    

    addr 的初始 drop_duplicates 假设为任何 name 输入的第一个地址是正确的。

    【讨论】:

    • 这是一种更快、更简单、或许更灵活的解决方案,但由于某种原因,@Abhi 的解决方案找到了更多组。我认为在应用于一个人的数据时应该测试这两个答案,因为根据复杂性,它们可能会产生不同的答案。
    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多