如何使用列表作为参数对函数进行矢量化?

How to vectorize a function with lists as argument?

我需要帮助在 numpy 中向量化一个函数。在 Julia,我可以做这样的事情:

((a,b,c) -> [a,b,c]).([[1,2],[3,4]],[[5,6],[7,8]],nothing)

哪个returns

2-element Vector{Vector{Union{Nothing, Vector{Int64}}}}:
 [[1, 2], [5, 6], nothing]
 [[3, 4], [7, 8], nothing]

它一次从可迭代对象中取出一个子列表并展开 nothing

在 Python 中,我只是不能有类似的行为。我试过了:

np.vectorize(lambda a,b,c: [a,b,c])([[1,2], [3,4]], [[5,6], [7,8]], None)

但是 returns:

array([[list([1, 5, None]), list([2, 6, None])],
       [list([3, 7, None]), list([4, 8, None])]], dtype=object)

如果我这样做:

np.vectorize(lambda a,b,c: print(a,b,c))([[1,2], [3,4]], [[5,6], [7,8]], np.nan)

我回来了:

1 5 nan
1 5 nan
2 6 nan
3 7 nan
4 8 nan

我尝试使用排除参数,但 il 排除了整个数组:

np.vectorize(lambda a,b,c: print(a,b,c), excluded=[0])([[1,2], [3,4]], [[5,6], [7,8]], np.nan)

打印:

[[1, 2], [3, 4]] 5 nan
[[1, 2], [3, 4]] 5 nan
[[1, 2], [3, 4]] 6 nan
[[1, 2], [3, 4]] 7 nan
[[1, 2], [3, 4]] 8 nan

顺便说一句,实际函数是 sklearn 函数,而不是 lambda 函数。

你给了它一个 (2,2), (2,2) 和标量参数。 np.vectorized 调用了您的函数 4 次,每次都使用这 3 个值的元组(一起广播)。

您也可以在 print 版本中看到这一点。开头有一个额外的元组,用于确定 return dtype,在本例中是一个列表,所以 dtype=object.

使用 exclude 它不会迭代第一个参数的值,而是将其整个传递。

这是创建列表列表的正确方法:

In [811]: a,b,c = [[1,2], [3,4]], [[5,6], [7,8]], None

In [813]: [[i,j,None] for i,j in zip(a,b)]
Out[813]: [[[1, 2], [5, 6], None], [[3, 4], [7, 8], None]]

如果我们添加一个signature(和otypes):

In [821]: f = np.vectorize(lambda a,b,c: [a,b,c], signature='(n),(n),()->()', otypes=[object])
In [822]: f(a,b,c)
Out[822]: 
array([list([array([1, 2]), array([5, 6]), None]),
       list([array([3, 4]), array([7, 8]), None])], dtype=object)

现在它只调用函数两次。但结果要慢得多。 反复阅读 notes 关于性能的文章。

如果我们先把列表参数做成数组:

In [825]: A,B = np.array(a), np.array(b)
In [826]: A,B
Out[826]: 
(array([[1, 2],
        [3, 4]]),
 array([[5, 6],
        [7, 8]]))

签名 f return 是同一件事,表明 vectorize 确实将列表转换为数组:

In [827]: f(A,B,c)
Out[827]: 
array([list([array([1, 2]), array([5, 6]), None]),
       list([array([3, 4]), array([7, 8]), None])], dtype=object)

如果我们将数组传递给列表理解,我们可以得到:

In [829]: np.array([[i,j,None] for i,j in zip(A,B)], object)
Out[829]: 
array([[array([1, 2]), array([5, 6]), None],
       [array([3, 4]), array([7, 8]), None]], dtype=object)
In [830]: _.shape
Out[830]: (2, 3)