其他分享
首页 > 其他分享> > 为什么其他数组创建例程不需要numpy的fromiter函数需要指定dtype?

为什么其他数组创建例程不需要numpy的fromiter函数需要指定dtype?

作者:互联网

为了提高内存效率,我一直在努力将我的一些代码从列表转换为可能的生成器/迭代器.我发现很多情况下,我只是将代码列表为np.array(some_list)的列表转换为np.array.

值得注意的是,some_list通常是在生成器上迭代的列表理解.

我正在查看np.fromiter,以查看是否可以更直接地使用生成器(而不是必须先将其转换为列表然后将其转换为numpy数组),但是我注意到np.fromiter函数与任何其他函数不同使用现有数据的其他数组创建例程需要指定dtype.

在我的大多数特殊情况下,我可以做到这一点(大多数情况下是处理对数似然性,因此float64会很好),但让我感到奇怪的是,为什么这仅对于fromiter数组创建者而不是其他数组创建者才是必需的.

首先尝试猜测:

内存预分配?

我的理解是,如果您知道dtype和count,它将允许将内存预分配给生成的np.array,并且如果您未指定可选的count参数,它将“按需调整输出数组的大小”.但是,如果您未指定计数,则似乎应该能够像在普通np.array调用中一样,即时推断dtype.

数据类型重铸?

我可以看到这对于将数据重铸为新的dtypes很有用,但这对于其他数组创建例程也同样适用,并且似乎值得将位置作为可选但非必需的参数.

重申问题的几种方法

那么为什么需要指定dtype才能使用np.fromiter?或者换种说法,如果无论如何都要按需调整数组的大小,指定dtype会带来什么好处?

与我的问题更直接相关的同一问题的更微妙版本:
我知道在不断调整np.ndarray的效率时,会损失很多效率,因此,使用np.fromiter(generator,dtype = d)而不是np.fromiter([gen_elem for generator gen_elem]中的收益, dtype = d)在np.array([gen_elem for gen_elem in generator],dtype = d)吗?

解决方法:

如果此代码是十年前编写的,并且没有更改它的压力,那么旧的原因仍然适用.大多数人对使用np.array感到满意. np.fromiter主要用于试图从迭代生成值的方法中加快速度的人.

我的印象是,在决定dtype(和其他属性)之前,主要选择np.array会读取/处理整个输入:

我可以通过更改一个元素来强制返回浮点数:

In [395]: np.array([0,1,2,3,4,5])
Out[395]: array([0, 1, 2, 3, 4, 5])
In [396]: np.array([0,1,2,3,4,5,6.])
Out[396]: array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.])

我使用的不是fromiter,但是我的感觉是通过要求dtype,它可以从一开始就将输入转换为该类型.尽管需要时间测试,但最终可能会产生更快的迭代.

我知道np.array的普遍性需要一定的时间成本.通常,对于小型列表,使用列表理解要比将其转换为数组更快,即使数组操作很快.

一些时间测试:

In [404]: timeit np.fromiter([0,1,2,3,4,5,6.],dtype=int)
100000 loops, best of 3: 3.35 µs per loop
In [405]: timeit np.fromiter([0,1,2,3,4,5,6.],dtype=float)
100000 loops, best of 3: 3.88 µs per loop
In [406]: timeit np.array([0,1,2,3,4,5,6.])
100000 loops, best of 3: 4.51 µs per loop
In [407]: timeit np.array([0,1,2,3,4,5,6])
100000 loops, best of 3: 3.93 µs per loop

差异很小,但表明我的推理是正确的.要求dtype有助于保持更快的速度.这么小的尺寸并没有什么不同.

奇怪的是,为np.array指定dtype会使它变慢.好像它附加了一个astype调用:

In [416]: timeit np.array([0,1,2,3,4,5,6],dtype=float)
100000 loops, best of 3: 6.52 µs per loop
In [417]: timeit np.array([0,1,2,3,4,5,6]).astype(float)
100000 loops, best of 3: 6.21 µs per loop

当我使用range(1000)(Python3生成器版本)时,np.array和np.fromiter之间的差异更加明显.

In [430]: timeit np.array(range(1000))
1000 loops, best of 3: 704 µs per loop

实际上,将范围转换为列表会更快:

In [431]: timeit np.array(list(range(1000)))
1000 loops, best of 3: 196 µs per loop

但是fromiter仍然更快:

In [432]: timeit np.fromiter(range(1000),dtype=int)
10000 loops, best of 3: 87.6 µs per loop

在生成/迭代期间,将int到float转换应用于整个数组的速度比应用于每个元素的速度更快

In [434]: timeit np.fromiter(range(1000),dtype=int).astype(float)
10000 loops, best of 3: 106 µs per loop
In [435]: timeit np.fromiter(range(1000),dtype=float)
1000 loops, best of 3: 189 µs per loop

注意,调整类型的大小操作并不昂贵,仅需20 µs.

===========================

array_fromiter(PyObject * NPY_UNUSED(ignored),PyObject * args,PyObject * keywds)在以下位置定义:

https://github.com/numpy/numpy/blob/eeba2cbfa4c56447e36aad6d97e323ecfbdade56/numpy/core/src/multiarray/multiarraymodule.c

它处理keywds和调用
PyArray_FromIter(PyObject * obj,PyArray_Descr * dtype,npy_intp计数)

https://github.com/numpy/numpy/blob/97c35365beda55c6dead8c50df785eb857f843f0/numpy/core/src/multiarray/ctors.c

这使得使用定义的dtype初始化初始数组:

ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1,
                                            &elcount, NULL,NULL, 0, NULL);

该数组的数据属性以50%的过度分配=>增长. 0、4、8、14、23、36、56、86 …,然后缩小以适合末尾.

该数组的dtype PyArray_DESCR(ret)显然具有一个可以取值(由迭代器提供)的函数,将其转换并将其设置在数据中.

`(PyArray_DESCR(ret)->f->setitem(value, item, ret)`

换句话说,所有dtype转换都由定义的dtype完成.如果代码“动态”决定如何转换值(以及所有先前分配的值),则代码将变得更加复杂.此函数中的大多数代码都用于分配数据缓冲区.

我将推迟查找np.array.我敢肯定它要复杂得多.

标签:memory-efficient,generator,arrays,python,numpy
来源: https://codeday.me/bug/20191119/2035585.html