将百分位数传递给pandas agg函数

111

我希望能够像其他numpy统计函数一样,通过pandas的agg()函数使用numpy的percentile()函数。

目前,我的数据框如下所示:

AGGREGATE   MY_COLUMN
A           10
A           12
B           5
B           9
A           84
B           22

我的代码看起来像这样:

grouped = dataframe.groupby('AGGREGATE')
column = grouped['MY_COLUMN']
column.agg([np.sum, np.mean, np.std, np.median, np.var, np.min, np.max])

上述代码可以工作,但我想实现类似于以下的操作:

column.agg([np.sum, np.mean, np.percentile(50), np.percentile(95)])

即,指定从agg()返回的各种百分位数。

应该如何做?


16个回答

152
也许不是特别高效,但一种方法是自己创建一个函数:
def percentile(n):
    def percentile_(x):
        return x.quantile(n)
    percentile_.__name__ = 'percentile_{:02.0f}'.format(n*100)
    return percentile_

然后将这个内容包含在你的agg中:
In [11]: column.agg([np.sum, np.mean, np.std, np.median,
                     np.var, np.min, np.max, percentile(50), percentile(95)])
Out[11]:
           sum       mean        std  median          var  amin  amax  percentile_50  percentile_95
AGGREGATE
A          106  35.333333  42.158431      12  1777.333333    10    84             12           76.8
B           36  12.000000   8.888194       9    79.000000     5    22             12           76.8

不确定这样做是否正确...

5
这对我来说有多个问题,请看我的答案 - Thomas
1
既然这是被接受的答案,很多人会直接盲目复制它,我已经更新了函数@Thomas。 - Roelant

77

您可以使用agg()函数在指定的列上执行自定义函数:

# 50th Percentile
def q50(x):
    return x.quantile(0.5)

# 90th Percentile
def q90(x):
    return x.quantile(0.9)

my_DataFrame.groupby(['AGGREGATE']).agg({'MY_COLUMN': [q50, q90, 'max']})

37

更具体地说,如果您只想使用百分位数函数汇总pandas groupby结果,则Python lambda函数提供了一个非常不错的解决方案。按照问题的表示法,按百分位95汇总应该是:

dataframe.groupby('AGGREGATE').agg(lambda x: np.percentile(x['COL'], q = 95))

你也可以将这个函数分配给一个变量,并与其他聚合函数一起使用。


我遇到了TypeError错误:必须提供“func”或“(column,aggfunc)元组”。你有什么想法发生了什么? - Dumb ML
虽然这看起来很漂亮,但如果你处理大数据的话,它绝对是高效的。 - Areza

28

我相信在pandas中做到这一点的惯用方式是:

df.groupby("AGGREGATE").quantile([0, 0.25, 0.5, 0.75, 0.95, 1])

24

我非常喜欢Andy Hayden给出的解决方案,不过对我来说有一些问题:

  • 如果数据框有多列,它会在列上进行聚合而不是行上进行聚合?
  • 对我来说,行名称是percentile_0.5(下划线而不是点)。不确定是什么原因,可能是我使用的是Python 3。
  • 需要导入numpy而不仅限于pandas(我知道,numpy在pandas中是隐式导入的...)

这里是修复了这些问题的更新版本:

def percentile(n):
    def percentile_(x):
        return x.quantile(n)
    percentile_.__name__ = 'percentile_{:02.0f}'.format(n*100)
    return percentile_

2
你的版本中是否打算使用 return x.quantile(n) - lighthouse65
好的发现!我确实犯了这个错误,谢谢你指出来。我会进行修改。 - Thomas
4
我认为格式{:02.0f}更好,可以避免单个数字百分比值的空格。 - ingomueller.net

20

尝试使用此方法计算 50% 和 95% 的百分位数:

column.describe(percentiles=[0.5, 0.95])

14

对于只需要describe的子集(通常是最常用的统计信息)的情况,您可以直接索引返回的pandas series,无需使用任何额外的函数。

例如,我经常只需要呈现25分位数、中位数、75分位数和计数。可以像这样在一行代码中完成:

columns.agg('describe')[['25%', '50%', '75%', 'count']]

如果要指定自己的百分位数集合,所选答案是一个不错的选择,但对于简单的用例,不需要额外的函数。


12
df.groupby("AGGREGATE").describe(percentiles=[0, 0.25, 0.5, 0.75, 0.95, 1])

默认情况下,describe函数会给出mean, count, std, min, max这些参数,而使用百分比数组可以选择所需的百分位数。

11

使用 pandas.Series.quantile 方法可以得到更高效的解决方案:

df.groupby("AGGREGATE").agg(("YOUR_COL_NAME", lambda x: x.quantile(0.5))

有几个百分位值

percentiles = [0.5, 0.9, 0.99]
quantile_funcs = [(p, lambda x: x.quantile(p)) for p in percentiles]
df.groupby("AGGREGATE").agg(quantile_funcs)

我喜欢这个,唯一的问题是列的名称。 - undefined
1
在定义聚合函数时,您可以自定义列的名称:[(f"percentile_{p}", lambda x: x.quantile(p)) for p in percentiles] - undefined
TIL。我猜这也可以与其他聚合函数混合使用,例如["mean", "std"] + q_fun_list。在我看来,这是最佳答案。 - undefined

6

你也可以使用lambda表达式来实现相同的功能。类似下面的代码:

        agg(
            lambda x: [
                np.min(a=x), 
                np.percentile(q=25,a=x), 
                np.median(a=x), 
                np.percentile(q=75,a=x), 
                np.max(a=x)
    ]
)

这与被接受的答案有何不同? - StupidWolf
唯一的区别是,您不需要定义一个新函数。这样可以节省一些代码行数。 - Ankit Khandelwal
1
你如何命名那些函数头?比如 np.min(a=x),你会如何为该函数命名头部? - Shivam Sahil

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