在我目前的工作中,我经常使用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()之前创建的值的中间列表吗?
祝你好运 尼尔斯
答案 0 :(得分:38)
我相信您正在寻找的答案是generator expressions
与numpy.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