我有这样的结构:
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])
答案 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轴等批次,如前所述。