【发布时间】:2016-11-23 00:59:14
【问题描述】:
我正在运行以下代码:
for i in range(1000)
My_Array=numpy.concatenate((My_Array,New_Rows[i]), axis=0)
上面的代码很慢。有没有更快的方法?
【问题讨论】:
标签: arrays performance numpy concatenation
我正在运行以下代码:
for i in range(1000)
My_Array=numpy.concatenate((My_Array,New_Rows[i]), axis=0)
上面的代码很慢。有没有更快的方法?
【问题讨论】:
标签: arrays performance numpy concatenation
这基本上是所有基于数组的算法中发生的事情。
每次更改数组的大小时,都需要重新调整大小并且需要复制每个元素。这也发生在这里。 (一些实现会保留一些空槽;例如,随着每次增长,内存空间会增加一倍)。
这不是一个特定于 numpy 的主题,而是更多关于数据结构的主题。
编辑:因为这个相当模糊的答案得到了一些赞成,我觉得有必要明确我的链表方法是一个可能的例子。正如评论中所指出的,python 的列表更像数组(绝对不是链表)。但核心事实是:python 中的 list.append() 是 fast (摊销:O(1)),而对于 numpy-arrays 则不是这样! docs中还有一小部分关于内部的:
列表是如何实现的?
Python 的列表实际上是可变长度数组,而不是 Lisp 样式的链表。 实现使用对其他对象的连续引用数组,并将指向该数组的指针和数组的长度保存在列表头结构中。
这使得索引列表 a[i] 的成本与列表的大小或索引的值无关。
添加或插入项目时,引用数组的大小会调整。 应用了一些巧妙的方法来提高重复追加项目的性能;当数组必须增长时,会分配一些额外的空间,因此接下来的几次不需要实际调整大小。
(我的粗体注释)
【讨论】:
list 实际上是一个数组(内部)。它肯定会做一些技巧,例如在增长时保留空槽,并且具有良好的摊销复杂性,但经典的链表可能更快(取决于许多因素)。
这取决于New_Rows[i]是什么,以及你想要什么样的数组。如果您从要首尾相连的列表(或一维数组)开始(以制作一个长的一维数组),只需一次将它们全部连接起来。连接采用任意长度的列表,而不仅仅是 2 个项目。
np.concatenate(New_Rows, axis=0)
或者可能使用中间列表推导(以获得更大的灵活性)
np.concatenate([row for row in New_Rows])
或者更接近你的例子。
np.concatenate([New_Rows[i] for i in range(1000)])
但如果New_Rows 元素的长度都相同,并且您想要一个二维数组,每行一个New_Rows 值,np.array 做得很好:
np.array(New_Rows)
np.array([i for i in New_Rows])
np.array([New_Rows[i] for i in range(1000)])
np.array 主要用于从列表列表构建数组。
np.concatenate 也可以在 2d 中构建,但输入需要是 2d 才能开始。 vstack 和 stack 可以解决这个问题。但是所有这些stack 函数都使用某种列表理解,然后是concatenate。
一般来说,迭代或附加列表会更好/更快,并且只应用一次np.array(或连接)。 appending 到列表很快;比创建一个新数组要快得多。
【讨论】:
也许创建一个大小正确的空数组而不是填充它? 如果你有一个相同维度的数组列表,你可以
import numpy as np
arr = np.zeros((len(l),)+l[0].shape)
for i, v in enumerate(l):
arr[i] = v
对我来说工作得更快,它只需要分配一个内存
【讨论】:
假设您有一个大的二维 numpy 数组列表,具有相同的列数和不同的行数,如下所示:
x = [numpy_array1(r_1, c),......,numpy_arrayN(r_n, c)]
像这样连接:
while len(x) != 1:
if len(x) == 2:
x = np.concatenate((x[0], x[1]))
break
for i in range(0, len(x), 2):
if (i+1) == len(x):
x[0] = np.concatenate((x[0], x[i]))
else:
x[i] = np.concatenate((x[i], x[i+1]))
x = x[::2]
【讨论】:
x = [np.zeros(2000) for _ in range(1000)] 运行了这个。这将在大约 50 毫秒内运行。 np.concatenate(x)(参见@hpaulj 的回答)在 2 毫秒内运行。您的答案肯定比问题中给出的方法快,但比 numpy 的最佳实践要慢得多。
我认为@thebeancounter 的解决方案是可行的方法。 如果你事先不知道你的 numpy 数组的确切大小,你也可以采取类似于在 C++ 中实现向量类的方法。
更具体地说,您可以将 numpy ndarray 包装到一个新类中,该类的默认大小大于您当前的需求。当 numpy 数组几乎填满时,将当前数组复制到更大的数组。
【讨论】: