Python pandas - 基于groupby选择行

5

I have a sample table like this:

Dataframe: df

Col1     Col2    Col3    Col4
A   1   10  i
A   1   11  k
A   1   12  a
A   2   10  w
A   2   11  e
B   1   15  s
B   1   16  d
B   2   21  w
B   2   25  e
B   2   36  q
C   1   23  a
C   1   24  b

我正在尝试获取所有记录/行的组(Col1,Col2),该组具有较少数量的记录,并跳过仅具有1个记录的组(在此示例中,Col1 ='C')。因此,输出如下:

A   2   10  w
A   2   11  e
B   1   15  s
B   1   16  d

由于(A,2)组仅有2条记录,而(A,1)组有3条记录,因此需要进行翻译。

我试图从不同角度来解决这个问题,但似乎无法得到所需的结果。我能够使用groupby、filter和agg的组合找到我需要的组,但如何将其作为df的选择过滤器使用呢?经过很长时间的研究,我甚至不确定这种方法是否正确,因为它看起来过于复杂。我相信有一个优雅的解决方案,但我只是看不到它。

我尝试通过以下方式获取我想要显示行的分组:

    groups = df.groupby(["Col1, Col2"])["Col2"].agg({'no':'count'})
filteredGroups = groups.groupby(level=0).filter(lambda group: group.size > 1)
    print filteredGroups.groupby(level=0).agg('idxmin')

第二行是为了考虑那些可能只有一个记录的组,因为我不想将它们考虑在内。说实话,我尝试了很多变化和方法,最终也没有得到我想要的结果。我看到所有的答案都不是一行代码,至少我不觉得自己想得太多了。

刚刚添加了一个重要的需求部分,即我需要不显示仅包含一个组(C,1)的任何组。 - Ant Smith
4个回答

4
df['sz'] = df.groupby(['Col1','Col2'])['Col3'].transform("size")

df['rnk']     = df.groupby('Col1')['sz'].rank(method='min')
df['rnk_rev'] = df.groupby('Col1')['sz'].rank(method='min',ascending=False)

df.loc[ (df['rnk'] == 1.0) & (df['rnk_rev'] != 1.0) ]

      Col1  Col2  Col3 Col4  sz  rnk  rnk_rev
3    A     2    10    w   2  1.0      4.0
4    A     2    11    e   2  1.0      4.0
5    B     1    15    s   2  1.0      4.0
6    B     1    16    d   2  1.0      4.0

编辑:将“count”更改为“size”(如@Marco Spinaci的答案中所述),在此示例中不重要,但如果存在缺失值可能很重要。

为了更清楚地说明,在删除选定行之前,数据框的外观如下。

   Col1  Col2  Col3 Col4  sz  rnk  rnk_rev
0     A     1    10    i   3  3.0      1.0
1     A     1    11    k   3  3.0      1.0
2     A     1    12    a   3  3.0      1.0
3     A     2    10    w   2  1.0      4.0
4     A     2    11    e   2  1.0      4.0
5     B     1    15    s   2  1.0      4.0
6     B     1    16    d   2  1.0      4.0
7     B     2    21    w   3  3.0      1.0
8     B     2    25    e   3  3.0      1.0
9     B     2    36    q   3  3.0      1.0
10    C     1    23    a   2  1.0      1.0
11    C     1    24    b   2  1.0      1.0

这个完美地运作了!感谢您和@Marco Spinaci提供的原始解决方案。 - Ant Smith
这个能被修改以跳过只包含一条记录的分组吗?我已经添加了额外行到我的表格中来进行说明。基本上,所有col1=='C'的行都应该被忽略,因为只有一个带有C的分组(C,2)。 - Ant Smith
完美运行!谢谢! - Ant Smith

2

这并不是一个友好的答案,但它应该有效:

tmp = df[['col1','col2']].groupby(['col1','col2'], as_index=False).size()
df['occurrencies'] = pd.Series(df.index).apply(lambda i: tmp[df.col1[i]][df.col2[i]])
df['min_occurrencies'] = pd.Series(df.index).apply(lambda i: tmp[df.col1[i]].min())
df[df.occurrencies == df.min_occurrencies]

但是使用groupby一定有比创建辅助数据框更巧妙的方法...


1
以下是基于groupby.apply方法的解决方案。还有其他更简单的方法,例如使用JohnE的方法创建数据Series,我认为这种方法更好。
该解决方案通过在Col1级别对数据帧进行分组,然后传递一个函数来进一步按Col2分组来工作。然后评估每个子组以得出最小的组。请注意,大小相同的绑定将由首先评估的绑定确定。这可能不是理想的。
#create data
import pandas as pd 
df = pd.DataFrame({   
"Col1" : ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
"Col2" : [1, 1, 1, 2, 2, 1, 1, 2, 2, 2],
"Col3" : [10, 11, 12, 10, 11, 15, 16, 21, 25, 36],
"Col4" : ["i", "k", "a", "w", "e", "s", "d", "w", "e", "q"]
                                    })

Grouped = df.groupby("Col1")

def transFunc(x):
    smallest = [None, None]
    sub_groups = x.groupby("Col2")
    for group, data in sub_groups:
        if not smallest[1] or len(data) < smallest[1]:
            smallest[0] = group
            smallest[1] = len(data)
    return sub_groups.get_group(smallest[0])

Grouped.apply(transFunc).reset_index(drop = True)

编辑以分配结果。
result = Grouped.apply(transFunc).reset_index(drop = True)
print(result)

我刚试了一下这段代码,并添加了 print Grouped.head(),但得到了完整的 df 输出。我错过了什么吗?谢谢。 - Ant Smith
在分组对象被赋值之前,数据框或分组对象不会发生任何改变。所有的更改都不是原地进行的。换句话说,您不能仅仅打印Grouped.head作为计算结果(最后一行)还没有被赋值给一个对象。没有直接在Grouped对象上进行更改。 - Woody Pride
很抱歉。确实,这完美地运行了。我尝试打印了错误的东西。谢谢。 - Ant Smith
我编辑了答案,只是为了确保清楚,在最小组并列的情况下,将保留先处理的组。我不确定这是否是您想要的行为。 - Woody Pride

0

我想添加一个更短但易读的JohnE解决方案版本

df['sz'] = df.groupby(['Col1','Col2'])['Col3'].transform("size")
df.groupby('Col1').filter(lambda x: x['sz'].rank(method='min') == 1 and x['sz'].rank(method='min', ascending=False) != 1)

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