【问题标题】:Vectorizing CONDITIONAL logic in Python?在 Python 中向量化条件逻辑?
【发布时间】:2019-09-11 20:21:17
【问题描述】:

试图与时俱进并学习 Python 的老派 c 程序员。努力了解如何有效地使用矢量化来替换 for 循环。我得到了 Python 可以在单个语句中对整个矩阵执行数学函数的基本概念,这真的很酷。但我很少处理数学关系。几乎我所有的 for 循环都应用了条件逻辑。

这里有一个非常简单的例子来说明这个概念:

import numpy as np

# Initial values
default = [1,2,3,4,5,6,7,8]
# Override values should only replace initial values when not nan
override = [np.nan,np.nan,3.5,np.nan,5.6,6.7,np.nan,8.95] 

# I wish I knew how to replace this for loop with a single line of vectorized code
for i in range(len(default)):
    if(np.isnan(override[i])==False): #Only override when override value is other than nan
        default[i]=override[i]

default

我有一种感觉,可以通过一个 python 语句消除 for 循环,该语句只用不是 np.nanoverride 的值覆盖 default 的值。但我不知道该怎么做。

这只是一个简单的例子来说明这个概念。 我真正的问题是向量化是否通常有助于用条件逻辑替换 for 循环,或者它是否仅适用于数学关系,实现它们的好处和方法是显而易见的。 我所有真实的代码挑战要复杂得多,条件逻辑也比简单的“仅在非 nan 时才使用此值”更复杂。

我在网上找到了数百篇关于如何在 Python 中使用向量化的文章,但它们似乎都专注于在 for 循环中替换 数学计算。我所有的 for 循环都涉及条件逻辑。矢量化可以帮助我还是我想在圆孔中安装一个方形钉?

谢谢!

【问题讨论】:

  • 我认为@EliadL 的回答很好,它说明了为什么“这取决于”你是否应该矢量化。您通常会从矢量化中获得性能提升——即您的代码将运行得更快。但是,对于可读性,有一些话要说。对我来说,代码中的原始 for 循环更具可读性和可理解性。 OTOH,您养成了每天使用 numpy 的习惯,那么 np.where 看起来完全正常,您会受益于速度和可读性。

标签: python numpy


【解决方案1】:

首先,矢量化版本:

override_is_not_nan = np.logical_not(np.isnan(override))
np.where(override_is_not_nan, override, default)

至于您的真正问题,矢量化对于多处理很有用。
不仅适用于多核 CPU。
考虑到当今的 GPU 有数千个内核,使用具有类似代码的张量可以使其运行速度更快。
快多少?这取决于您的数据、实施和硬件。
显然,向量化与 GPU 的结合是深度学习领域取得巨大进步的部分原因。

【讨论】:

  • 非常感谢。但我真正的“真正问题”是如何了解这一点。您确切地知道如何使用 np.where 来实现这个特殊的解决方案。我真的很难吸收如何在很多情况下编写矢量化代码的更大图景。我在网上能找到的所有文章都是关于数学关系的。任何想法如何/在哪里学习“思考矢量化”?谢谢!!!
  • @eriktownsend 在很大程度上,如果你摆脱了非向量化的有线思维模式,这就像学习一门新语言。所以,只需练习并强迫自己避免循环! NumPy 作为一个拥有矢量化 ufunc、工具等的环境,一旦您通过了语法(使用文档再次很容易),就为我们提供了一种简单的方法。
  • 我是在 CS 课程中学习 MATLAB 时第一次接触矢量化的。见some examples here。后来我在 Andrew Ng 的两个在线课程中再次遇到它:Machine LearningDeep Learning(链接到相关视频,它们是免费提供的)。使用numpy,我使用的越多,我越改进,并在谷歌上搜索或阅读文档。
  • 非常感谢您提供此信息。超级有用!
【解决方案2】:

列表推导通常是 Python 中 for 循环的首选一行替代方案。也可以在理解中加入条件。

在这种特定情况下,我们通过将defaultoverride 的元素压缩在一起并根据条件检查替换default 的值来迭代它们。

>>> [y if not(np.isnan(y)) else x for (x,y) in zip(default, override)]

[1, 2, 3.5, 4, 5.6, 6.7, 7, 8.95]

要回答您关于矢量化和加速的更广泛的问题,很遗憾,答案是视情况而定。在某些情况下,简单的for 循环比其矢量化循环表现更好。例如,列表推导只是为了提高代码的可读性,而不是提供严重的加速。

this 问题的答案更详细地解决了这个问题。

【讨论】:

  • 有趣的事实:np.nan 没有自我平等,即。 np.nan == np.nan 返回 False。所以你也可以把代码替换成[x if y==y else y for (x,y) in zip(default, override)]
【解决方案3】:

首先找到非 nan 值所在的索引。
default 数组中的索引值替换为 override 数组值。

import numpy as np
np_default = np.array(default).asdtype(float) # Convert np_default to numpy array with float values
non_nan_indices = np.where(~np.isnan(override)) # Get non nan indices
np_default[non_nan_indices] = np.array(override)[non_nan_indices] # Replacing the values at non-nan indices
np_default # Returns array([1.  , 2.  , 3.5 , 4.  , 5.6 , 6.7 , 7.  , 8.95])

向量化是 numpy 为您提供帮助的地方,它利用了数组的类型化特性,从而实现了更快的操作。详情请见 [BlogPost]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-20
    • 2018-08-13
    • 2012-05-11
    • 2019-08-17
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多