【问题标题】:Parameters to numpy's fromfunctionnumpy fromfunction 的参数
【发布时间】:2013-09-09 15:47:02
【问题描述】:

我还没有理解numpy 中的关键概念。

我想创建一个 3 维数组并使用函数调用的结果填充每个单元格 - 即,该函数将使用不同的索引多次调用并返回不同的值。

注意:自从写了这个问题,文档已经更新得更清楚了。

我可以用零(或空)创建它,然后用 for 循环覆盖每个值,但直接从函数中填充它似乎更简洁。

fromfunction 听起来很完美。 Reading the documentation 听起来该函数每个单元格调用一次。

但是当我真正尝试它时......

from numpy import *

def sum_of_indices(x, y, z):
    # What type are X, Y and Z ? Expect int or duck-type equivalent.
    # Getting 3 individual arrays
    print "Value of X is:"
    print x

    print "Type of X is:", type(x)
    return x + y + z

a = fromfunction(sum_of_indices, (2, 2, 2))

我希望得到类似的东西:

Value of X is:
0
Type of X is: int
Value of X is:
1
Type of X is: int

重复 4 次。

我明白了:

Value of X is:
[[[ 0.  0.]
  [ 0.  0.]]

 [[ 1.  1.]
  [ 1.  1.]]]
[[[ 0.  0.]
  [ 1.  1.]]

 [[ 0.  0.]
  [ 1.  1.]]]
[[[ 0.  1.]
  [ 0.  1.]]

 [[ 0.  1.]
  [ 0.  1.]]]
Type of X is: <type 'numpy.ndarray'>

该函数只被调用一次,并且似乎返回整个数组作为结果。

根据对索引函数的多次调用来填充数组的正确方法是什么?

【问题讨论】:

  • 您的预期结果是什么?每个单元格调用一次 fromfunction - “多次调用索引函数”是什么意思?
  • 在您的第一个代码块中,a 是您填充的数组,其中a[i, j, k] = sum_of_indices(i, j, k)
  • 对不起,我认为预期的结果从 cmets 很清楚。我已经扩展了。是的,我知道 'a' 是填充数组,但(我相信)只是因为数组添加。当我将 sum_of_indices 替换为无法实现的“真实”功能(例如数据库查找)时。

标签: python arrays numpy


【解决方案1】:

文档在这方面非常具有误导性。就像你注意到的那样:numpy 不是执行f(0,0), f(0,1), f(1,0), f(1,1),而是执行

f([[0., 0.], [0., 1.]], [[1., 0.], [1., 1.]])

当您尝试使用类似lambda i: l[i] 的东西时,使用 ndarrays 而不是承诺的整数坐标非常令人沮丧,其中l 是另一个数组或列表(尽管实际上,在 numpy 中可能有更好的方法来做到这一点)。

numpy vectorize 函数解决了这个问题。你在哪里

m = fromfunction(f, shape)

尝试使用

g = vectorize(f)
m = fromfunction(g, shape)

【讨论】:

  • 您的“矢量化”修复似乎大部分都有效,但我认为它出于某种原因调用了两次 f(0, 0)。为什么会这样?
  • NumPy documentation for vectorize 说:vectorized 输出的数据类型是通过使用输入的第一个元素调用函数来确定的。这可以通过指定 otypes 参数来避免。
【解决方案2】:

我显然没有说清楚。我收到了回复,fromfunc 实际上就像我的测试代码演示的那样工作,我已经知道这一点,因为我的测试代码演示了它。

我一直在寻找的答案似乎分为两部分:


fromfunc 文档具有误导性。它可以一次填充整个数组。

注意:自编写此问题以来,文档已更新为更清晰。

特别是,documentation 中的这一行 不正确(或至少具有误导性)

例如,如果shape 为 (2, 2),则参数依次为 (0, 0), (0, 1), (1, 0), (1, 1)。

没有。如果shape(即从上下文中,fromfunction 的第二个参数)是 (2,2),则参数将是(不是“依次”,而是在唯一的调用中):

(array([[ 0.,  0.], [ 1.,  1.]]), array([[ 0.,  1.], [ 0.,  1.]]))

文档已更新,目前阅读更准确:

该函数使用 N 个参数调用,其中 N 是形状的等级。每个参数代表沿特定轴变化的阵列坐标。例如,如果形状是 (2, 2),那么参数将是 array([[0, 0], [1, 1]]) 和 array([[0, 1], [0, 1]])

(我的简单示例源自手册中的示例,可能具有误导性,因为+ 可以对数组和索引进行操作。这种歧义是文档不清楚的另一个原因。我想最终使用一个不是基于数组,而是基于单元格的函数——例如,每个值可能是基于索引从 URL 或数据库中获取的,甚至是来自用户的输入。)


回到问题 - 我如何从每个元素调用一次的函数中填充数组,答案似乎是:

您不能以功能样式执行此操作。

您可以以命令式/迭代式的方式进行 - 即编写嵌套的 for 循环,并自己管理索引长度。

您也可以将其用作迭代器,但迭代器仍需要跟踪自己的索引。

