【问题标题】:Numpy - efficiently building 2d array from 1d arraysNumpy - 从一维数组有效地构建二维数组
【发布时间】:2015-07-13 14:20:48
【问题描述】:

我正在从 numpy (Python 2.7) 中的一维数组构建二维数组。我正在寻找最有效的方法来做到这一点。最新我想出了:

a=np.ones(100000)

#   SUBSCRIPTING

n_dim=3
x=0

for i in xrange(0,1000):
    x=np.zeros(shape=(100000,n_dim))
    for j in xrange(0,n_dim):
        x[:,j]=a*j

# ~1.574 s/1000 loops - joinind 3 1d arrays
# ~9.162 s/1000 loops - joinind 10 1d arrays



#   STACKING

for i in xrange(0,1000):
    x=a*0.
    for j in xrange(1,n_dim):
        x=np.vstack((x,a*j))
    x=x.T

# ~1.786 s/1000 loops - joinind 3 1d arrays
# ~16.603 s/1000 loops - joinind 10 1d arrays

第一种方法(下标)是我想出的最快的方法,第二种方法(堆叠)的性能增益随着我加入的一维数组的数量而增长。由于我需要多次重复此步骤,我想知道是否有更快的方法?如果它提供了显着的性能提升,我愿意采用失去清晰度的解决方案。

也许我可以尝试以限制堆叠操作次数的方式堆叠数组(例如,加入 4 个一维数组的情况:首先堆叠数组 1 和 2,然后是数组 3 和 4,最后是结果数组)。

我的问题是关于从一维数组有效地构建二维数组。我在这里使用的数组中的值是虚拟的。在实际应用中,我加入的一维数组中的大多数值可能会有所不同。

【问题讨论】:

  • 你能提供一个输入较小的例子吗?
  • 是关于一般的堆叠,还是关于您显示的计算?

标签: python arrays python-2.7 numpy


【解决方案1】:

因为 numpy 在row-major order 中存储(默认)数组,所以按行设置值更有效。因此,我会使用:

x=np.zeros(shape=(n_dim, 100000))
for j in range(0,n_dim):
    x[j,:]=a*j

或者,您可以将x 定义为列优先,然后,这与之前的代码一样快:

x=np.zeros(shape=(100000,n_dim), order='F')
for j in range(0,n_dim):
    x[:,j]=a*j

您也可以使用 numpy 外积创建 x:

v = np.arange(n_dim)
x = np.outer(v, a)

【讨论】:

  • 将 x=np.zeros(shape=(100000,n_dim)) 更改为 x=np.zeros(shape=(100000,n_dim), order='F') 提供了显着的加速。 1000 个循环对于 3 个维度需要 1.344 秒,对于 10 个维度需要 4.672 秒。谢谢。外部产品对我来说不是一个选项,因为在实际情况下,合并的一维数组中的所有条目都会有所不同。
【解决方案2】:

这是使用vstack 的糟糕方式;您反复调用它,为每个 j 创建一个新的 x

x=a*0.
for j in xrange(1,n_dim):
    x=np.vstack((x,a*j))
x=x.T

正确的方法是建立一个数组列表,并且只使用一次vstack。

xlist=[]
for j in xrange(1,n_dim):
    xlist.append(a*j)
x = np.array(xlist).T

在这种情况下,append 的效果与vstack 一样好,而且可能更快。还有一个column_stack 函数。主要区别在于我利用了快速列表追加,以及array(和vstack)在其参数列表中获取许多项目的能力。

如果你能把循环写成列表理解就更好了

x = np.array([a*j for j in xrange(1,n_dim)])

在预分配数组中插入通常是最快的选择。但是您应该熟悉这种从列表构建的方法。

基本的np.array 表达式

np.array([[1,2,3],[4,5,6]])

就是这样,从一维数组列表(或在本例中为列表)构建二维。

np.array([a*0,a*1,a*2])

jakub 注意到np.array 很慢。对于n_dim=10:

In [257]: timeit x=np.array([(a*j) for j in range(n_dim)]).T
1 loops, best of 3: 228 ms per loop

In [258]: timeit x=np.array([(a*j).tolist() for j in range(n_dim)]).T
1 loops, best of 3: 228 ms per loop

显然np.array 正在将输入数组转换为列表,然后从嵌套列表(或类似的东西)进行通常的构造。

In [259]: timeit x=np.vstack([(a*j) for j in range(n_dim)]).T
10 loops, best of 3: 24.9 ms per loop

vstack 在数组列表上的速度要快得多。比迭代的vstack(我预期的)更快。和Ramon's的行插入基本相同(以及插入order='F')

In [272]: %%timeit
x=np.zeros((n_dim,a.shape[0]))
for j in range(n_dim):
   x[j,:]=a*j
   .....: x=x.T
   .....: 
10 loops, best of 3: 23.3 ms per loop

虽然concatenate(由vstack 使用)被编译,但我怀疑它的作用类似于迭代插入。在源 C 代码中创建一个空的目标数组,然后用适当的值填充它是很常见的。

【讨论】:

  • 我尝试了您的解决方案。它在 3 个维度上运行大约 29 s/1000 个循环。对于 10 个维度,它是 96.638s/1000 个循环。所以它可能很优雅,但似乎效率极低。
  • 我做了一些计时。在这种情况下(加入数组),np.array 表现不佳。但是vstack 在整个列表中的表现相当不错。
  • 我也在类似的设置中进行了计时,正如您所写,它是 Ramon 提出的插入次佳。时序仅供参考,10维:5.5s/1000循环,3维:1.4s/1000循环。我不知道将顺序更改为“F”如何影响我随后的计算。我将检查这两种方法中的哪一种(Raymon 的插入和您的 vstack)为整个应用程序带来了最大的性能提升。
猜你喜欢
  • 2011-06-22
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 2021-06-24
  • 1970-01-01
  • 2016-12-27
  • 2012-11-06
相关资源
最近更新 更多