Python 有效地将3D Numpy阵列重塑为1D列表加坐标向量
我有一个大型嵌套数组Python 有效地将3D Numpy阵列重塑为1D列表加坐标向量,python,arrays,numpy,optimization,Python,Arrays,Numpy,Optimization,我有一个大型嵌套数组a(256x256x256),我需要将其重新构造为一个包含以下元素的列表: [ (i,j,k), a[i,j,k] ] 我目前的做法如下: aflat = a.flatten().tolist() coords = list(itertools.product(range(256), repeat=3)) thelist = [list(x) for x in zip(coords, aflat)] 这是可行的,但速度相当慢 我可以通过删除运行时生成的坐标向量并从文件中读
a
(256x256x256),我需要将其重新构造为一个包含以下元素的列表:
[ (i,j,k), a[i,j,k] ]
我目前的做法如下:
aflat = a.flatten().tolist()
coords = list(itertools.product(range(256), repeat=3))
thelist = [list(x) for x in zip(coords, aflat)]
这是可行的,但速度相当慢
我可以通过删除运行时生成的坐标向量并从文件中读取它们来节省一秒钟左右的时间。然而,主要的减速似乎是在最后一行,这是时钟超过6秒
有没有一种更快的方法来生成Python所需的数据结构?正如@p-I所评论的,主要问题是代码创建了大量列表,Python花费了大量时间进行内存管理。为了消除这种情况,可以使用numpy数组预先分配数据,并使用其
repeat
和tile
函数生成i、j、k
值:
# order='F' is important here so column-wise assignment can
# occur with a stride of 1. Switching the order results
# in a significant performance hit.
coords = numpy.zeros([a.size,4],'d',order='F')
NI, NJ, NK = a.shape
# build columns for (i,j,k) tuples using repeat and tile
coords[:,0] = numpy.repeat(range(NI),NJ*NK)
coords[:,1] = numpy.tile(numpy.repeat(range(NJ),NK), NI)
coords[:,2] = numpy.tile(range(NK), NI*NJ)
coords[:,3] = a.flatten()
这将生成一个数组,其中每一行都是(i,j,k,value)
。它确实假设您的原始数组是有序的(numpy中的C有序数组)
根据我的时间安排,基于2013年MacBook Pro上Python 3.5的十次迭代,每次转换运行OP的转换大约需要20秒,使用这种方法每次转换只需要8秒
输出格式必须是列表,数组可以在最后一步转换为列表。然而,在我的测试中,这将每次转换的时间增加到了13秒。要扩展@wii上面的评论,您需要查找
np.ndenumerate
通常,您会避免显式创建列表,而是使用迭代器。例如:
for (i,j,k), val in np.ndenumerate(your_3d_array):
assert val == your_3d_array[i,j,k]
# Note that we also could have done:
for ind, val in np.ndenumerate(your_3d_array):
assert val == your_3d_array[ind]
但是,如果确实要创建完整的中间列表,则可以使用:
list(np.ndenumerate(your_3d_array))
作为更完整的示例:
In [1]: import numpy as np
In [2]: x = np.arange(3*4*5).reshape(3, 4, 5)
In [3]: x
Out[7]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39]],
[[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49],
[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59]]])
In [4]: list(np.ndenumerate(x))
Out [4]:
[((0, 0, 0), 0),
((0, 0, 1), 1),
((0, 0, 2), 2),
((0, 0, 3), 3),
...
((2, 3, 1), 56),
((2, 3, 2), 57),
((2, 3, 3), 58),
((2, 3, 4), 59)]
看起来问题在于动态分配。您正在创建2^(8*3)个列表。您最初选择的数据类型看起来有问题。当然你想要固定大小的东西,你可以预先分配。。。搜寻“结构包”可能会有帮助。这个想法很有趣,但这真的更快吗?我可能做错了,但在ipython中尝试一些简单的基准测试,这种方法比我发布的方法花费的时间多30%。@MarcoTompitak我的计时结果与你的一致。经过一点调查,我认为主要的性能影响是内存管理,正如一位评论者所指出的。我仔细考虑了一下这个问题,发现如果将输出保持为数组格式,可以节省大量时间。太棒了!我不知道我们的计时差异来自何处,但在我的电脑上,你的方法快了8-9倍,而不仅仅是2倍。我对代码的其余部分进行了修改,以尽可能长时间地处理数组(我需要在某个时候转换为一个列表),并且运行得更快。瓶颈已经从数据结构的生成转移到了我正在进行的实际分析,这对我来说已经足够好了。这肯定要简单得多,但似乎并没有更快。在256x256x256阵列上进行测试,这比我端的代码慢20%以上。它还提供了元组列表,而不是列表列表。在我的代码中,对列表的转换导致了很大的速度减慢。如果我只使用
zip()?