以下代码使用两种不同的方法总结了数值数据。
第一种方法使用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个循环)
欢迎所有输入!
答案 0 :(得分:1)
我将其发布为答案,也许稍后会删除,因为它比实际答案更像是一种有根据的猜测。另外,发表评论的时间太长了。
因此,在阅读您的回答后,我要做的第一件事是在分析器中重新运行您的计时,以仔细研究该问题。由于计算时间本身很短,因此数据生成大大掩盖了它。但是,总体而言,时间与您所描述的相似。不仅如此,差异更加明显:
第一种方法为 1094ms ,第二种方法为 63ms 。这使得差异17倍。
由于时间越短越小,我决定它太小而无法信任,并使用* 10生成的数据样本大小重新运行测试。它将数据生成步骤增加到一分钟,而数字变得很奇怪: 第一种方法为 1173ms ,第二种方法为 506ms 。因素仅比两个小。
我开始怀疑。为了证实我的怀疑,我进行了最后一次测试,再次将数据大小增加了10倍。结果可能会让您感到惊讶:
第一种方法为 12258ms ,第二种方法为 3646ms 。表格已经完全翻转,系数约为0.3。
在这种情况下,我的猜测是熊猫计算实际上是具有更好的优化/算法的算法。但是,由于它是大熊猫,所以它周围有很多额外的行李-为方便和坚固而付出的代价。这意味着无论数据集有多大,都需要随身携带一层“不必要的”(计算方面)的行李。
因此,即使您想要使用比熊猫大的数据集,也要以最快的方式进行操作并自己编写,以确保它们比熊猫更快。 这样一来,他们就可以保持最佳状态,并丢弃为方便起见而支付的行李。
答案 1 :(得分:0)
注意:此答案适用于熊猫版本1.0.5。其他版本可能有所不同。
pandas describe()
方法始终会比您的版本慢,因为在幕后它使用的是几乎完全相同的逻辑,另外还有一些其他事情,例如确保数据具有正确的尺寸,对结果进行排序以及检查NaN和正确的数据类型。
看看source code of the describe
method,我们可以看到一些事情:
describe()
方法内的this line。这意味着熊猫describe
会总是变慢。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毫秒,就完成了相对较小的修改!
describe
方法代码提取到一个“自包含” *版本中,您可以使用here对其进行概要分析和修改(太长了,无法在此答案中发布)。对此进行分析,我发现有很大一部分时间花在了"set a convenient order for rows"上。删除该顺序可将describe
的时间从530毫秒降低到489毫秒,提高了约8%。