仅在 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%) – maximumclip 在整个范围内(令人惊讶地)比其他人慢,并且显然共享实现。

数组的大小通常无关紧要: 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 循环更快 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

更好/更快地循环遍历集合或列表?

python中的if循环怎么样?

在while循环中动态回显字符串[重复]

在 Python 中使用数组更快的 for 循环

python中循环语句

07_Python的控制判断循环语句1(if判断for循环)_Python编程之路