一个python函数,它接受标量或numpy数组作为参数

时间:2014-10-24 06:31:30

标签: python arrays function numpy

正如标题所说,假设我想写一个符号函数(让我们暂时忘记符号(0)),显然我们期望sign(2)= 1和sign(array([ - 2,-2,2]] ))= array([ - 1,-1,1])。但是,以下函数不起作用,因为它无法处理numpy数组。

def sign(x):
    if x>0: return 1
    else: return -1

下一个函数不起作用,因为x只有一个形状成员,如果它只是一个数字。即使使用y = x * 0 + 1这样的技巧,y也不会有[]方法。

def sign(x):
    y = ones(x.shape)
    y[x<0] = -1
    return y

即使是另一个问题(how can I make a numpy function that accepts a numpy array, an iterable, or a scalar?)的想法,当x是单个数字时,下一个函数将不起作用,因为在这种情况下x.shape和y.shape只是()并且索引y是非法的。

def sign(x):
    x = asarray(x)
    y = ones(x.shape)
    y[x<0] = -1
    return y

唯一的解决方案似乎是首先确定x是数组还是数字,但我想知道是否有更好的东西。如果你有很多像这样的小函数,编写分支代码会很麻烦。

8 个答案:

答案 0 :(得分:3)

np.vectorize可以用来实现它,但是会很慢,因为当你用一个数组调用你的装饰函数时,它会循环遍历数组元素并将标量函数应用于每个元素,即不是利用numpy的速度。

我发现一个有用的矢量化函数的方法涉及if-else正在使用np.choose

def sign_non_zero(x):
    return np.choose(
        x > 0,  # bool values, used as indices to the array
        [
            -1, # index=0=False, i.e. x<=0
            1,  # index=1=True, i.e. x>0
        ])

x是标量或数组时,这种方法有效,并且比在python空间中循环更快。

使用np.choose的唯一缺点是以这种方式编写if-else逻辑并不直观,而且代码的可读性较差。当我使用它时,我会包含上述评论,以便读者更容易理解正在发生的事情。

答案 1 :(得分:2)

我想知道你想要的是 矢量化功能

>>> import numpy as NP

>>> def fnx(a):
        if a > 0:
            return 1
        else:
            return -1

>>> vfnx = NP.vectorize(fnx)

>>> a = NP.random.randint(1, 10, 5)
array([4, 9, 7, 9, 2])

>>> a0 = 7

>>> vfnx(a)
array([1, 1, 1, 1])

>>> vfnx(a0)
array(1)

答案 2 :(得分:1)

这是一个解决方案:

import numpy as np

def sign(x):
    y = np.ones_like(x)
    y[np.asarray(x) < 0] = -1

    if isinstance(x, np.ndarray):
        return y
    else:
        return type(x)(y)

这应该返回与输入相同类型的值。例如sign(42)给出1sign(42.0)给出1.0。如果你给它一个ndarray,它将像np.sign一样工作。

通常,您可以继续假设您的输入是ndarray。如果您尝试访问ndarray具有的属性或方法,但您的输入没有,那么您将回退到以标量类型运行。使用例外来实现这一点。例如:

def foo_on_scalars(x):
    # do scalar things

def foo(x):
    try:
        # assume x is an ndarray
    except AttributeError:
        foo_on_scalars(x)

答案 3 :(得分:0)

您可以先将数字转换为单元素数组,

然后专注于对数组进行操作。

你仍然需要检查x的类型

答案 4 :(得分:0)

这是一个解决方案:

>>> def sign(x):
...      if type(x)==int:
...          if x>0: return 1
...          else: return -1 
...      else:
...          x=np.array(x)
...          pos=np.where(x>=0)
...          neg=np.where(x<0)
...          res=np.zeros(x.shape[0])
...          res[pos]=1
...          res[neg]=-1
...          return res.tolist()
... 
>>> sign(56)
1
>>> sign(-556)
-1
>>> sign([23,4,-3,0,45,-3])
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0]
>>> sign(np.array([23,4,-3,0,45,-3]))
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0]

答案 5 :(得分:0)

我之前采用的方法很像你的上一个例子,但在开头添加额外的标量检查:

def sign(x):
    if isscalar(x):
        x = (x,)
    x = asarray(x)
    y = ones(x.shape)
    y[x<0] = -1
    return y

答案 6 :(得分:0)

处理标量和numpy数组的简单解决方案:

>>> import numpy as np

>>> def sign_non_zero(x):
        return (x > 0) * 1 + (x < 0) * -1

>>> sign_non_zero(2)
1

>>> sign_non_zero(np.array([-2, -2, 2]))
array([-1, -1,  1])

答案 7 :(得分:0)

numpy函数自然地处理标量或数组输入,并在输出中保留形状。因此,总是最好找到执行此工作的numpy函数。在这种情况下,如上所述,函数应为np.sign。对于不同的逻辑,可以使用np.where(x>0, 1, -1),它适用于x的标量和数组值。