【问题标题】:numpy.array(list) being slownumpy.array(list) 很慢
【发布时间】:2017-11-12 02:46:28
【问题描述】:

所以我有一个包含 5,000,000 个整数的列表。我想将列表覆盖到一个 numpy 数组。我尝试了以下代码:

numpy.array( list )

但是速度很慢。

我对该操作进行了 100 次基准测试,并在列表中循环了 100 次。没有太大区别。

有什么好办法让它更快吗?

【问题讨论】:

  • 你确定你的列表只包含整数吗?结果数组的 dtype 是什么?
  • 是的,我对此非常积极。它是图像数据的平面数组。所以输入和输出数组都是uint8类型
  • @user3201090 我对我的答案进行了更新,上一个不快,这个是
  • 我很困惑。你说list 是一个整数列表,但是在cmets 中说它是flat array of image datauint8。 Python 整数不是uint8。如果不是ndarray,是不是字节串?
  • 什么是“非常慢”?以交互方式完成时,您会看到延迟这么慢吗?还是在转换 1000 张这些“图像”时变慢?

标签: arrays performance list numpy


【解决方案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 ---

【讨论】:

  • 为什么循环遍历整个列表会更快?
  • @user3201090 我会说copyto中没有重新分配,因为ret已经分配并且形状正确,这就是它快速的原因
  • @SeverinPappadeux,OP 正在谈论我之前的尝试,带有 for 循环
  • 真的比np.array(your_list)快吗?我做了一些计时,并没有更快。请注意,np.zeros 给出了一个 float 数组,但问题指定了一个 int 数组。
  • @MSeifert 我试过这个方法。它并不比 np.array(.) 快。
【解决方案2】:

如果你有,你可以创建一个绝对更快的函数。但只是一个警告:如果您的列表中有无效元素(非整数或太大的整数),它将崩溃。

我在这里使用了 IPython 的魔法(%load_ext cython%%cython),重点是展示函数的外观 - 而不是展示如何编译 Cython 代码(这并不难,Cythons “how-to- compile”的文档非常好)。

%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 fps 的视频,最重要的是,不必用 c++ 重写数千行代码。
  • 虽然它在我的 Mac 上只快 5 倍左右。
  • @Yinan 你也可以使用cdef np.uint8_t[:] arr = np.zeros(len(inp), dtype=np.uint8)(需要cimport numpy as np以及import numpy as np)。该操作基本上是带宽有限的,因此选择可能的最小 dtype 可以显着加快速度。我使用 Windows 这么久在 mac 上是 int32,它可能是 int64,所以它可以解释为什么你的似乎慢了 2 倍。你的“数字”是原来的两倍。
【解决方案3】:

制作一个小整数的大列表;使用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

制作一个指定dtype的数组

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多