numpy.array(list)的速度很慢

5

我有一个包含5,000,000个整数的列表。我想把这个列表转换成numpy数组。我尝试了以下代码:

numpy.array( list )

但是它非常慢。

我对此操作进行了100次基准测试,并循环遍历列表100次。没有太大的区别。

有什么好的想法可以让它更快吗?


你确定你的列表只包含整数吗?生成的数组的数据类型是什么? - chrisb
是的,我非常肯定。这是一个扁平的图像数据数组。因此,输入和输出数组都是uint8类型。 - Yinan
我有些困惑。你说list是一个整数列表,但是在注释中又说它是一组图像数据的平坦数组uint8。Python中的整数不是uint8。如果它不是ndarray,那么它是一个字节串吗? - hpaulj
什么是“非常慢”?交互式操作时会出现延迟吗?还是在转换数千个这些“图像”时很慢? - hpaulj
@hpaulj 抱歉,我的意思是原始列表中的每个值都是正整数,并且我想将该列表转换为具有numpy.uint8值的numpy数组。 - Yinan
显示剩余2条评论
3个回答

3
如果您使用,可以创建一个函数,该函数肯定会更快。但是请注意:如果列表中包含无效元素(不是整数或太大的整数),它将崩溃。
我在这里使用IPython魔术命令(%load_ext cython和%%cython),重点是展示函数的外观-而不是展示如何编译Cython代码(这不难,Cython的“如何编译”文档非常好)。
%load_ext cython

%%cython

cimport cython
import numpy as np

@cython.boundscheck(False)
cpdef to_array(list inp):
    cdef long[:] arr = np.zeros(len(inp), dtype=long)
    cdef Py_ssize_t idx
    for idx in range(len(inp)):
        arr[idx] = inp[idx]
    return np.asarray(arr)

和时间:

import numpy as np

def other(your_list):  # the approach from @Damian Lattenero in the other answer
    ret = np.zeros(shape=(len(your_list)), dtype=int)
    np.copyto(ret, your_list)
    return ret

inp = list(range(1000000))
%timeit np.array(inp)
# 315 ms ± 5.42 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.array(inp, dtype=int)
# 311 ms ± 2.28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit other(inp)
# 316 ms ± 3.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit to_array(inp)
# 23.4 ms ± 1.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

所以它比原来快了10倍以上。


由于这项性能改进,我现在可以以35帧每秒的速度处理视频,最重要的是,不必重写成千上万行的C++代码。谢谢! - Yinan
虽然在我的 Mac 上只快了约 5 倍。 - Yinan
@Yinan 你也可以使用 cdef np.uint8_t[:] arr = np.zeros(len(inp), dtype=np.uint8)(需要同时导入cimport numpy as npimport numpy as np)。该操作基本上是带宽受限的,因此选择可能速度更快的最小dtype可能会加速此过程。我正在使用Windows,所以long是int32,在Mac上可能是int64,这可能解释了为什么你的速度似乎慢了一倍。你的“数字”长度是两倍。 - MSeifert

1

我认为这很快,我已经检查了时间:

import numpy as np
import time
start_time = time.time()

number = 1
elements = 10000000

your_list = [number] * elements

ret = np.zeros(shape=(len(your_list)))
np.copyto(ret, your_list)

print("--- %s seconds ---" % (time.time() - start_time))
--- 0.7615997791290283 seconds ---

为什么遍历整个列表会更快? - Yinan
@user3201090 我认为在copyto中没有重新分配,因为'ret'已经被分配并处于正确的状态,这就是为什么它很快的原因。 - Severin Pappadeux
@SeverinPappadeux,OP在谈论我的先前尝试,使用了一个for循环。 - developer_hatch
@SeverinPappadeux 对于造成的混淆我很抱歉。 - developer_hatch
1
那真的比np.array(your_list)快吗?我进行了一些计时,但并没有更快。请注意,np.zeros会提供一个浮点数数组,但是问题要求一个整数数组。 - MSeifert
1
@MSeifert 我尝试了这种方法。它不比np.array(.)更快。 - Yinan

1

创建一个小整数的大列表,使用numpy库:

In [619]: arr = np.random.randint(0,256, 5000000)
In [620]: alist = arr.tolist()
In [621]: timeit alist = arr.tolist()     # just for reference
10 loops, best of 3: 108 ms per loop

现在是对简单列表进行迭代的时间(不执行任何操作)

In [622]: timeit [i for i in alist]
10 loops, best of 3: 193 ms per loop

创建指定数据类型的数组。
In [623]: arr8 = np.array(alist, 'uint8')
In [624]: timeit arr8 = np.array(alist, 'uint8')
1 loop, best of 3: 508 ms per loop

我们可以通过使用fromiter来获得2倍的性能提升;显然它会进行更少的检查。即使列表中包含数字和字符串,np.array也可以工作。它还可以处理嵌套列表等等。
In [625]: timeit arr81 = np.fromiter(alist, 'uint8')
1 loop, best of 3: 249 ms per loop

使用数组的好处在于当我们对整个数组进行数学运算时变得更加明显:
In [628]: timeit arr8.sum()
100 loops, best of 3: 6.93 ms per loop
In [629]: timeit sum(alist)
10 loops, best of 3: 74.4 ms per loop
In [630]: timeit 2*arr8
100 loops, best of 3: 6.89 ms per loop
In [631]: timeit [2*i for i in alist]
1 loop, best of 3: 465 ms per loop

众所周知,使用数组比使用列表更快,但存在显著的“启动”开销。

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