Python:矢量化lambda函数数组的计算

时间:2015-03-23 06:29:11

标签: python arrays numpy lambda vectorization

如何对lambda函数数组的求值进行矢量化?

以下是了解我所谈论的内容的一个例子。 (即使我使用的是numpy数组,但我并不仅限于使用numpy。)

我们说我有以下numpy数组。

array1 = np.array(["hello", 9])
array2 = np.array([lambda s: s == "hello", lambda num: num < 10])

(您可以将这些类型的对象存储在numpy中,而不会抛出错误,信不信由你。)我想要的是类似于以下内容。

array2 * array1 
# Return np.array([True, True]). PS: An explanation of how to `AND` all of 
# booleans together quickly would be nice too.

当然,对于大小为2的数组来说,这似乎是不切实际的,但对于任意大小的数组,我认为这会因为所有低级优化而提高性能。

那么,有谁知道如何编写这种奇怪的python代码?

2 个答案:

答案 0 :(得分:2)

当然,简单的答案是你不能轻易地使用numpy(或者使用标准Python)来做到这一点。根据我的知识,Numpy实际上并没有对大多数操作本身进行矢量化:它使用BLAS / ATLAS /等库来执行某些情况。即使它确实如此,它也会在C中针对特定情况这样做:它肯定无法向量化Python函数执行。

如果您想在此处涉及多处理, 是可能的,但这取决于您的情况。您的单个功能应用程序是否耗时,使它们可以一个一个地发送,或者您是否需要大量的快速功能执行,在这种情况下,您可能希望将它们的批量发送到每个进程?

一般来说,由于可能被认为是糟糕的基础设计(例如,全局解释器锁),标准Python很难实现轻量级并行化,因为您希望在这里。有很多重要的方法,比如多处理模块或Ipython.parallel,但这些方法需要一些工作才能使用。

答案 1 :(得分:1)

好吧,伙计们,我有一个答案:numpy的vectorize

请阅读已编辑的部分。您会发现python实际上为您优化了代码,这实际上违背了在这种情况下使用numpy数组的目的。 (但使用numpy数组不会降低性能。)

最后一个测试确实表明python列表尽可能高效,因此这个向量化过程是不必要的。这就是为什么我没有将这个问题标记为“最佳答案”。

设置代码:

def factory(i): return lambda num: num==i

array1 = list()
for i in range(10000): array1.append(factory(i))
array1 = np.array(array1)
array2 = np.array(xrange(10000))

“未实现”版本:

def evaluate(array1, array2):
    return [func(val) for func, val in zip(array1, array2)]

%timeit evaluate(array1, array2)
# 100 loops, best of 3: 10 ms per loop

矢量化版本

def evaluate2(func, b): return func(b)
vec_evaluate = np.vectorize(evaluate2)
vec_evaluate(array1, array2)
# 100 loops, best of 3: 2.65 ms per loop

修改

好的,我只是想使用上述测试粘贴更多基准测试,除了测试用例不同。

我做了第三次编辑,显示如果你只是使用python列表会发生什么。长话短说,你实际上不会后悔太多。这个测试用例位于最底层。

仅涉及整数的测试用例

总之,如果n很小,那么非版本化版本会更好。否则,矢量化是要走的路。

使用n = 30

%timeit evaluate(array1, array2)
# 10000 loops, best of 3: 35.7 µs per loop
%timeit vec_evaluate(array1, array2)
# 10000 loops, best of 3: 27.6 µs per loop

使用n = 7

%timeit evaluate(array1, array2)
100000 loops, best of 3: 9.93 µs per loop
%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 21.6 µs per loop

涉及字符串的测试用例

矢量化获胜。

设置代码:

def factory(i): return lambda num: str(num)==str(i)

array1 = list()
for i in range(7):
    array1.append(factory(i))
array1 = np.array(array1)
array2 = np.array(xrange(7))

使用n = 10000

%timeit evaluate(array1, array2)
10 loops, best of 3: 36.7 ms per loop

%timeit vec_evaluate(array1, array2)
100 loops, best of 3: 6.57 ms per loop

使用n = 7

%timeit evaluate(array1, array2)
10000 loops, best of 3: 28.3 µs per loop

%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 27.5 µs per loop

随机测试

只是看看分支预测如何发挥作用。从我所看到的,它并没有真正改变太多。矢量化通常仍然胜出。

设置代码。

def factory(i):
    if random() < 0.5:
        return lambda num: str(num) == str(i)
    return lambda num: num == i

n = 10000

%timeit evaluate(array1, array2)
10 loops, best of 3: 25.7 ms per loop

%timeit vec_evaluate(array1, array2)
100 loops, best of 3: 4.67 ms per loop

n = 7

%timeit evaluate(array1, array2)
10000 loops, best of 3: 23.1 µs per loop

%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 23.1 µs per loop

使用python列表而不是numpy数组

我运行此测试以查看当我选择不使用“优化的”numpy数组时发生了什么,我收到了一些非常令人惊讶的结果。

设置代码几乎相同,只是我选择不使用numpy数组。我也只针对“随机”案例进行此测试。

def factory(i):
    if random() < 0.5:
        return lambda num: str(num) == str(i)
    return lambda num: num == i


array1 = list()
for i in range(10000): array1.append(factory(i))
array2 = range(10000)

和“未经过滤的”版本:

%timeit evaluate(array1, array2)
100 loops, best of 3: 4.93 ms per loop

你可以看到这实际上是非常令人惊讶的,因为这几乎与我通过涉及矢量化evaluate的随机测试案例收到的基准相同。

%timeit vec_evaluate(array1, array2)
10 loops, best of 3: 19.8 ms per loop

同样,如果在使用vec_evaluate之前将这些更改为numpy数组,则会得到相同的4.5 ms基准。