Pandas Dataframe groupby描述8倍〜比分开计算慢

时间:2018-06-24 22:10:43

标签: python pandas quantile percentile describe

以下代码使用两种不同的方法总结了数值数据。

第一种方法使用Dataframe()。describe()并传递一些特定的额外百分位数。

第二种方法分别计算汇总统计量(均值,标准差,N),将其堆叠,计算相同的分位数,然后将两者相加并按索引排序,因此结果基本相同作为第一种方法。

我们可以清除后缀的名称上的细微差别,并且由于汇总数据很小,因此非常快。

事实证明,在此示例中,使用describe函数的速度要慢8倍。

我正在寻找原因,以及为什么可能会进一步加快其速度的其他方法(过滤器,组,值)的建议都从UI传递到龙卷风服务-因此速度非常重要,因为用户等待结果,数据甚至可能比该示例大。

import pandas as pd
import numpy as np
from datetime import datetime

def make_data (n):

    ts = datetime.now().timestamp() + abs(np.random.normal(60, 30, n)).cumsum()

    df = pd.DataFrame({
        'c1': np.random.choice(list('ABCDEFGH'), n),
        'c2': np.random.choice(list('ABCDEFGH'), n),
        'c3': np.random.choice(list('ABCDEFGH'), n),
        't1': np.random.randint(1, 20, n),
        't2': pd.to_datetime(ts, unit='s'),
        'x1': np.random.randn(n),
        'x2': np.random.randn(n),
        'x3': np.random.randn(n)
        })

    return df

def summarize_numeric_1 (df, mask, groups, values, quantiles): 

    dfg = df[mask].groupby(groups)[values]

    return dfg.describe(percentiles = quantiles).stack()

def summarize_numeric_2 (df, filt, groups, values, quantiles): 

    dfg = df[mask].groupby(groups)[values]

    dfg_stats = dfg.agg([np.mean, np.std, len]).stack()
    dfg_quantiles = dfg.quantile(all_quantiles)

    return dfg_stats.append(dfg_quantiles).sort_index()

%time df = make_data(1000000)

groups = ['c1', 'c2', 't1']
mask = df['c3'].eq('H') & df['c1'].eq('A')
values = ['x1', 'x3']
base_quantiles = [0, .5, 1] 
extd_quantiles = [0.25, 0.75, 0.9]
all_quantiles = base_quantiles + extd_quantiles

%timeit summarize_numeric_1(df, mask, groups, values, extd_quantiles)
%timeit summarize_numeric_2(df, mask, groups, values, all_quantiles)

在我的PC上执行此操作的时间是:

使用描述: 每个循环873毫秒±8.9毫秒(平均±标准偏差,共运行7次,每个循环1次)

使用两步方法: 每个循环105 ms±490 µs(平均±标准偏差,共运行7次,每个循环10个循环)

欢迎所有输入!

2 个答案:

答案 0 :(得分:1)

有根据的猜测

我将其发布为答案,也许稍后会删除,因为它比实际答案更像是一种有根据的猜测。另外,发表评论的时间太长了。

因此,在阅读您的回答后,我要做的第一件事是在分析器中重新运行您的计时,以仔细研究该问题。由于计算时间本身很短,因此数据生成大大掩盖了它。但是,总体而言,时间与您所描述的相似。不仅如此,差异更加明显:
第一种方法为 1094ms ,第二种方法为 63ms 。这使得差异17倍。

由于时间越短越小,我决定它太小而无法信任,并使用* 10生成的数据样本大小重新运行测试。它将数据生成步骤增加到一分钟,而数字变得很奇怪: 第一种方法为 1173ms ,第二种方法为 506ms 。因素仅比两个小。

我开始怀疑。为了证实我的怀疑,我进行了最后一次测试,再次将数据大小增加了10倍。结果可能会让您感到惊讶:
第一种方法为 12258ms ,第二种方法为 3646ms 。表格已经完全翻转,系数约为0.3。

在这种情况下,我的猜测是熊猫计算实际上是具有更好的优化/算法的算法。但是,由于它是大熊猫,所以它周围有很多额外的行李-为方便和坚固而付出的代价。这意味着无论数据集有多大,都需要随身携带一层“不必要的”(计算方面)的行李。

因此,即使您想要使用比熊猫大的数据集,也要以最快的方式进行操作并自己编写,以确保它们比熊猫更快。 这样一来,他们就可以保持最佳状态,并丢弃为方便起见而支付的行李。

答案 1 :(得分:0)

注意:此答案适用于熊猫版本1.0.5。其他版本可能有所不同。

tl; dr

pandas describe()方法始终会比您的版本慢,因为在幕后它使用的是几乎完全相同的逻辑,另外还有一些其他事情,例如确保数据具有正确的尺寸,对结果进行排序以及检查NaN和正确的数据类型。


更长的答案

看看source code of the describe method,我们可以看到一些事情:

  • pandas使用与您的代码相同的逻辑来计算统计信息。有关如何使用相同逻辑的示例,请参见describe()方法内的this line。这意味着熊猫describe总是变慢。
  • pandas使用s.count()计算非NaN值,但是您的代码计算所有值。让我们尝试修改您的代码,以使用相同的方法代替len()
def summarize_numeric_3(df, filt, groups, values, quantiles): 
    dfg = df[mask].groupby(groups)[values]
    dfg_stats = dfg.agg([np.mean, np.std, pd.Series.count]).stack()
    dfg_quantiles = dfg.quantile(all_quantiles)
    return dfg_stats.append(dfg_quantiles).sort_index()

%timeit -n 10 summarize_numeric_3(df, mask, groups, values, all_quantiles)

# outputs
# 48.9 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

大约需要49毫秒,而您的计算机上的版本则需要42毫秒。仅仅花费了7毫秒,就完成了相对较小的修改!

  • pandas所做的比您的代码要多得多,以确保数据具有正确的类型和形状并以漂亮的格式显示。我已经将熊猫describe方法代码提取到一个“自包含” *版本中,您可以使用here对其进行概要分析和修改(太长了,无法在此答案中发布)。对此进行分析,我发现有很大一部分时间花在了"set a convenient order for rows"上。删除该顺序可将describe的时间从530毫秒降低到489毫秒,提高了约8%。