Python 熊猫数据帧分组比单独计算慢8倍

Python 熊猫数据帧分组比单独计算慢8倍,python,pandas,quantile,percentile,describe,Python,Pandas,Quantile,Percentile,Describe,下面的代码使用两种不同的方法总结数值数据 第一种方法使用Dataframe().descripe()并传递一些特定的额外百分位数 第二种方法分别计算汇总统计数据(均值、标准差、N),将其叠加,计算相同的分位数,然后将二者相加并按索引排序,因此结果基本上与第一种方法相同 有一些小的命名差异,我们可以清理afterword的,因为总结的数据很小,这是非常快的 事实证明,在本例中,使用descripe函数的速度大约慢了8倍 import pandas as pd import nump

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

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

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

有一些小的命名差异,我们可以清理afterword的,因为总结的数据很小,这是非常快的

事实证明,在本例中,使用descripe函数的速度大约慢了8倍

    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)
我正在寻找原因,也许是关于任何其他方法的建议,这些方法可能会加快速度(过滤器、组、值)都会从UI传递到tornado服务,因此速度很重要,因为用户正在等待结果,而且数据可能比本例中的数据还要大

    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 ms±8.9 ms(7次运行的平均值±标准偏差,每个回路1次)

使用两步法: 每个回路105 ms±490µs(7次运行的平均值±标准偏差,每个10个回路)

欢迎所有的输入

有根据的猜测 我将把这篇文章作为一个答案发表,也许以后会被删除,因为它更像是一个有根据的猜测,而不是一个实际的答案。此外,它是一个有点太长的评论

因此,在阅读了你的答案后,我做的第一件事是在剖析器中重新运行计时,以便更仔细地研究这个问题。由于计算本身的时间很短,因此数据生成的影响很大。然而,总的来说,时间与你描述的相似。不仅如此,这种差异还变得更加明显:
第一次进近时1094ms,第二次进近时63ms。这就产生了17倍的差异

由于较低的时间非常小,我决定它太小,不值得信任,并使用*10生成的数据样本大小重新运行测试。它将数据生成步骤提高到了一分钟,数字变得很奇怪: 第一次进近为1173ms,第二次进近为506ms。系数仅比二稍差

我开始怀疑什么。为了证实我的怀疑,我再次运行了最后一个测试,将数据大小增加了10倍。结果可能会让您大吃一惊:
第一次进近为12258ms
,第二次进近为3646ms。表已经完全变了,系数约为0.3

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

因此,如果您想在您这样大的数据集上比pandas更快,请自己编写它们的操作,并尽可能以最直接的方式编写。
这将保持他们的优化和丢弃行李的便利性。

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

tl;博士 pandas
Descripte()
方法总是比您的版本慢,因为在引擎盖下,它使用几乎完全相同的逻辑,加上其他一些事情,例如确保数据具有正确的维度、排序结果以及检查NaN和正确的数据类型


更长的答案 看一看,我们可以看到一些东西:

  • pandas使用与代码相同的逻辑来计算统计数据。有关如何使用相同逻辑的示例,请参见
    descripe()
    方法内部。这意味着panda
    description
    将始终较慢
  • pandas使用
    s.count()
    计算非NaN值,但您的代码计算所有值。让我们尝试修改您的代码以使用相同的方法,而不是
    len()
您的版本在我的机器上需要约49毫秒,而在我的机器上需要42毫秒。仅此相对较小的修改就需要7毫秒

  • pandas在确保数据具有正确的类型和形状以及以漂亮的格式呈现数据方面所做的工作远不止您的代码。我已经将pandas
    descripe
    方法代码提取到一个“独立的”*版本中,您可以对其进行分析和修改(太长了,无法在此答案中发布)。在分析这一点时,我发现大部分时间都花在了测试上。删除该顺序将
    descripe
    定时提高了约8%,从530毫秒降至489毫秒

我也有同样的问题。它甚至比8次单独一个一个地做还要慢。我非常怀疑他们只是按照工作逻辑来实施这些措施;未针对运行时进行优化。刚刚尝试,得到了类似的结果。仍然没有更新。由于.descripe()包含.count(),但不是您的两步方法,因此我们可能会看到与使用len函数包含计数的两步方法报告的问题相同的问题。我刚才还尝试在agg()中的函数列表中使用“count”,但仍然看到基于描述的方法比单独计算相同的分位数和组统计数据慢13.9倍。熊猫1.1.0就是这样。
count
len
不同,我相信
len
将为您提供总行数<代码>计数返回非空值的数目,