根据Lambda函数,尺寸形状和dtype生成数组

时间:2019-05-11 03:29:25

标签: python arrays numpy lambda

我需要创建一个接受lambda,尺寸形状和Numpy dtype的函数,并生成一个数组。 我知道有fromfunction可以做到这一点,但我不能使用它。我猜想看待它的一种方法是,我需要对fromfunction进行硬编码。我遇到的问题是无法将lambda函数作为其函数传递。香港专业教育学院尝试使用循环和使用索引,我是后一个想法的新手,所以我可能做得不好。基本上我需要创建函数。输入内容与注释中的(预期)结果相同。

import numpy as np
def array_function(f, d, dtype=None):

    return x

print(array_function(lambda i,j: (i - j)**2, [4, 4]))

# Expected Result
#[[0. 1. 4. 9.]
# [1. 0. 1. 4.]
# [4. 1. 0. 1.]
# [9. 4. 1. 0.]]

3 个答案:

答案 0 :(得分:0)

  

lambda函数无法传递

lambda函数是一个函数,它是python中的一类对象。将其作为参数传递给另一个函数没有什么麻烦。您可以只看一下或理解这两个维度,然后制作和排列,然后重塑形状:

import numpy as np
def array_function(f, d, dtype=None):
    a = np.array([f(i, j) for i in range(d[0]) for j in range(d[1])], dtype)
    return a.reshape(d)

print(array_function(lambda i,j: (i - j)**2, [4, 4]))

结果:

[[0 1 4 9]  
 [1 0 1 4]  
 [4 1 0 1]  
 [9 4 1 0]]  

如果您想让array_function接受任意大小的数组,会有些棘手。一种选择是使数组具有大小,然后枚举所有元素以调用该函数:

import numpy as np
def array_function(f, d, dtype=None):
    a = np.zeros(d)
    for coord, val in np.ndenumerate(a):
        a[coord] = f(*coord)    
    return a

# three dimensions    
print(array_function(lambda i,j, k: k+(i - j)**2, [4, 5,2], np.float))

基于评论进行编辑

您可以使用itertools中的starmapproduct构建一个迭代器。我不确定迭代器会用numpy为您带来很多收益,因为您通常想在创建时知道数组的大小。您可以传递一个长度,这不是必需的,但是可以提高性能:

from itertools import product, starmap
import numpy as np
from operator import mul
from functools import reduce

def array_function(f, d, dtype=None):
    length = reduce(mul, d)
    iterator = starmap(f, product(*[range(x) for x in d]))

    a = np.fromiter(iterator, dtype, length)
    return a.reshape(d)

print(array_function(lambda i,j: (i - j)**2, [4, 4], np.float))

答案 1 :(得分:0)

如何从np.ufunc创建一个lambda并使用reduce将其应用于多个维度?

from functools import reduce
import numpy as np

def apply(f, shape, dtype=None):
    ufunc = np.frompyfunc(f, 2, 1)
    ranges = (np.arange(dim) for dim in shape)
    return reduce(ufunc.outer, ranges).astype(dtype)

print(apply(lambda i, j: (i - j) ** 2, (4, 4)))

输出:

[[0. 1. 4. 9.]
 [1. 0. 1. 4.]
 [4. 1. 0. 1.]
 [9. 4. 1. 0.]]

答案 2 :(得分:0)

为此lambdafromfunction可以正常工作:

In [1]: foo = lambda i,j: (i-j)**2                                              
In [2]: np.fromfunction(foo,(4,4))                                              
Out[2]: 
array([[0., 1., 4., 9.],
       [1., 0., 1., 4.],
       [4., 1., 0., 1.],
       [9., 4., 1., 0.]])

fromfunction根据形状生成索引的“网格”:

In [7]: np.indices((4,4))                                                       
Out[7]: 
array([[[0, 0, 0, 0],
        [1, 1, 1, 1],
        [2, 2, 2, 2],
        [3, 3, 3, 3]],

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

并将两个平面(第一维)传递给您的函数。正如您所写,您的函数可用于这些二维网格等数组。 meshgridmgrid(和ogrid)生成相似的索引。

但是我也可以直接创建两个数组,然后将它们传递给foo

In [8]: foo(np.arange(4)[:,None], np.arange(4))                                 
Out[8]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]])

这两个输入数组彼此广播,就像Out[7]中的两个平面一样。实际上,它们是(4,1)和(4)形状的等价物。

请注意,在Python中,lambda只是一个匿名函数。在这里,我将其分配给一个变量,并给出了一个名称。也可以使用def函数。

只要您的函数可以使用所需的2d索引数组,就不需要任何特殊的编码。

如果该函数仅适用于ij的标量值,则您必须求助于Python级别的迭代操作(与使用已编译的numpy函数相反)

列表理解版本:

In [6]: np.array([[foo(i,j) for j in range(4)] for i in range(4)])              
Out[6]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]])

我更喜欢frompyfunc,它将用作:

In [9]: f = np.frompyfunc(foo, 2,1)                                             
In [10]: f(np.arange(4)[:,None], np.arange(4))                                  
Out[10]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]], dtype=object)

请注意,它返回一个对象dtype。可以使用astype进行修改。如果您太懒惰以至于无法编写自己的可广播fromfunctionI数组,也可以将其传递给J

根据我的经验,frompyfunc方法比列表理解要快一些(最多约2倍)。另一方面,如果foo与[8]中的数组一起使用,则速度比更像是10倍。因此,如果您可以编写适用于整个数组而不是标量索引的函数,那么就性能而言,您将最幸福。