Numpy连接速度慢:有其他替代方法吗?

23

我正在运行以下代码:

for i in range(1000)
    My_Array=numpy.concatenate((My_Array,New_Rows[i]), axis=0)

上述代码运行缓慢。有更快的解决方法吗?

5个回答

31
这基本上是所有基于数组的算法所发生的事情。
每次改变数组大小时,它都需要重新调整大小并复制每个元素。这也是在这里发生的事情。(一些实现会保留一些空槽;例如,每次增长时将内部存储器的空间加倍)。
- 如果您在 np.array 创建时获得了数据,只需一次性添加即可(内存将仅分配一次!) - 如果没有,请使用类似链接列表的东西收集它们(允许 O(1) 的附加操作)。然后一次性在 np.array 中读取它们(再次只有一个内存分配)。
这不是太多关于 numpy 特定主题,但更多关于数据结构。 编辑:由于这个含糊的答案得到了一些点赞,我感到有必要澄清一下,我的链表方法只是一个可能的例子。正如评论中所示,Python的列表更像数组(而绝不是链表)。但核心事实是:在Python中,list.append()的速度很(摊销:O(1)),而对于numpy数组来说则并非如此!还有一个关于内部机制的小部分在文档中解释:
Python的列表实现其实是可变长度数组,而不是Lisp风格的链表。实现使用一个连续的对象引用数组,并在列表头结构中保留对该数组及其长度的指针。这使得索引列表a[i]的操作成本独立于列表的大小或索引值。当追加或插入项目时,对象引用数组会被调整大小。一些巧妙的技巧被应用于提高重复追加项目的性能;当数组必须增长时,会分配一些额外的空间,以便下几次不需要实际调整大小。

2
我实际上使用了列表追加,性能得到了大幅提升。 - Admia
请注意,Python的list实际上是一个数组(内部)。它肯定会执行一些技巧,如在增长时保留空槽,并具有良好的平摊复杂度,但经典的链表可能会更快(取决于许多因素)。 - sascha
3
Python没有本地的链表类。 - hpaulj
你如何在Python中实现真正的链表?没有内置功能吗?只能像这样使用 https://www.tutorialspoint.com/python/python_linked_lists.htm 吗? - Homero Esmeraldo
1
有一个来自collections模块的deque,比列表更快:https://dev59.com/MGUq5IYBdhLWcg3wPeCr - Homero Esmeraldo

13
也许可以创建一个正确大小的空数组,然后填充它?如果您有一列维度相同的数组,您可以...
import numpy as np 
arr = np.zeros((len(l),)+l[0].shape) 
for i, v in enumerate(l):
   arr[i] = v

对我来说运行速度更快,它只需要一个内存分配


9

这取决于 New_Rows[i] 是什么,以及您想要什么类型的数组。如果您从要连接在一起的列表(或1d数组)开始(以创建长1d数组),只需一次性将它们全部连接即可。Concatenate接受任意长度的列表,而不仅限于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的。vstackstack可以解决这个问题。但所有这些stack函数都使用一些列表推导式,然后跟上concatenate

一般来说,使用列表迭代或附加更好/更快,并只在最后使用np.array(或concatenate)。append到一个列表很快,比创建新数组要快得多。


1
我认为@thebeancounter的解决方案是可行的。 如果您事先不知道numpy数组的确切大小,您也可以采用类似于C ++中实现向量类的方法。
更具体地说,您可以将numpy ndarray包装到一个新类中,该类具有默认大小,该大小大于当前需求。当numpy数组几乎完全填充时,请将当前数组复制到较大的数组中。

-1

假设您有一个大的2D 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的最佳实践要慢得多。 - user2699
这确实是一种聪明的方法,可以在最少的操作次数内合并数组对,但是concatenate接受任意长度的列表,因此您不仅限于成对的数组。 - user2699
1
我从未声称这是最佳的Pythonic解决方案,它只是有效且简单。我使用成对的2,因为每个新的内存分配用于连接都应该是最少的。当我首次按顺序运行np.concatenate时,我的电脑卡住了,所以我尝试了这种方法并且对我有效,因此我觉得分享一下,因为当时我找不到任何答案。也许我应该再搜索一下。 - leo
预先分配 np.empty 数组是可行的方法。假设需要收集许多随机大小的 [N,5] 数据包,然后将它们保存为单个垂直堆叠的数组,在这种情况下该方法运行速度比较快,大约快 100 倍。当然,前提是您拥有足够的内存来容纳所有数据。 - tobi delbruck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接