首页 > 解决方案 > Series 和 Numpy 的性能比较

问题描述

我正在构建 Numpy 和 Series 之间的性能比较表:

两个实例引起了我的注意。任何帮助都会非常有帮助。

  1. 我们说我们应该避免在 Numpy 和 Series 中使用循环,但我遇到了一种情况,其中 for 循环的性能更好

在下面的代码中,我使用 for 循环和不使用 for 循环来计算行星的密度

mass=  pd.Series([0.330, 4.87, 5.97, 0.073, 0.642, 1898, 568, 86.8, 102, 0.0146], index = ['MERCURY', 'VENUS', 'EARTH', 'MOON', 'MARS', 'JUPITER', 'SATURN', 'URANUS', 'NEPTUNE', 'PLUTO'])
diameter = pd.Series([4879, 12104, 12756, 3475, 6792, 142984, 120536, 51118, 49528, 2370], index = ['MERCURY', 'VENUS', 'EARTH', 'MOON', 'MARS', 'JUPITER', 'SATURN', 'URANUS', 'NEPTUNE', 'PLUTO'])
 
%%timeit -n 1000
 
density = mass / (np.pi * np.power(diameter, 3) /6)
 
1000 loops, best of 3: 617 µs per loop
 
%%timeit -n 1000
 
density = pd.Series()

for planet in mass.index:

    density[planet] = mass[planet] / ((np.pi * np.power(diameter[planet], 3)) / 6)
 
1000 loops, best of 3: 183 µs per loop
  1. 其次,我正在尝试使用两种方法替换 Series 中的 nan 值

为什么第一种方法工作得更快???我猜第二种方法是在 Nd 数组中转换 Series Object

sample2 = pd.Series([1, 2, 3, 4325, 23, 3, 4213, 102, 89, 4, np.nan, 6, 803, 43, np.nan, np.nan, np.nan])
 
x = np.mean(sample2)
 
x
 
%%timeit -n 10000
 
sample3 = pd.Series(np.where(np.isnan(sample2), x, sample2))
 
10000 loops, best of 3: 166 µs per loop
 
%%timeit -n 10000
 
sample2[np.isnan(sample2)] =x
 
10000 loops, best of 3: 1.08 ms per loop
 

标签: pythonpandasnumpy

解决方案


ipython控制台会话中:

In [1]: import pandas as pd
In [2]: mass=  pd.Series([0.330, 4.87, 5.97, 0.073, 0.642, 1898, 568, 86.8, 102, 0.
   ...: 0146], index = ['MERCURY', 'VENUS', 'EARTH', 'MOON', 'MARS', 'JUPITER', 'SA
   ...: TURN', 'URANUS', 'NEPTUNE', 'PLUTO'])
   ...: diameter = pd.Series([4879, 12104, 12756, 3475, 6792, 142984, 120536, 51118
   ...: , 49528, 2370], index = ['MERCURY', 'VENUS', 'EARTH', 'MOON', 'MARS', 'JUPI
   ...: TER', 'SATURN', 'URANUS', 'NEPTUNE', 'PLUTO'])
   ...: 
In [3]: mass
Out[3]: 
MERCURY       0.3300
VENUS         4.8700
EARTH         5.9700
MOON          0.0730
MARS          0.6420
JUPITER    1898.0000
SATURN      568.0000
URANUS       86.8000
NEPTUNE     102.0000
PLUTO         0.0146
dtype: float64
In [4]: diameter
Out[4]: 
MERCURY      4879
VENUS       12104
EARTH       12756
MOON         3475
MARS         6792
JUPITER    142984
SATURN     120536
URANUS      51118
NEPTUNE     49528
PLUTO        2370
dtype: int64

您的密度计算会创建一个具有相同索引的系列。这里系列的索引匹配,但我认为总的来说pandas能够匹配索引。

In [5]: density = mass / (np.pi * np.power(diameter, 3) /6)
In [6]: density
Out[6]: 
MERCURY    5.426538e-12
VENUS      5.244977e-12
EARTH      5.493286e-12
MOON       3.322460e-12
MARS       3.913302e-12
JUPITER    1.240039e-12
SATURN     6.194402e-13
URANUS     1.241079e-12
NEPTUNE    1.603427e-12
PLUTO      2.094639e-12
dtype: float64
In [7]: timeit density = mass / (np.pi * np.power(diameter, 3) /6)
532 µs ± 437 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

由于索引匹配,我们可以使用numpyvalues 数组获得相同的数字:

In [8]: mass.values/(np.pi * np.power(diameter.values, 3)/6)
Out[8]: 
array([5.42653818e-12, 5.24497707e-12, 5.49328558e-12, 3.32246038e-12,
       3.91330208e-12, 1.24003876e-12, 6.19440202e-13, 1.24107933e-12,
       1.60342694e-12, 2.09463905e-12])
In [9]: timeit mass.values/(np.pi * np.power(diameter.values, 3)/6)
11.5 µs ± 67.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这要快得多。 numpy没有花时间来匹配索引。它也没有制作新的系列。

您的迭代方法:

In [11]: %%timeit
    ...: density = pd.Series(dtype=float)
    ...: for planet in mass.index:
    ...:     density[planet] = mass[planet] / ((np.pi * np.power(diameter[planet],
    ...: 3)) / 6)
    ...: 

7.36 ms ± 312 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这要慢得多。

出于好奇,让我们初始化一个系列并用numpy计算填充它:

In [18]: %%timeit
    ...: density = pd.Series(index=mass.index, dtype=float)
    ...: density[:] = mass.values/(np.pi * np.power(diameter.values, 3)/6)
241 µs ± 8.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

比 [7] 好 2 倍,但仍然比 pure 慢很多numpy。虽然pandas使用numpy数组 - 在这里我认为两者index都是values数组。但是pandas相对于纯numpy代码确实增加了显着的开销。


推荐阅读