【发布时间】:2016-07-20 14:06:44
【问题描述】:
给定以下 2 列数组,我想从第二列中选择与第一列中的“边”相对应的项目。这只是一个示例,因为实际上我的a 可能有数百万行。因此,理想情况下,我希望尽可能快地完成此操作,并且不会产生中间结果。
import numpy as np
a = np.array([[1,4],[1,2],[1,3],[2,6],[2,1],[2,8],[2,3],[2,1],
[3,6],[3,7],[5,4],[5,9],[5,1],[5,3],[5,2],[8,2],
[8,6],[8,8]])
即我要查找结果,
desired = np.array([4,6,6,4,2])
这是a[:,1] 中的条目,对应于a[:,0] 更改的位置。
一个解决方案是,
b = a[(a[1:,0]-a[:-1,0]).nonzero()[0]+1, 1]
给出np.array([6,6,4,2]),我可以简单地添加第一项,没问题。但是,这会创建第一个项目的索引的中间数组。我可以通过使用列表理解来避免中间:
c = [a[i+1,1] for i,(x,y) in enumerate(zip(a[1:,0],a[:-1,0])) if x!=y]
这也给出了[6,6,4,2]。假设基于生成器的zip(在 Python 3 中为真),这不需要创建中间表示并且应该非常节省内存。但是,内部循环不是 numpy,它需要生成一个列表,然后必须将其转换回 numpy 数组。
你能想出一个内存效率为c但速度效率为b的numpy-only版本吗?理想情况下,只需要通过a 一次。
(请注意,在这里测量速度不会有太大帮助,除非 a 非常大,所以我不会费心对此进行基准测试,我只想要理论上快速且内存高效的东西。例如,你可以假设a 中的行是从文件流式传输的并且访问速度很慢——避免b 解决方案的另一个原因,因为它需要通过a 进行第二次随机访问。)
编辑:一种生成用于测试的大型a 矩阵的方法:
from itertools import repeat
N, M = 100000, 100
a = np.array(zip([x for y in zip(*repeat(np.arange(N),M)) for x in y ], np.random.random(N*M)))
【问题讨论】:
-
我想另一个更普遍的问题是,简单地说,“如何在 numpy 数组上执行流式处理(类似生成器)操作?” (没有把它们变成列表!)
-
"...尽可能快,并且不产生中间结果..." 这些有时是相互冲突的目标。哪个更重要,最佳性能还是最小化内存使用?
-
好吧,与其说是不“浪费内存”,不如说是能够对可能根本不适合内存的数组大小进行操作,同时又不会过多地牺牲速度。 (例如,来自内存映射文件的数组。)很遗憾,转换为
numpy.fromiter似乎意味着要牺牲 10 倍的速度。
标签: python arrays performance numpy