0

我注意到,当您将数据帧乘以标量时,它比乘以 numpy 数组要慢一个数量级。缓慢度随着数据帧大小的增加而线性增加。Python 3.6,熊猫 0.24.0。

有更好的解决方法吗?

为什么在 Pandas 中乘以一个标量这么慢?对我来说,这似乎是一个性能错误。

df0 = pd.DataFrame(np.random.randn(1000, 400))

# Time to make a copy
%timeit df = df0.copy();
1.25 ms ± 5.64 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# Multiplying by a scalar is absurdly slow
%timeit df = df0.copy(); df = df * 1
64.7 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Using numpy is much faster
%timeit df = df0.copy(); df[:] = df.values * 1
3.54 ms ± 251 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# Multiplying by another dataframe with the same indexes is much faster
%timeit df = df0.copy(); df = df * df0
1.68 ms ± 5.31 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4

3 回答 3

3

有更好的解决方法吗?

如果性能很重要,您可以使用 numpy 数组和 DataFrame 构造函数:

In [219]: %timeit df = df0.copy()
2.78 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [220]: %timeit df = df0.copy(); df = df * 1
129 ms ± 3.74 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [221]: %timeit df = df0.copy(); df[:] = df.values * 1
9.35 ms ± 118 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [222]: %timeit df = df0.copy(); df = pd.DataFrame(df.values * 1,
                                                     index=df.index,
                                                     columns=df.columns)

4.88 ms ± 40.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
于 2019-08-26T13:07:02.657 回答
0

在简单的数学运算上让 pandas 比 numpy 更快

如果您安装模块 numexpr (并且不要关闭它的使用。默认情况下它是打开的),您可以使 pandas 比 numpy (单独)更好地执行。

看看这个以获得更多解释:

为什么 pandas 在简单的数学运算上比 numpy 快?

通过这样做,您上面的示例肯定会表现得更好。我测试了 pandas 的乘法和其他运算符的行为:标量、行向量、列向量和矩阵的左右乘法。

于 2020-06-18T07:32:51.740 回答
0

真的有那么糟糕吗?

在下文中,我想向您展示如何使您的代码更快。但后来我意识到,这也取决于使用的数据集的大小。尽管如此,让我们先看看你的问题。我将在我的机器上运行相同的代码以进行比较。我将为一个大数据集(你的 100 倍)和一个小数据集(你的数据集)做所有事情。

Pandas 在一些数值计算上很慢。让我们看看与等效的 numpy 操作相比有多慢。

在 Linux 32 核上使用 pandas 0.23.4,在一个 jupyter 笔记本中(在 Windows 2 核上使用 pandas 1.0.4 以获得最后的结果,在一个 jupyter 笔记本中)

请注意,所有结果都在 jupyter 笔记本中找到。我没有更改任何设置。在现实世界的条件下,结果可能会有所不同。

测量

下面是我的测量。

大数据集

import pandas as pd
import numpy as np

a = np.random.randn(10000, 4000)
df0 = pd.DataFrame(a.copy())
df = df0.copy()

请注意,我使用了更多数据,100 倍以上。此外,我使用神奇的 %%time 命令而不是 %%timeit 进行测量。

df.shape

(10000, 4000)

我运行以下单元两次。第一次运行它时,内核可能仍会加载库或编译某些东西。它会显示不同的结果。但是您可以假设,在执行简单乘法时(就像执行 groupby 和聚合时一样),DataFrame 上没有任何内部状态更改或结果缓存。

此外,我不会像您那样在每个单元格中创建副本。尽管如此,以下创建一个新的 DataFrame 并保留旧的。它不仅是左侧数据框的视图。

%%time
_ = df * 1

CPU times: user 78 ms, sys: 90.6 ms, total: 169 ms
Wall time: 24.3 ms

如果我们将生成的 DataFrame 实例分配给df指针,则单元格的执行需要更长的时间。也许是因为垃圾收集器从左侧释放了 DataFrame:笔记本中不再有对这个的引用。所以在你的性能测试中要小心,你正在测量什么!

%%time
df = df * 1

CPU times: user 84.4 ms, sys: 94.7 ms, total: 179 ms
Wall time: 31.7 ms

或使用就地乘法

%%time
df *= 1

CPU times: user 77.1 ms, sys: 97 ms, total: 174 ms
Wall time: 31 ms
对上述内容的观察:请注意,总时间高于挂钟时间(现在是你的挂钟或智能手机时钟)。这告诉我们,一些多处理或并发多线程在后台工作。

