这里似乎有一些毫无价值的东西。
First, df_a.cumsum()
默认为axis=0
(Pandas 没有在一次调用中对整个 DataFrame 求和的概念),而 NumPy 调用默认为axis=None
。因此,通过在一个操作上指定一个轴并有效地展平另一个操作,您可以将苹果与橙子进行比较。
也就是说,您可以比较三个调用:
>>> np.cumsum(df_a, axis=0)
>>> df_a.cumsum()
>>> val.cumsum(axis=0) # val = df_a.values
在最后的通话中,val
是底层的 NumPy 数组,我们不计算得到.values
运行时的属性。
因此,如果您在 IPython shell 中工作,请使用以下命令进行行分析%prun a try:
>>> %prun -q -T pdcumsum.txt df_a.cumsum()
>>> val = df_a.values
>>> %prun -q -T ndarraycumsum.txt val.cumsum(axis=0)
>>> %prun -q -T df_npcumsum.txt np.cumsum(df_a, axis=0)
-T
将输出保存为文本,以便您可以查看所有三个相互匹配的内容。这就是你最终得到的结果:
-
df_a.cumsum()
: 186函数调用,0.022 秒。其中 0.013 花在numpy.ndarray.cumsum()
。 (我的猜测是,如果没有 NaN,那么nancumsum()
不需要,但请不要引用我的话)。另一个块用于复制数组。
-
val.cumsum(axis=0)
:5 个函数调用,0.020 秒。不进行任何复制(尽管这不是就地操作)。
-
np.cumsum(df_a, axis=0)
: 204函数调用,0.026 秒。可以这么说,将 Pandas 对象传递给顶级 NumPy 函数似乎最终会调用 Pandas 对象上的等效方法,该方法会经历一大堆开销,然后重新调用 NumPy 函数。
现在,与%timeit
,您在这里只拨打 1 次电话,就像您在%time
,所以我不会太依赖于相对时间差异%prun
;也许比较内部函数调用是有用的。但在这种情况下,当您为两者指定相同的轴时,即使 Pandas 的调用数量使 NumPy 的调用数量相形见绌,时间差异实际上并没有那么大。换句话说,在这种情况下,所有三个调用的时间都由np.ndarray.cumsum()
,辅助 Pandas 调用不会占用太多时间。在其他情况下,辅助 Pandas 调用确实会消耗更多的运行时间,但这似乎不是其中之一。
大局观——如承认韦斯·麦金尼,
从索引到汇总统计的相当简单的操作可能会在到达最低层计算之前经过多层脚手架。
您可能会说,权衡是灵活性和增加的功能。
最后一个细节:在 NumPy 中,您可以避免一点点开销通过调用实例方法ndarray.cumsum()
而不是顶层函数np.cumsum()
,因为后者最终结束routing到前者。但正如一位智者曾经说过的,过早的优化是万恶之源。
以供参考:
>>> pd.__version__, np.__version__
('0.22.0', '1.14.0')