仅在 python 中使用 if 循环更快 [重复]
Posted
技术标签:
【中文标题】仅在 python 中使用 if 循环更快 [重复]【英文标题】:Faster for loop with only if in python [duplicate] 【发布时间】:2022-01-23 14:08:18 【问题描述】:我正在处理一个大数据集,基本上是这样的:
test = np.random.rand(int(1e7))-0.5
def test0(test):
return [0 if c<0 else c for c in test]
这是这样做的:
def test1(test):
for i,dat in enumerate(test):
if dat<0:
test[i] = 0
else:
test[i] = dat
return test
有没有办法修改 test0 以跳过 else 请求,所以我的工作方式如下:
def test1(test):
for i,dat in enumerate(test):
if dat<0: test[i] = 0
return test
提前致谢!
【问题讨论】:
或***.com/questions/10335090/… "which is doing this:" – 不完全是,test0
返回一个新列表,test1
修改输入数组。
【参考方案1】:
你可以试试
np.maximum(test, 0)
但是where
在我的机器上是最快的:
https://gist.github.com/axil/af6c4adb8c5634ff39ed9f3da1efaa90
其实取决于数组中负值的数量:
https://gist.github.com/axil/ce4ecdf1cb0446db47b979c37ed5fba3
结果:
– where
在大多数情况下是最快的,并且是唯一具有平坦曲线的
– putmask
是 #2
– where
只有在几乎无事可做时才比其他人快 (≤10%)
– maximum
和 clip
在整个范围内(令人惊讶地)比其他人慢,并且显然共享实现。
数组的大小通常无关紧要: https://gist.github.com/axil/2241e62977f46753caac7005268d5b28
【讨论】:
哪里不是最快的,因为它仍然是一种 if else 但矢量化 @DariuszKrynicki 在您的计算机上运行这个 gist 并自己查看 ;) 您的测试不同。在每个循环中,您执行每个函数一次,而我在每个循环中测试该函数的性能。 这很有趣。 where 在少量执行时速度更快,但在执行次数较多时速度较慢。 @DariuszKrynicki 不可能这样。 Python 不是麻木的。每行代码都有预定义数量的字节码操作。无论您执行多少次,您都将获得相同的速度。现代 CPU 有自己的怪癖和分支预测优化,但我认为我们在这项任务中并没有达到那么低的水平。如果您坚持这一点,我可以绘制时间与重复次数的关系,但我预计不会有任何惊喜。如果你愿意,你可以自己试试。【参考方案2】:只做对你来说似乎是最快的选择:
(1) test[test < 0] = 0
(2) np.where(test < 0, 0, test) # THANKS TO @antony-hatchkins
(3) test.clip(0) # THANKS TO @u12-forward
取决于你如何测试它。
当您执行每个方法 1000 次时,方法 2 是最快的。当您测量单个函数执行时,选项编号 1 是最快的。
测试:
import numpy as np
import timeit
from copy import copy
from functools import partial
def create_data():
return np.random.rand(int(1e7))-0.5
def func1(data):
data[data < 0] = 0
def func2(data):
np.putmask(data, data < 0, 0)
def func3(data):
np.maximum(data, 0)
def func4(data):
data.clip(0)
def func5(data):
np.where(data < 0, 0, data)
if __name__ == '__main__':
n_loops = 1000
test = create_data()
t1 = timeit.Timer(partial(func1, copy(test)))
t2 = timeit.Timer(partial(func2, copy(test)))
t3 = timeit.Timer(partial(func3, copy(test)))
t4 = timeit.Timer(partial(func4, copy(test)))
t5 = timeit.Timer(partial(func4, copy(test)))
print(f"func1 (x[x < 0]): timeit t1.timeit(n_loops) num test loops n_loops")
print(f"func2 (putmask): timeit t2.timeit(n_loops) num test loops n_loops")
print(f"func3 (maximum): timeit t3.timeit(n_loops) num test loops n_loops")
print(f"func4 (clip): timeit t4.timeit(n_loops) num test loops n_loops")
print(f"func5 (where): timeit t5.timeit(n_loops) num test loops n_loops")
测试结果:
func1 (x[x < 0]): timeit 7.2177265440000005 num test loops 1000
func2 (putmask): timeit 13.913492435999999 num test loops 1000
func3 (maximum): timeit 23.065230873999997 num test loops 1000
func4 (clip): timeit 22.768682354000006 num test loops 1000
func5 (where): timeit 23.844607757999995 num test loops 1000
编辑:
测试数据的不同方法[data
import numpy as np
from time import perf_counter as clock
z = np.random.rand(10**7) - 0.5
start = clock()
for i in range(100):
a = z.copy()
np.where(a<0, 0, a)
print(clock() - start)
start = clock()
for i in range(100):
a = z.copy()
a[a<0] = 0
print(clock() - start)
测试结果:
7.9247566030000005
8.021165436000002
测试3:
In [1]: import numpy as np
...: from copy import copy
...:
...:
...:
...: test = np.random.rand(int(1e7))-0.5
...:
...:
...: def func1():
...: data = copy(test)
...: data[data < 0] = 0
...:
...:
...: def func2():
...: data = copy(test)
...: np.putmask(data, data < 0, 0)
...:
...:
...: def func3():
...: data = copy(test)
...: np.maximum(data, 0)
...:
...:
...: def func4():
...: data = copy(test)
...: data.clip(0)
...:
...:
...: def func5():
...: data = copy(test)
...: np.where(data < 0, 0, data)
...:
In [2]: timeit func1
16.9 ns ± 0.117 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
In [3]: timeit func2
15.8 ns ± 0.184 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
In [4]: timeit func3
22.1 ns ± 0.287 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [5]: timeit func4
15.6 ns ± 0.0594 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
In [6]: timeit func5
16.2 ns ± 0.187 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
【讨论】:
哇,速度非常快,甚至比 np.where(test 对我来说它实际上有点慢(19 vs 7.41 ms)。 我已将clip
添加到比较中
您的基准测试不正确,因为该操作是在第一次调用 timeit 函数时应用的,然后您对一个已经非负的数组进行操作,这显然更快,而不是您要进行基准测试的东西反对。
@AntonyHatchkins 好点!每个循环 64.5 毫秒 ± 1.86 毫秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环) 每个循环 54.3 毫秒 ± 638 微秒(7 次运行的平均值 ± 标准偏差,每个 100 次循环) 68.7 毫秒 ± 665 微秒循环(7 次运行的平均值±标准偏差,每次 100 次循环)每个循环 68.5 毫秒±374 微秒(7 次运行的平均值±标准偏差,每个循环 100 次循环)每个循环 64.6 毫秒±1.57 毫秒(平均值±标准偏差。 dev. 7 次运行,每次 100 个循环)这些是我的结果(只是复制了上面的代码),按 func1 ... func5 排序确实在我的硬件上 putmask 方法是最快的。学到了很多,谢谢大家的回答!【参考方案3】:
像test.clip(min=0)
一样使用np.ndarray.clip
:
>>> test.clip(0)
array([0. , 0.11819274, 0.36379089, ..., 0. , 0.13401746,
0. ])
>>>
np.ndarray.clip
的文档:
返回一个数组,其值限制为
[min, max]
。必须给出 max 或 min 之一。
【讨论】:
根据我的基准测试clip
是最慢的。这令人惊讶,因为这是它的主要用例,而且它看起来好像是 numpy 中的一个错误,它是如此缓慢。
我同意,剪辑在较大的阵列上并不快。以上是关于仅在 python 中使用 if 循环更快 [重复]的主要内容,如果未能解决你的问题,请参考以下文章