现在让我们继续讨论如何让事情变得更快。您基本上尝试了以下方法:

%%time
df[:] = df.values * 1.

CPU times: user 258 ms, sys: 234 ms, total: 492 ms
Wall time: 491 ms

这不是更快,因为在 s__setitem__上非常复杂的pandas.Dataframes 很慢。你得到的一样loc

%%time
df.loc[:] = df.values * 1.

CPU times: user 260 ms, sys: 224 ms, total: 485 ms
Wall time: 483 ms
直接访问数据

您可以直接访问数据并设置值。这似乎更快。(但你可能会遇到问题,如果你有混合数据类型DataFrame。)

%%time
df.values[...] = df.values * 1. 

CPU times: user 95.7 ms, sys: 78.5 ms, total: 174 ms
Wall time: 173 ms

甚至更快,做所有的事情。(只要df.values[...]返回对数据存储的引用。)

%%time
df.values[...] *= 1

CPU times: user 43.4 ms, sys: 0 ns, total: 43.4 ms
Wall time: 42.6 ms

能比这更快吗?让我们将其与以下乘法进行比较。首先通过乘以初始数据集,numpyarray a...

%%time
_ = a * 1

CPU times: user 45.9 ms, sys: 82.7 ms, total: 129 ms
Wall time: 128 ms

...并通过执行相应的就地乘法。

%%time
a *= 1

CPU times: user 43.5 ms, sys: 0 ns, total: 43.5 ms
Wall time: 42.9 ms

它表明,不能预期少于大约 43 毫秒。因此,直接访问数据并对其进行操作与直接对 numpy 数组进行操作一样快。

但请注意,在我的示例中,即使是最初的问题也比这更快。表明,熊猫进行了一些优化,而 numpy 则没有。奇怪的!

小数据集

在这里,我做了和你一样的观察。直接访问数据的技巧再次发挥最佳效果(df.values[...] *= 1)。

import numpy as np
import pandas as pd

a = np.random.randn(1000, 400)
df0 = pd.DataFrame(a.copy())
df = df0.copy()


df.shape
(1000, 400)

%%time
_ = df * 1
CPU times: user 4.23 ms, sys: 1.28 ms, total: 5.51 ms
Wall time: 2.83 ms


%%time
df = df * 1
CPU times: user 4.68 ms, sys: 188 µs, total: 4.87 ms
Wall time: 2.22 ms


%%time
df *= 1
CPU times: user 2.66 ms, sys: 1.76 ms, total: 4.42 ms
Wall time: 1.71 ms

%%time
df[:] = df.values * 1.
CPU times: user 4.28 ms, sys: 21 µs, total: 4.3 ms
Wall time: 3.51 ms

%%time
df.loc[:] = df.values * 1.
CPU times: user 3.77 ms, sys: 0 ns, total: 3.77 ms
Wall time: 3.13 ms

%%time
df.values[...] = df.values * 1. 
CPU times: user 2.19 ms, sys: 0 ns, total: 2.19 ms
Wall time: 1.38 ms

%%time
df.values[...] *= 1
CPU times: user 211 µs, sys: 1.05 ms, total: 1.26 ms
Wall time: 681 µs

%%time
_ = a * 1
CPU times: user 1.61 ms, sys: 0 ns, total: 1.61 ms
Wall time: 818 µs


%%time
a *= 1
CPU times: user 379 µs, sys: 950 µs, total: 1.33 ms
Wall time: 671 µs

开放式问题

看起来使用 pandas 的简单乘法有时比使用 numpy 更快。这里是上面的大数据集。

%%timeit
_ = df * df
22.8 ms ± 590 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
_ = a * a
133 ms ± 4.85 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

我调用 timeit 或 time 都没有关系。结果是一样的。

%%time
_ = df * df
CPU times: user 62.3 ms, sys: 99.2 ms, total: 162 ms
Wall time: 23.8 ms

%%time
_ = a * a
CPU times: user 57.6 ms, sys: 82.3 ms, total: 140 ms
Wall time: 139 ms

我没想到会这样。你呢?

我在带有 pandas 1.0.4 的 Windows 10、2 核上对此进行了交叉检查。结果看起来基本相同。尽管相对差异不再那么大。

%%timeit
df * df
165 ms ± 5.96 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
a * a
251 ms ± 9.71 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
于 2020-06-15T13:00:33.087 回答