【问题标题】:numpy: efficient execution of a complex reshape of an arraynumpy:高效执行数组的复杂整形
【发布时间】:2011-07-22 06:52:26
【问题描述】:

我正在将供应商提供的大型二进制数组读入 2D numpy 数组 tempfid(M, N)

# load data
data=numpy.fromfile(file=dirname+'/fid', dtype=numpy.dtype('i4'))

# convert to complex data
fid=data[::2]+1j*data[1::2]

tempfid=fid.reshape(I*J*K, N)

然后我需要使用索引的非平凡映射将其重塑为 4D 数组有用 4d(N,I,J,K)。我按照以下几行使用 for 循环来执行此操作:

for idx in range(M):
    i=f1(idx) # f1, f2, and f3 are functions involving / and % as well as some lookups
    j=f2(idx)
    k=f3(idx)
    newfid[:,i,j,k] = tempfid[idx,:] #SLOW! CAN WE IMPROVE THIS?

转换为复数需要 33% 的时间,而复制这些切片 M 个切片需要剩余的 66%。无论我是在循环中一一执行此操作,还是通过 numpy.vectorizing 操作并将其应用于 arange(M),计算索引都很快。

有没有办法加快速度?任何有关更有效的切片、复制(或不复制)等方面的帮助表示赞赏。

编辑: 正如对问题"What's the fastest way to convert an interleaved NumPy integer array to complex64?" 的回答所了解的那样,如果改用视图,则可以将转换为复杂的速度提高 6 倍:

 fid = data.astype(numpy.float32).view(numpy.complex64)

【问题讨论】:

  • 您是否尝试过矢量化 i,j,k 的计算,然后使用生成的数组在一行中制作副本?
  • @Winston Ewert:这就是我可能失败的地方。我能够向量化 i,j,k 的计算并创建 vec_f1=numpy.vectorize(lambda x: f1(x)) 并获得 i_idx=vec_f1(idx) 等 - 但是,我想不出一个数组的 -line 操作:vec_assign=vectorize(lambda idx:newfid[ *** ]=tempfid[***]) 给出错误,因为 'lambda 不能包含赋值'
  • 另外,如果您使用的是 Python 2.x,并且 M 很大,那么您应该考虑使用 xrange 而不是 range,如果您要进行循环,就像一般规则一样。

标签: python multidimensional-array numpy slice


【解决方案1】:
idx = numpy.arange(M)
i = numpy.vectorize(f1)(idx)
j = numpy.vectorize(f2)(idx)
k = numpy.vectorize(f3)(idx)

# you can index arrays with other arrays
# that lets you specify this operation in one line.    
newfid[:, i,j,k] = tempfid.T

我从未使用过 numpy 的矢量化。 Vectorize 只是意味着 numpy 会多次调用你的 python 函数。为了提高速度,您需要像我在这里展示的那样使用数组操作,而您过去常常得到复数。

编辑

问题是大小128的维度在newfid中是第一个,而在tempfid中是最后一个。这很容易通过使用 .T 进行转置。

【讨论】:

  • 我认为最后一行行不通。例如i = [1,0]; j = [0,1]; b = np.zeros((2,2)); a = np.arange(4); b[i,j] = a[a] 给你一个广播错误。
  • @Winston Ewert:使用这个我得到一个'ValueError:数组不可广播以纠正形状'。请注意,idx,i,j,k 都具有相同的一维长度,并且(在我看来)正确地处理了它们各自的尺寸。
  • @JoshAdel,失败是因为 len(i) != len(a)
  • @DrSar,为所有有问题的数组打印 array.shape 并告诉我它是什么
  • @Winston Ewert:四个维度的形状是一维的,但都具有相同的长度:i=(76800,), j=(76800,), k=(76800,), idx=(76800 ,) 每个维度的最大值不超过它们不应该的值:MAX i=63, j=0, k=1199, idx=76799。我们正在处理的数组的形状是 newfid=(128, 64, 1, 1200) 和 tempfid=(76800, 128)
【解决方案2】:

这个怎么样。使用 f1,f2,f3 的矢量化版本为我们设置您的索引(不一定使用 np.vectorize,但可能只是编写一个接受数组并返回数组的函数),然后使用np.ix_

http://docs.scipy.org/doc/numpy/reference/generated/numpy.ix_.html

获取索引数组。然后将 tempfid 重塑为与 newfid 相同的形状,然后使用np.ix_ 的结果设置值。例如:

tempfid = np.arange(10)
i = f1(idx) # i = [4,3,2,1,0]
j = f2(idx) # j = [1,0]
ii = np.ix_(i,j)
newfid = tempfid.reshape((5,2))[ii]

这会将tempfid 的元素映射到具有不同顺序的新形状上。

【讨论】:

  • @JoshAdel:这看起来很有希望,但是我得到一个“ValueError:广播尺寸太大”。这表明我搞砸了 np.ix_ 业务还是有限制?我正在处理一个 128 x 64 x 1 x 1200 的复数数组
  • @DrSAR:我可以很容易地在我的机器上创建那个大小的空复杂数组。然后,如果我这样做 h = np.empty((128,64,1,1200),dtype=complex); a = np.arange(h.size); a = a+1j*a; ii = np.ix_(range(128),range(64),range(1),range(1200)); h = a.reshape(h.shape)[ii] 一切正常(对不起,连续干扰了这个)。 np.ix_ 你可能会犯错
  • @JoshAdel:我可能是。您在评论中的版本对我有用。但是,当我让 ix_ lost 在 128 x 64 x 1 x 1 的情况下实现时,它可以在没有 ValueError 的情况下工作,但与简单的循环相比,它出奇地慢。事实上,大约是 3000 倍。我还注意到您的操作在我的机器上花费了大约 1.2 秒(没有 ValueError,我同意),其中大部分花费在使用索引的数组分配中。 ix_ 应该方便还是快捷?或者两者兼而有之?
  • @DrSAR:Python for 循环通常很慢(参考您对 Winston 帖子的评论),有时索引技巧可以更快。但显然在这种情况下,重塑或幕后发生的其他事情会产生很大的开销。如果这些索引技巧没有为您提供比 for 循环更好的性能,您可以考虑使用 Cython 进行转换。
  • @JoshAdel:非常感谢您一直以来的帮助 - 我感觉我现在已经尝试并用尽了普通 python/numpy 中的大多数选项,并且在不诉诸外部化的情况下我可以达到性能极限这个关键位。在这个过程中我学到了很多;谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多