接受标量或numpy数组作为参数的python函数

接受标量或numpy数组作为参数的python函数,python,arrays,function,numpy,Python,Arrays,Function,Numpy,正如标题所说,假设我想写一个符号函数(现在我们先忘掉符号(0)),显然我们期望符号(2)=1和符号(数组([-2,-2,2])=数组([-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(

正如标题所说,假设我想写一个符号函数(现在我们先忘掉符号(0)),显然我们期望符号(2)=1和符号(数组([-2,-2,2])=数组([-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
def符号(x):
y=一(x.shape)

y[x我想知道这是否是您想要的矢量化函数:

>>> 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)

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

然后集中精力操作阵列


尽管

np,您仍然需要检查x的类型。矢量化
可以用来实现这一点,但速度会很慢,因为当使用数组调用装饰函数时,它所做的一切都是在数组元素之间循环,并将标量函数应用于每个元素,即不利用numpy的速度

我发现一种方法对于涉及if else使用的函数的矢量化非常有用:

def符号非零(x):
返回np.choose(
x>0,#布尔值,用作数组的索引
[
-1,#index=0=False,即x0
])
x
是标量或数组时,这种方法可以工作,并且比python空间中的循环更快

使用
np.choose
的唯一缺点是以这种方式编写if-else逻辑不直观,代码可读性较差。当我使用它时,我会添加类似上面的注释,以便读者更容易理解所发生的事情。

这里有一个解决方案:

>>> 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]
>>定义符号(x):
…如果类型(x)==int:
…如果x>0:返回1
…否则:返回-1
……其他:
…x=np.数组(x)
…位置=np,其中(x>=0)
…负=np.其中(x>>符号(56)
1.
>>>符号(-556)
-1
>>>符号([23,4,-3,0,45,-3])
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0]
>>>符号(np.数组([23,4,-3,0,45,-3]))
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0]

这里有一个解决方案:

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)

我之前采用的方法与上一个示例非常相似,但在开始时添加了一个额外的标量检查:

def sign(x):
    if isscalar(x):
        x = (x,)
    x = asarray(x)
    y = ones(x.shape)
    y[x<0] = -1
    return y
def符号(x):
如果isscalar(x):
x=(x,)
x=阵列(x)
y=一(x.shape)

y[x处理标量和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])
>>将numpy作为np导入
>>>def符号非零(x):
返回(x>0)*1+(x<0)*-1
>>>符号非零(2)
1.
>>>符号非零(np.数组([-2,-2,2]))
数组([-1,-1,1])

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

的标量值和数组值,使用掩码索引
y
是合法的:这里的问题是
x<0
再次是标量而不是0-d数组。如果您尝试
y[asarray(x<0)]
应该可以。你考虑过使用内置的
np.sign
的可能性吗?@MarkDickinson这是一个很好的方法,但是当x是一个单一的数字时,它会给出错误,因为现在y也必须是一个单一的数字——那么y就不能被索引了…@Jaime符号函数就是一个例子,我应该使用类似分段的函数数学中的函数。顺便问一下,你知道numpy是如何实现符号函数的吗?@Taozi:是的;如果你想采用这种方法,你在开始时仍然需要
x=asarray(x)
。符号(56L)
?或
符号(np.int32(56))
符号(56)的输出是什么
?此外,整个要点是避免逻辑的重复。@shx2正是我想问的,类型判断的问题是类型太多了。可以使用if type(numpyarray)=='ndarray',但这些分支都是我想要避免的。>>>符号非零([1,2,3])给出的1应该是1,1,1>>符号非零([1,2,-3])给出的1应该是1,1-1@BHATIRSHAD,对,目前的情况是,
sign\u non\u zero
支持标量和numpy数组。要同时支持列表,您只需将
x
替换为
np.asarray(x)当你用NP.asLoad(x)替换X之后,这一个就可以完美地实现符号函数。然而,如果你有三个决定要做的话(如果你考虑符号(0)),使用选择似乎很方便。现在,选择函数是无用的,我们必须再次面对老问题——如果x可以被索引,也就是说,如果x是一个数字或数组。@陶子,如果你有三重决定,我不会说选择是无用的。你的“标量版本”中的每个
if
elif
语句可以转换为对
choose
的调用,因此您需要使用
choose
两次。在choose版本中,以if状态数为单位的标量版本的复杂性在以choose调用数为单位的版本中保持不变。这仍然具有numpy-fast的优点。很好地使用了
np。choose
。您还可以查看
np。对于更复杂的返回类型,请选择
,然后该函数返回一个需要从客户端解包的单元素数组。这很好,但是否如shx2所述,向量化函数速度慢且没有利用numpy的速度?如果使用此方法,则需要定义每个函数两次-一次是关注单个数字的简陋版本,一次是名称应该相近但不同的矢量化版本,对吗?根据文档,矢量化fn被实现为python for循环,这确实是NumPy的per的主要原因
def foo_on_scalars(x):
    # do scalar things

def foo(x):
    try:
        # assume x is an ndarray
    except AttributeError:
        foo_on_scalars(x)
def sign(x):
    if isscalar(x):
        x = (x,)
    x = asarray(x)
    y = ones(x.shape)
    y[x<0] = -1
    return y
>>> 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])