数组数组的意思

时间:2013-02-27 00:45:42

标签: python numpy

我有这样的结构:

a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])

它是一个2x2结构,每个元素都可以有任意维度

我想得到a的每个元素的均值:

np.mean(a)

ValueError: operands could not be broadcast together with shapes (3) (5) 

我尝试使用axis,但它不起作用。我希望新的np.array等于[[2, 2], [2, 2]

一般来说,我希望能够以同样的方式在a上运行任何矢量化函数。

怎么做?我需要快速代码,所以请避免显式循环。

我能做的最好的事情是:

f = np.mean
result = np.zeros(a.shape)
for i in np.ndindex(a.shape):
  result[i] = f(a[i])

3 个答案:

答案 0 :(得分:3)

这是我能做的最好的事情:

a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])
np.array([ np.mean(b) for b in a.ravel()]).reshape(a.shape)

输出

array([[ 2.,  2.],
       [ 2.,  2.]])

可以推广为其他函数:

def ravel_func(func,arr):
    return np.asarray([ func(part) for part in arr.ravel()]).reshape(arr.shape)

速度测试,感谢Jaime

In [82]: timeit vmean(a)
10000 loops, best of 3: 65 us per loop

In [83]: timeit ravel_func(np.mean,a)
10000 loops, best of 3: 67 us per loop

答案 1 :(得分:3)

我想你想要numpy.vectorize,它基本上就像ndarrays的内置map一样。

>>> a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])
>>> vmean = np.vectorize(np.mean)
>>> vmean(a)
array([[ 2.,  2.],
       [ 2.,  2.]])

答案 2 :(得分:2)

从你的评论:你有相对较少的相当大的元素。这意味着外循环迭代的速度是无关紧要的,而内循环迭代的速度是至关重要的。

我们在此基础上加上一些实际数字。您的外部阵列最多有4个尺寸,最大尺寸为10.这意味着最多有10000个元素。同时,这些元素是“相当大”的-let的解释,保守地只有50个。所以,你有510000个循环迭代。您为提高10000外部迭代的速度所做的任何操作都会使代码中的差异小于2%。事实上,它远不如此 - 2%假设除了迭代本身之外没有任何工作可做,这显然不是真的。

所以,你专注于错误的地方。只有500000内迭代很重要。如果您可以使用单个一维更高的数组替换数组,并在numpy中完成所有操作,那么它可能会更快,但会使您的代码更加复杂且难以理解以获得订单收益1%的一小部分是愚蠢的。只需使用简单明了的vectorize答案或理解答案。


同时

  

可能我应该尝试并行化评估,为每个矩阵元素使用一个线程。

并行性在这里是一个好主意。但不使用线程,而不是每个元素。

如果你有一台8核机器,使用8个硬件线程意味着你的工作速度快了近8倍。但是使用10000个硬件线程并不意味着你完成事情的速度要快10000倍,甚至快8倍 - 你可能花费了很多时间进行上下文切换,分配和释放线程堆栈等,它实际上需要比顺序更长的时间版。因此,您希望创建一个包含8个硬件线程的工作池,并将10000个任务放在队列中以便由该池运行。

此外,10000可能过于细化。每个工作都有一点开销,如果你的工作太小,你就会浪费太多时间在开销上。批量处理的显而易见的方法是每轴 - 具有1000个作业,每个作业执行一行10个元素,或100个作业,每个作业执行一个包含100个元素的2D数组。测试0,1,2和3轴的批量大小,并查看哪些轴提供最佳性能。如果差异很大,您可能需要尝试稍微复杂的批处理,例如拆分为3x10x10和4x10x10的3D数组。

最后,虽然Python线程是真正的OS(以及硬件)线程,但GIL会阻止其中多个线程一次运行Python代码。 numpy做了一些工作来解决这个问题,但它并不完美(特别是如果你在numpy次调用之间有很多开销)。最简单的方法是使用multiprocessing,它允许您拥有8个独立进程的池,而不是1个进程中的8个线程。这显着增加了开销 - 您需要复制子数组(隐式地,作为任务函数的参数和返回值,或显式地,通过管道)或将它们放在共享内存中。但是如果你的任务规模足够大,那通常不是问题。


以下是如何并行化现有代码的示例:

f = np.mean
result = np.zeros(a.shape)
future_map = {}
with futures.ProcessPoolExecutor(max_workers=8) as executor:
    for i in np.ndindex(a.shape):
        future_map[executor.submit(f, a[i])] = i
    for future in futures.as_completed(future_map):
        i = future_map[future]
        result[i] = future.result()

显然,你可以简化它(例如,用dict理解来替换提交循环),但我想让你尽可能明显地改变它。

另外,我正在使用futures(需要3.2+;如果您使用的是2.7,请从PyPI安装backport)因为它使代码更简单;如果您需要更多灵活性,则需要更复杂的multiprocessing库。

最后,我没有进行任何批处理 - 每个任务都是单个元素 - 所以开销可能会非常糟糕。

但从这开始,尽可能地简化它,然后将其转换为使用1,2,3轴等批次,如前所述。