有没有一种方法可以在python中优化cumprod?

时间:2018-10-05 09:36:47

标签: pandas numpy

我有一个熊猫数据帧df,并希望在函数中执行以下计算。到目前为止最长的那条生产线是一条cumprod。我想知道是否有加速的方法?就像在numpy中一样,它们是获得相同结果的不同方法,例如np.inner vs np.einsum,我想知道是否可以在这里做类似的事情。

import pandas as pd

In [122]: import numpy as np

In [123]: df = pd.DataFrame(np.random.randn(100000, 1000))

In [124]: %time ((1+df).cumprod(axis=0)-1)
CPU times: user 5.22 s, sys: 884 ms, total: 6.1 s
Wall time: 6.12 s

2 个答案:

答案 0 :(得分:1)

您可以使用NumPy而不是Pandas进行计算。 对于您的输入大小,这大约为5%,虽然不令人兴奋,但总比没有好。对于较小的输入,收益要大得多。

import pandas as pd
import numpy as np

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

x = ((1 + df).cumprod(axis=0) - 1)
y = np.cumprod(1 + arr, axis=0) - 1

print(np.allclose(x, y))

鉴于这是相同的结果,时间是:

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

%timeit ((1 + df).cumprod(axis=0) - 1)
# 3.64 s ± 76.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.cumprod(1 + arr, axis=0) - 1
# 3.42 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

显示输入的上述速度增益。

对于较小的输入,相差较大,例如:

arr = np.random.randn(1000, 10)
df = pd.DataFrame(arr)

%timeit ((1 + df).cumprod(axis=0) - 1)
# 469 µs ± 4.13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit np.cumprod(1 + arr, axis=0) - 1
# 36.6 µs ± 427 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

表明在这种情况下,在NumPy中执行计算的速度比在熊猫中快13倍。


编辑:

如@hpaulj所建议的,np.multiply.accumulate()可以变得更快一些。

# for shape = (100000, 1000)
%timeit np.multiply.accumulate(1 + arr, axis=0) - 1
# 3.38 s ± 79.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

,对于较小的输入:

# for shape = (1000, 10)
%timeit np.multiply.accumulate(1 + arr, axis=0) - 1
# 35.8 µs ± 423 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

但是,像往常一样,这类微基准测试应带有一粒盐,尤其是在观察到如此小的差异时。

答案 1 :(得分:1)

如果您愿意使用其他模块来加快计算速度,我建议您使用import java.text.DecimalFormat; import java.util.Scanner; public class Calculate { public static void main(String[] args) { DecimalFormat df = new DecimalFormat("#.#####"); Scanner scanner = new Scanner(System.in); double n = scanner.nextDouble(); double x = scanner.nextDouble(); double factorial = 1; double pow = 1; double S = 0; double result; for (int i = 1; i <= n; i++) { factorial *= i; pow *= x; result = (factorial / pow); S += result; } double finalResult = (S + 1); String formatted = df.format(finalResult); System.out.println(formatted); } } 。 Numba将python代码编译为LLVM,并且专门旨在使用numba加速数值计算。

由于numpy尚不支持将numba之类的kwargsaxis=0一起使用,因此您的代码将如下所示:

np.cumprod

一些时间表明,numba大约比在DataFrame上使用cumprod快4倍,比使用numpy快3.7倍:

import numpy as np
import pandas as pd
import numba as nb

@nb.njit(parallel=True)
def nb_cumprod(arr):
    y = np.empty_like(arr)
    for i in range(arr.shape[1]):
        y[:, i] = np.cumprod(1 + arr[:, i]) - 1
    return y

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

x = ((1 + df).cumprod(axis=0) - 1)
y = np.cumprod(1 + arr, axis=0) - 1
z = nb_cumprod(arr)

print(np.allclose(x, z))

您可以使用诸如%timeit ((1 + df).cumprod(axis=0) - 1) # 6.83 s ± 482 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit np.cumprod(1 + arr, axis=0) - 1 # 6.38 s ± 509 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit nb_cumprod(arr) # 1.71 s ± 158 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 之类的其他选项来进一步提高性能,但这会产生略微的不同结果。