【问题标题】:Pandas Dataframe groupby describe 8x ~slower than computing separatlyPandas Dataframe groupby 描述 8x ~ 比单独计算慢
【发布时间】:2018-12-03 11:48:45
【问题描述】:

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

第一种方法使用 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)

我的电脑上的时间是:

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

使用两步法: 每个循环 105 毫秒 ± 490 微秒(平均值 ± 标准偏差,7 次运行,每次 10 次循环)

欢迎所有输入!

【问题讨论】:

  • 我也有同样的问题。甚至比一个一个单独做 8 次还要慢。我非常怀疑他们将它们实现为只是工作逻辑;未针对运行时进行优化。
  • 刚才试了一下,结果差不多。仍然没有更新。
  • 由于 .describe() 包括 .count() 但不包括您的两步方法,我们可能会看到与stackoverflow.com/questions/63314312 报告的相同问题
  • 两步法包括使用 len 函数进行计数。我刚才还尝试在 agg() 的函数列表中使用“计数”,但仍然看到基于描述的方法比单独计算相同的分位数和组统计数据慢 13.9 倍。这是熊猫 1.1.0。
  • countlen 不同,我相信。 len 将为您提供总行数。 count返回非空值的个数,

标签: python pandas quantile percentile describe


【解决方案1】:

有根据的猜测

我会将其发布为答案,可能稍后会删除,因为它更多的是有根据的猜测而不是实际答案。另外,评论有点太长了。

因此,在阅读您的答案后,我做的第一件事就是在分析器中重新运行您的时间,以仔细查看问题。由于计算本身的时间相当短,因此它被数据生成所掩盖。但是总的来说,时间与您描述的相似。不仅如此,差异变得更加明显:第一种方法为
1094ms,第二种方法为 63ms。这产生了 17 倍的差异。

由于较低的时间相当小,我认为它太小而无法信任,并使用 *10 生成的数据样本大小重新运行测试。它将数据生成步骤提高到一分钟,而且数字变得很奇怪: 第一种方法为 1173ms,第二种方法为 506ms。系数只比两个差一点。

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

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

因此,如果您希望即使在您的大小数据集上也比 pandas 更快,请自己进行操作并自己编写它们 - 尽可能以最直接的方式。 这将保持他们的优化并丢弃为方便而支付的行李。

【讨论】:

    【解决方案2】:

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

    tl;博士

    pandas describe() 方法总是比你的版本慢,因为在底层它使用几乎完全相同的逻辑,加上其他一些事情,比如确保数据具有正确的维度、排序结果和检查 NaN以及正确的数据类型。


    更长的答案

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

    • pandas 使用与您的代码相同的逻辑来计算统计信息。请参阅describe() 方法中的this line,了解它如何使用相同逻辑的示例。这意味着 pandas 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 不仅可以确保数据具有正确的类型和形状,而且还可以以漂亮的格式呈现数据,而不仅仅是您的代码。我已将 pandas describe 方法代码提取到“自包含”* 版本中,您可以使用 here 对其进行分析和修改(太长,无法在此答案中发布)。对此进行分析,我发现大部分时间都花在了"set a convenient order for rows" 上。删除该排序后,describe 的时间缩短了约 8%,从 530 毫秒降至 489 毫秒。

    【讨论】:

      猜你喜欢
      • 2022-06-13
      • 1970-01-01
      • 2015-11-07
      • 2019-06-02
      • 2020-09-11
      • 1970-01-01
      • 2022-11-21
      • 2020-12-25
      • 2019-01-26
      相关资源
      最近更新 更多