我已经分配了一个给定大小的大numpy
数组。例如
my_array = numpy.empty(10000, numpy.float)
可以通过(模拟示例)
生成数组的值k * val ** 2 for val in range(0, 10000)
设置数组值的这一步已完成多次。例如,for k in range(0,1000)
。我不想做任何其他分配,而不是开始时numpy.empty()
所做的分配。
我考虑过,
my_array = numpy.array([k*val**2 for val in range(0,10000)])
但看起来至少会有列表[k * val ** 2 for val in range(0, 10000)]
的分配。是吗?
我也看到numpy.fromiter,但这似乎是用于构建数组。
my_array = numpy.fromiter((k*val**2 for val in range(0,10000)), numpy.float, 10000)
这里有进一步的分配是真的吗?
要查看numpy.fromiter
是否分配了一个数组,我尝试了以下内容
import numpy as np
iterable1 = (x*x for x in range(5))
iterable2 = (x*x + 1.0 for x in range(5))
my_array = np.fromiter(iterable1, np.float)
print(my_array)
print(hex(id(my_array)))
my_array = np.fromiter(iterable2, np.float)
print(my_array)
print(hex(id(my_array)))
在输出I中,打印的两个地址不同。这是不是意味着np.fromiter
分配了一个新的数组,然后将其分配给my_array
?
答案 0 :(得分:2)
首先确保您了解变量赋值的作用:
my_array = numpy.empty(10000, numpy.float)
my_array = numpy.fromiter(...)
第二个作业取代了第一个作业。 my_array
最初引用的对象是免费的并且被垃圾收集。这只是基本的Python变量处理。要挂起原始数组(可变对象),您必须更改其值
my_array[:] = <new values>
但是,生成<new values>
的过程很可能会创建一个临时缓冲区(或两个或三个)。然后将这些值复制到目标。即使x += 1
执行缓冲计算。几乎没有就地的numpy操作。
一般来说,第二次猜测numpy的内存分配并不起作用。效率只能通过时间测试来衡量,而不是通过猜测幕后发生的事情。
不要为预先分配&#39;而烦恼。除非你需要迭代填写它:
In [284]: my_array = np.empty(10, int)
In [285]: for i in range(my_array.shape[0]):
...: my_array[i] = 2*i+3
In [286]: my_array
Out[286]: array([ 3, 5, 7, 9, 11, 13, 15, 17, 19, 21])
与以下相比,这是一种创建阵列的可怕方式:
In [288]: np.arange(10)*2+3
Out[288]: array([ 3, 5, 7, 9, 11, 13, 15, 17, 19, 21])
fromiter
方法更漂亮,但速度更快。
In [290]: np.fromiter((i*2+3 for i in range(10)),int)
Out[290]: array([ 3, 5, 7, 9, 11, 13, 15, 17, 19, 21])
一些时间:
In [292]: timeit np.fromiter((i*2+3 for i in range(10000)),int)
100 loops, best of 3: 4.76 ms per loop
# giving a count drops the time to 4.28 ms
In [293]: timeit np.arange(10000)*2+3
The slowest run took 8.73 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 47.4 µs per loop
In [294]: %%timeit
...: my_array=np.empty(10000,int)
...: for i in range(my_array.shape[0]):
...: my_array[i] = 2*i+3
...:
100 loops, best of 3: 4.72 ms per loop
In [303]: timeit np.array([i*2+3 for i in range(10000)],int)
100 loops, best of 3: 4.48 ms per loop
fromiter
与显式循环一样长,而纯粹的numpy解决方案的速度要快几个数量级。时间方面,np.array
与列表理解和fromiter
与生成器之间几乎没有差异。
从预先存在的列表创建数组大约需要1/3的时间。
In [311]: %%timeit alist=[i*2+3 for i in range(10000)]
...: x=np.array(alist, int)
...:
1000 loops, best of 3: 1.63 ms per loop
将列表分配给现有的empty
数组并不快。
In [315]: %%timeit alist=[i*2+3 for i in range(10000)]
...: arr = np.empty(10000,int)
...: arr[:] = alist
1000 loops, best of 3: 1.65 ms per loop
In [316]: %%timeit alist=[i*2+3 for i in range(10000)]; arr=np.empty(10000,int)
...: arr[:] = alist
1000 loops, best of 3: 1.63 ms per loop
有一些numpy
函数采用out
参数。通过这种方式重用数组可以节省一些时间。 np.cross
是一个利用此功能的函数(代码是Python并且可读)。
另一个&#39;矢量化&#39;从标量函数创建值的方法:
In [310]: %%timeit f=np.frompyfunc(lambda i: i*2+3,1,1)
...: f(range(10000))
...:
100 loops, best of 3: 8.31 ms per loop
答案 1 :(得分:2)
鉴于评论中的解释,似乎问题如下:
第二项是问题:只要您的值来自Python,将它们放入一个numpy数组中永远不会真正有效。这是因为你必须遍历解释代码中的每个值。
我希望找到已经打包在内置函数中的表达式
for ind, elem in enumerate(iterable): my_array[ind] = elem
。你知道Python解释器是否将该表达式编译成一个整体吗?
CPython的虚拟机与C ++模型有很大不同;具体而言,编译器不能内联表达式或将其解释为一个整体,以使其显着提高效率。即使它支持在C中执行此特定操作的字节码指令,它仍然需要调用生成器的next
方法,该方法在执行Python字节后将每个值生成为堆分配的Python对象-码。在任何一种情况下,每次迭代都会涉及解释代码,而您确实希望避免这种情况。
解决问题的有效方法是从头开始设计,永不留下numpy。正如其他人在评论中所解释的那样,分配成本(如果通过numpy有效地完成)与在Python中逐个实际处理数据的成本相比微不足道。我会按如下方式设计:
numpy.fromiter
将迭代器转换为numpy数组。my_array[:] = new_array[:]
或my_array = new_array
将新值引入数组。 (前者将在微观上花费更多时间,但在数据模型中的许多地方共享my_array
时更有意义。)如果在执行上述操作后numpy不支持某些操作,并且测量结果显示效率非常低,则可以使用Python/C API创建一个有效执行计算的扩展模块,并将结果返回为在C中创建的numpy数组。
答案 2 :(得分:1)
np.fromiter
不做任何进一步的分配。它只是从一个可迭代创建一个数组。这是该功能的全部精髓。它还接受count
参数,允许fromiter
预先分配输出数组,而不是根据需要调整它。
此外,如果您想一次更改所有项目,则无需使用np.empty
。
毕竟,如果你通过对一系列数字进行一些数学运算来构建新数组,你也可以简单地对Numpy数组进行操作。
以下是一个例子:
In [4]: a = np.arange(10)
In [6]: a**2 + 10
Out[6]: array([10, 11, 14, 19, 26, 35, 46, 59, 74, 91])