【问题标题】:Why is numpy.vectorize() changing the division output of a scalar function?为什么 numpy.vectorize() 会改变标量函数的除法输出?
【发布时间】:2020-03-01 09:12:13
【问题描述】:

当我使用 numpy 对函数进行矢量化时,我得到了一个奇怪的结果。

import numpy as np
def scalar_function(x, y):
    """ A function that returns x*y if x<y and x/y otherwise
    """
    if x < y :
        out = x * y 
    else:
        out = x/y 
    return out

def vector_function(x, y):
    """
    Make it possible to accept vectors as input
    """
    v_scalar_function = np.vectorize(scalar_function)
    return v_scalar_function(x, y)

我们有

scalar_function(4,3)
# 1.3333333333333333

为什么矢量化版本会给出这种奇怪的输出?

vector_function(np.array([3,4]), np.array([4,3]))
[12  1]

虽然对矢量化版本的调用工作正常:

vector_function(np.array([4,4]), np.array([4,3]))
[1.         1.33333333]

阅读numpy.divide

注意事项 地板除法运算符 // 在 Python 2.2 中添加了 // 和 / 等效运算符。 / 的默认楼层除法操作可以用 from __future__ 导入除法替换为真除法。 在 Python 3.0 中,// 是地板除法运算符和 / 真正的除法运算符。 true_divide(x1, x2) 函数相当于 Python 中的真正除法。

让我觉得这可能是与 python2 相关的遗留问题? 但我使用的是 python 3!

【问题讨论】:

  • 文档说“输出类型是通过评估输入的第一个元素来确定的,除非它被指定”。我的猜测是,对于整个返回数组,第一个实例中整数类型的乘法会自动转换为 int 类型,而除法,即使是 4/4 的结果也会自动转换为浮点数
  • 顺便说一句;我看到你主要习惯于 R。只要检查一下你是否知道 vectorize 是一个便利函数,它不会增加 python for 循环的速度,应该尽可能避免

标签: python numpy division


【解决方案1】:

numpy.vectorize 状态的文档:

输出类型是通过计算的第一个元素来确定的 输入,除非指定

由于您没有指定返回数据类型,并且第一个示例是整数乘法,所以第一个数组也是整数类型并对值进行四舍五入。相反,当第一个操作是除法时,数据类型会自动向上转换为浮点数。您可以通过在 vector_function 中指定一个 dtype 来修复您的代码(对于这个问题,它不一定必须像 64 位一样大):

def vector_function(x, y):
    """
    Make it possible to accept vectors as input
    """
    v_scalar_function = np.vectorize(scalar_function, otypes=[np.float64])
    return v_scalar_function(x, y)

另外,您还应该从相同的文档中注意到,numpy.vectorize 是一个便利函数,基本上只是包装了一个 Python for 循环,因此在它提供任何真正的性能提升的意义上没有向量化。

对于这样的二元选择,更好的整体方法是:

def vectorized_scalar_function(arr_1, arr_2):
    return np.where(arr_1 < arr_2, arr_1 * arr_2, arr_1 / arr_2)

print(vectorized_scalar_function(np.array([4,4]), np.array([4,3])))
print(vectorized_scalar_function(np.array([3,4]), np.array([4,3])))

上述内容应该快几个数量级,并且(可能是巧合,而不是依赖一个硬性规则)不会遇到结果的类型转换问题。

【讨论】:

  • 感谢您的回答和使用np.where 的建议。这类似于 R 函数ifelse。您写道,np.where 的输出不受类型转换问题的影响,但它“可能是巧合”而不是“依赖规则”。任何见解,将在哪里指定 np.where 类型输出的规则?
  • @PaulRougieux 绝大多数方法都允许您指定dtype 的输出。至于一般的铸造规则,如果留给 numpy,那就更棘手了。我记得曾经有一个优先表,但我不确定存在 / 是否仍然相关。困扰我几次的一个大问题是,如果保留默认值,int 数组在 Windows 上将是 32 位,并且可以在 sum 之类的东西上静默溢出
【解决方案2】:

检查哪些statemets被触发:

import numpy as np

def scalar_function(x, y):
    """ A function that returns x*y if x<y and x/y otherwise
    """
    if x < y :
        print('if x: ',x)
        print('if y: ',y)
        out = x * y 
        print('if out', out)
    else:
        print('else x: ',x)
        print('else y: ',y)
        out = x/y
        print('else out', out)

    return out

def vector_function(x, y):
    """
    Make it possible to accept vectors as input
    """
    v_scalar_function = np.vectorize(scalar_function)
    return v_scalar_function(x, y)


vector_function(np.array([3,4]), np.array([4,3]))

if x:  3
if y:  4
if out 12
if x:  3
if y:  4
if out 12
else x:  4
else y:  3
else out 1.3333333333333333 # <-- seems that the value is calculated correctly, but the wrong dtype is returned

所以,你可以重写标量函数:

def scalar_function(x, y):
    """ A function that returns x*y if x<y and x/y otherwise
    """
    if x < y :
        out = x * y 
    else:
        out = x/y
    return float(out)


vector_function(np.array([3,4]), np.array([4,3]))
array([12.        ,  1.33333333])

【讨论】:

    猜你喜欢
    • 2013-10-29
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    相关资源
    最近更新 更多