【讨论】:

  • 这是一个令人难以置信的误导性文档。再加上 x == y 具有进行逐点比较和返回数组的完全荒谬的行为,并且您有文档示例实际上似乎暗示,对于具有 numpy 之外的经验的人来说,它正在逐个进行单元格计算
【解决方案3】:

我认为您误解了fromfunction 的作用。

来自numpysource code

def fromfunction(function, shape, **kwargs):
    dtype = kwargs.pop('dtype', float)
    args = indices(shape, dtype=dtype)
    return function(*args,**kwargs)

其中indicesmeshgrid 相当,其中每个变量都是np.arange(x)

>>> side = np.arange(2)
>>> side
array([0, 1])
>>> x,y,z = np.meshgrid(side,side,side)
>>> x
array([[[0, 0],
        [1, 1]],

       [[0, 0],
        [1, 1]]])
>>> x+y+z #Result of your code.
array([[[0, 1],
        [1, 2]],

       [[1, 2],
        [2, 3]]])

【讨论】:

  • 了解(现在),但请参阅我的回答。文档与此代码不匹配,这并没有解决正确方法是什么的问题。
【解决方案4】:

这会给你一个不正确的结果吗? a 应该符合预期(并且是我测试它的时候),并且似乎是做你想做的事的好方法。

>>> a
array([[[ 0.,  1.],    # 0+0+0, 0+0+1
        [ 1.,  2.]],   # 0+1+0, 0+1+1

       [[ 1.,  2.],    # 1+0+0, 1+0+1
        [ 2.,  3.]]])  # 1+1+0, 1+1+1

由于fromfunction 处理输入的数组索引, 你可以看到它只需要调用一次。文档没有说明这一点,但您可以看到该函数正在源代码中的索引数组上调用(来自numeric.py):

def fromfunction(function, shape, **kwargs):
    . . .
    args = indices(shape, dtype=dtype)
    return function(*args,**kwargs)

sum_of_indices 在数组输入上调用,其中每个数组都保存该数组的索引值 维度。

array([[[ 0.,  0.],
        [ 1.,  1.]],

       [[ 1.,  1.],
        [ 1.,  1.]]])

+

array([[[ 0.,  0.],
        [ 1.,  1.]],

       [[ 0.,  0.],
        [ 1.,  1.]]])

+
array([[[ 0.,  1.],
        [ 0.,  1.]],

       [[ 0.,  1.],
        [ 0.,  1.]]])

=

array([[[ 1.,  1.],
        [ 1.,  2.]],

       [[ 1.,  2.],
        [ 2.,  3.]]])

【讨论】:

  • 对不起 - 我的例子让你走错了路。是的,该示例按描述工作,但我想将 sum_of_indices 替换为无法在数组级别工作的真实函数。看我的回答。
【解决方案5】:

以下是我对您的问题的看法:

正如 Chris Jones 所说,解决方案的核心是使用 np.vectorize

# Define your function just like you would
def sum_indices(x, y, z):
    return x + y + z

# Then transform it into a vectorized lambda function
f = sum_indices
fv = np.vectorize(f)

如果你现在做np.fromfunction(fv, (3, 3, 3)) 你会得到这个:

array([[[0., 1., 2.],
        [1., 2., 3.],
        [2., 3., 4.]],

       [[1., 2., 3.],
        [2., 3., 4.],
        [3., 4., 5.]],

       [[2., 3., 4.],
        [3., 4., 5.],
        [4., 5., 6.]]])

这是你想要的吗?

【讨论】:

  • "f = lambda i, j, k: sum_indices(i, j, k)" 这似乎将三个参数的函数转换为三个参数的相同函数。为什么不直接说“f = sum_indices”? (或者完全排除?)
  • @Oddthinking 你是对的。起初我尝试这样做,但代码失败了。我认为不使用lambda 的参数解包有问题。我现在确实意识到问题是一个错字。现已编辑答案以反映您的建议。
【解决方案6】:

我认为大多数 fromfunction 的示例都使用方数组有点令人困惑。

也许查看非方形数组会有所帮助?

def f(x,y):
    print(f'x=\n{x}')
    print(f'y=\n{y}')
    return x+y

z = np.fromfunction(f,(4,3))
print(f'z=\n{z}')

结果:

x=
[[0 0 0]
 [1 1 1]
 [2 2 2]
 [3 3 3]]
y=
[[0 1 2]
 [0 1 2]
 [0 1 2]
 [0 1 2]]
z=
[[0 1 2]
 [1 2 3]
 [2 3 4]
 [3 4 5]]

【讨论】:

  • 这个问题不是关于数组维度的混淆。就是 7 年前的文档误导了 fromfunction 被调用的次数(一次,而不是每个单元格一次)。这已在文档中得到纠正,其他答案已经解释了使用 vectorize 实现缺失部分的方法。
  • @Oddthinking - 谢谢!就像 Archer 一样,有时我会错过核心概念 :)
【解决方案7】:

如果您将参数dtype 设置为int,您可以获得所需的输出:

a = fromfunction(sum_of_indices, (2, 2, 2), dtype=int)

https://numpy.org/doc/stable/reference/generated/numpy.fromfunction.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 2018-09-01
    • 2019-04-16
    • 2016-11-29
    • 2014-02-26
    • 1970-01-01
    相关资源
    最近更新 更多