27

我有一长串 xy 坐标,并想将其转换为 numpy 数组。

>>> import numpy as np
>>> xy = np.random.rand(1000000, 2).tolist()

显而易见的方法是:

>>> a = np.array(xy) # Very slow...

但是,上面的代码速度慢得不合理。有趣的是,首先转置长列表,将其转换为 numpy 数组,然后转回会快得多(在我的笔记本电脑上是 20 倍)。

>>> def longlist2array(longlist):
...     wide = [[row[c] for row in longlist] for c in range(len(longlist[0]))]
...     return np.array(wide).T
>>> a = longlist2array(xy) # 20x faster!

这是numpy的错误吗?

编辑:

这是一个动态生成的点列表(带有 xy 坐标),因此我认为当前的表示是最自然的,而不是预先分配一个数组并在必要时扩大它,或者为 x 和 y 维护两个 1D 列表。

考虑到我们在两个方向上遍历 python 列表,为什么循环第二个索引比第一个索引快?

编辑2:

根据@tiago 的回答和这个问题,我发现以下代码的速度是原始版本的两倍:

>>> from itertools import chain
>>> def longlist2array(longlist):
...     flat = np.fromiter(chain.from_iterable(longlist), np.array(longlist[0][0]).dtype, -1) # Without intermediate list:)
...     return flat.reshape((len(longlist), -1))
4

3 回答 3

6

这是因为列表中变化最快的索引是最后一个,因此np.array()必须多次遍历数组,因为第一个索引要大得多。如果您的列表被转置,np.array()将比您的更快longlist2array

In [65]: import numpy as np

In [66]: xy = np.random.rand(10000, 2).tolist()

In [67]: %timeit longlist2array(xy)
100 loops, best of 3: 3.38 ms per loop

In [68]: %timeit np.array(xy)
10 loops, best of 3: 55.8 ms per loop

In [69]: xy = np.random.rand(2, 10000).tolist()

In [70]: %timeit longlist2array(xy)
10 loops, best of 3: 59.8 ms per loop

In [71]: %timeit np.array(xy)
1000 loops, best of 3: 1.96 ms per loop

您的问题没有神奇的解决方案。这就是 Python 将列表存储在内存中的方式。你真的需要一个具有这种形状的列表吗?不能逆转吗?(考虑到您正在转换为 numpy,您真的需要一个列表吗?)

如果你必须转换一个列表,这个函数比你的快 10% longlist2array

from itertools import chain

def convertlist(longlist)
    tmp = list(chain.from_iterable(longlist))
    return np.array(tmp).reshape((len(longlist), len(longlist[0])))
于 2013-07-31T15:04:09.920 回答
6

在 Cython 中实现这一点,无需额外检查以确定维度等,几乎消除了您所看到的时间差异。这是.pyx我用来验证的文件。

from numpy cimport ndarray as ar
import numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def toarr(xy):
    cdef int i, j, h=len(xy), w=len(xy[0])
    cdef ar[double,ndim=2] new = np.empty((h,w))
    for i in xrange(h):
        for j in xrange(w):
            new[i,j] = xy[i][j]
    return new

我假设额外的时间用于检查每个子列表的长度和内容,以确定所需数组的数据类型、维度和大小。当只有两个子列表时,它只需要检查两个长度来确定数组中的列数,而不是检查其中的 1000000 个。

于 2013-07-31T19:06:21.540 回答
3

如果你有 pandas,你可以使用pandas.lib.to_object_array(),这是最快的方法:

import numpy as np
import pandas as pd
a = np.random.rand(100000, 2)
b = a.tolist()

%timeit np.array(b, dtype=float, ndmin=2)
%timeit np.array(b, dtype=object).astype(float)
%timeit np.array(zip(*b)).T
%timeit pd.lib.to_object_array(b).astype(float)

输出:

1 loops, best of 3: 462 ms per loop
1 loops, best of 3: 192 ms per loop
10 loops, best of 3: 39.9 ms per loop
100 loops, best of 3: 13.7 ms per loop
于 2013-08-01T10:06:30.630 回答