使用基于分组条件的方式删除一些 Pandas 数据帧的行。

6
我有一些和销售相关的数据,并想查看不同邮编之间的比较:是否有些邮编提供的业务比其他邮编更加有利可图?因此,我按邮编对数据进行分组,并可以轻松地获得每个邮编的各种统计数据。然而,有一些非常高价值的工作会扭曲统计数据,因此我想要忽略这些异常值。出于各种原因,我希望通过分组来定义异常值:例如,在数据框中删除分组中处于前X个百分位或前N个的行。

因此,如果我有以下数据框:

>>> df
Out[67]: 
     A         C         D
0  foo -0.536732  0.061055
1  bar  1.470956  1.350996
2  foo  1.981810  0.676978
3  bar -0.072829  0.417285
4  foo -0.910537 -1.634047
5  bar -0.346749 -0.127740
6  foo  0.959957 -1.068385
7  foo -0.640706  2.635910

我希望能够有一个函数,比如drop_top_n(df, group_column, value_column, number_to_drop),其中drop_top_n(df, "A", "C", 2)将返回:

     A         C         D
0  foo -0.536732  0.061055
4  foo -0.910537 -1.634047
5  bar -0.346749 -0.127740
7  foo -0.640706  2.635910

使用filter会删除整个组,而不是组的部分。

我猜想可以通过迭代组,并为每个组找出要删除的行,然后返回到原始数据框并删除它们,但那似乎非常笨拙。有更好的方法吗?

2个回答

7

在0.13版本中,您可以使用cumcount函数:

In [11]: df[df.sort('C').groupby('A').cumcount(ascending=False) >= 2]  # use .sort_index() to remove UserWarning
Out[11]: 
     A         C         D
0  foo -0.536732  0.061055
4  foo -0.910537 -1.634047
5  bar -0.346749 -0.127740
7  foo -0.640706  2.635910

[4 rows x 3 columns]

可能更有意义的是先进行排序:
In [21]: df = df.sort('C')

In [22]: df[df.groupby('A').cumcount(ascending=False) >= 2]
Out[22]: 
     A         C         D
4  foo -0.910537 -1.634047
7  foo -0.640706  2.635910
0  foo -0.536732  0.061055
5  bar -0.346749 -0.127740

[4 rows x 3 columns]

这很有趣,而且非常好地解决了我提出的具体例子。如果我想要删除(或保留)只有那些值高于组均值或众数的行,或者在前x个百分位之内,是否有更一般的方法呢? - lpryor

3
你可以使用apply()方法:
import pandas as pd
import io


txt="""     A         C         D
0  foo -0.536732  0.061055
1  bar  1.470956  1.350996
2  foo  1.981810  0.676978
3  bar -0.072829  0.417285
4  foo -0.910537 -1.634047
5  bar -0.346749 -0.127740
6  foo  0.959957 -1.068385
7  foo -0.640706  2.635910"""

df = pd.read_csv(io.BytesIO(txt), delim_whitespace=True, index_col=0)

def f(df):
    return df.sort("C").iloc[:-2]
df2 = df.groupby("A", group_keys=False).apply(f)
print df2

输出:

     A         C         D
5  bar -0.346749 -0.127740
4  foo -0.910537 -1.634047
7  foo -0.640706  2.635910
0  foo -0.536732  0.061055

如果您想要原始订单:
print df2.reindex(df.index[df.index.isin(df2.index)])

输出:

    A         C         D
0  foo -0.536732  0.061055
4  foo -0.910537 -1.634047
5  bar -0.346749 -0.127740
7  foo -0.640706  2.635910

获取组均值以上的行:

def f(df):
    return df[df.C>df.C.mean()]
df3 = df.groupby("A", group_keys=False).apply(f)
print df3

我认为这就是我想要的。apply()函数会分别作用于groupby()产生的每个组,我之前没有意识到这一点。 - lpryor

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