从列表理解和一般情况下有效地创建numpy数组

时间:2013-01-17 05:17:46

标签: python performance numpy

在我目前的工作中,我经常使用Numpy和列表理解,为了获得最佳表现,我有以下问题:

如果我按如下方式创建Numpy数组,幕后实际发生了什么? :

a = numpy.array( [1,2,3,4] )

我的猜测是python首先创建一个包含值的普通列表,然后使用列表大小来分配一个numpy数组,然后将值复制到这个新数组中。这是正确的,还是解释器聪明到足以意识到列表只是中介而是直接复制值?

同样,如果我希望使用numpy.fromiter()从列表推导中创建一个numpy数组:

a = numpy.fromiter( [ x for x in xrange(0,4) ], int )

这会导致在被送入fromiter()之前创建的值的中间列表吗?

祝你好运 尼尔斯

3 个答案:

答案 0 :(得分:38)

我相信您正在寻找的答案是generator expressionsnumpy.fromiter一起使用。

numpy.fromiter((<some_func>(x) for x in <something>),<dtype>,<size of something>)

生成器表达式是惰性的 - 它们在迭代它们时会计算表达式。

使用列表推导生成列表,然后将其提供给numpy,而生成器表达式将一次生成一个。

Python评估内部的事物 - &gt; out,就像大多数语言一样(如果不是全部的话),所以使用[<something> for <something_else> in <something_different>]会生成列表,然后迭代它。

答案 1 :(得分:8)

您可以创建自己的列表并进行实验,以了解情况......

>>> class my_list(list):
...     def __init__(self, arg):
...         print 'spam'
...         super(my_list, self).__init__(arg)
...   def __len__(self):
...       print 'eggs'
...       return super(my_list, self).__len__()
... 
>>> x = my_list([0,1,2,3])
spam
>>> len(x)
eggs
4
>>> import numpy as np
>>> np.array(x)
eggs
eggs
eggs
eggs
array([0, 1, 2, 3])
>>> np.fromiter(x, int)
array([0, 1, 2, 3])
>>> np.array(my_list([0,1,2,3]))
spam
eggs
eggs
eggs
eggs
array([0, 1, 2, 3])

答案 2 :(得分:0)

针对标题中的问题,现在有一个支持numpy array comprehension的numba包,直接构造numpy数组,没有中间python列表。与 numpy.fromiter 不同,它还支持嵌套理解。但是,如果您不熟悉 numba,请记住它有一些限制和性能怪癖。

也就是说,它可以非常快速和高效,但是如果您可以使用 numpy 的向量运算来编写它,最好让它更简单。

>>> from timeit import timeit
>>> # using list comprehension
>>> timeit("np.array([i*i for i in range(1000)])", "import numpy as np", number=1000)
2.544344299999999
>>> # using numpy operations
>>> timeit("np.arange(1000) ** 2", "import numpy as np", number=1000)
0.05207519999999022
>>> # using numpy.fromiter
>>> timeit("np.fromiter((i*i for i in range(1000)), dtype=int, count=1000)",
...        "import numpy as np",
...        number=1000)
1.087984500000175
>>> # using numba array comprehension
>>> timeit("squares(1000)",
... """
... import numpy as np
... import numba as nb
... 
... @nb.njit
... def squares(n):
...     return np.array([i*i for i in range(n)])
... 
... 'compile the function'
... squares(10)
... """,
... number=1000)
0.03716940000003888