12

假设我有这个:

def incrementElements(x):
   return x+1

但我想对其进行修改,使其可以采用 numpy 数组、可迭代对象或标量,并将参数提升为 numpy 数组并将每个元素加 1。

我怎么能那样做?我想我可以测试参数类,但这似乎是个坏主意。如果我这样做:

def incrementElements(x):
   return numpy.array(x)+1

它适用于数组或可迭代对象,但不适用于标量。这里的问题是,numpy.array(x)对于标量 x 会产生一些奇怪的对象,这些对象包含在一个 numpy 数组中,但不是“真正的”数组;如果我向其添加标量,则结果将降级为标量。

4

4 回答 4

9

你可以试试

def incrementElements(x):
    x = np.asarray(x)
    return x+1

np.asarray(x)等价于np.array(x, copy=False),意味着标量或可迭代对象将转换为 a ndarray,但如果x已经是 a ndarray,则不会复制其数据。

如果您传递一个标量并想要一个ndarray作为输出(不是标量),您可以使用:

def incrementElements(x):
    x = np.array(x, copy=False, ndmin=1)
    return x

ndmin=1参数将强制数组至少具有一维。ndmin=2至少用于 2 个维度,依此类推。您也可以使用它的等价物np.atleast_1d(或np.atleast_2d2D 版本...)

于 2012-09-29T13:38:00.060 回答
3

只要您的函数专门使用 ufunc(或类似的东西)来隐式循环输入值,Pierre GM 的答案就很好。如果您的函数需要迭代输入,那么np.asarray就做得不够,因为您不能迭代 NumPy 标量:

import numpy as np

x = np.asarray(1)
for xval in x:
    print(np.exp(xval))

Traceback (most recent call last):
  File "Untitled 2.py", line 4, in <module>
    for xval in x:
TypeError: iteration over a 0-d array

如果您的函数需要遍历输入,则可以使用np.atleast_1dnp.squeeze(请参阅数组操作例程 — NumPy 手册)。我包含了一个aaout("Always Array OUT") 参数,因此您可以指定是否希望标量输入产生单元素数组输出;如果不需要,它可以被丢弃:

def scalar_or_iter_in(x, aaout=False):
    """
    Gather function evaluations over scalar or iterable `x` values.

    aaout :: boolean
        "Always array output" flag:  If True, scalar input produces
        a 1-D, single-element array output.  If false, scalar input
        produces scalar output.
    """
    x = np.asarray(x)
    scalar_in = x.ndim==0

    # Could use np.array instead of np.atleast_1d, as follows:
    # xvals = np.array(x, copy=False, ndmin=1)
    xvals = np.atleast_1d(x)
    y = np.empty_like(xvals, dtype=float)  # dtype in case input is ints
    for i, xx in enumerate(xvals):
        y[i] = np.exp(xx)  # YOUR OPERATIONS HERE!

    if scalar_in and not aaout:
        return np.squeeze(y)
    else:
        return y


print(scalar_or_iter_in(1.))
print(scalar_or_iter_in(1., aaout=True))
print(scalar_or_iter_in([1,2,3]))


2.718281828459045
[2.71828183]
[ 2.71828183  7.3890561  20.08553692]

当然,对于求幂,您不应该像这里那样显式迭代,但是使用 NumPy ufunc 可能无法表达更复杂的操作。如果您不需要迭代,但希望对标量输入是否产生单元素数组输出进行类似控制,则函数的中间部分可能更简单,但返回必须处理np.atleast_1d

def scalar_or_iter_in(x, aaout=False):
    """
    Gather function evaluations over scalar or iterable `x` values.

    aaout :: boolean
        "Always array output" flag:  If True, scalar input produces
        a 1-D, single-element array output.  If false, scalar input
        produces scalar output.
    """
    x = np.asarray(x)
    scalar_in = x.ndim==0

    y = np.exp(x)  # YOUR OPERATIONS HERE!

    if scalar_in and not aaout:
        return np.squeeze(y)
    else:
        return np.atleast_1d(y)

我怀疑在大多数情况下,该aaout标志不是必需的,并且您总是希望标量输出和标量输入。在这种情况下,回报应该是:

    if scalar_in:
        return np.squeeze(y)
    else:
        return y
于 2018-03-20T00:00:56.427 回答
0

这是一个老问题,但这是我的两分钱。

尽管 Pierre GM 的答案效果很好,但它具有将标量转换为数组的——可能是不受欢迎的——副作用。如果那是您想要/需要的,请停止阅读;否则,继续。虽然这可能没问题(并且可能有利于lists返回iterablesa np.array),但可以说对于标量它应该返回一个标量。如果这是所需的行为,为什么不遵循 python 的EAFP哲学。这是我通常做的(我更改了示例以显示np.asarray返回“标量”时可能发生的情况):

def saturateElements(x):
    x = np.asarray(x)
    try:
        x[x>1] = 1
    except TypeError:
        x = min(x,1)
    return x

我意识到它比 Pierre GM 的回答更冗长,但正如我所说,如果传递了一个标量,这个解决方案将返回一个标量,或者 anp.array是一个数组或传递了 iterable。

于 2015-10-03T00:37:52.543 回答
0

现有的解决方案都有效。这是另一种选择。

def incrementElements(x):
    try:
        iter(x)
    except TypeError:
        x = [x]
    x = np.array(x)
    # code that operators on x
于 2020-12-03T00:16:33.867 回答