用2D数组中的值填充3D NumPy数组的对角线

yxyvkwin  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(127)

我尝试使用numpy.fill_diagonal函数用2D数组的值填充3D NumPy数组。这两个数组具有相同的行数和列数。但是,它会导致错误ValueError: All dimensions of input must be of equal length
下面是一个例子,我试图做什么,我期望有

A = [[[0 1 1 1]
      [1 0 1 1]
      [1 1 0 1]
      [1 1 1 0]]
        
      [[0 1 1 1]
       [1 0 1 1]
       [1 1 0 1]
       [1 1 1 0]]]

B = [[1 4 2 3]
     [3 1 4 2]]

# Actual array shapes
A.shape (35,4,4)
B.shape (35,4)

numpy.fill_diagonal(A, B)

Expected result:
    [[[1 1 1 1]
      [1 4 1 1]
      [1 1 2 1]
      [1 1 1 3]]
        
      [[3 1 1 1]
      [1 1 1 1]
      [1 1 4 1]
      [1 1 1 2]]]

字符串
我已尝试将2D阵列重塑为3D形状,如下所示

numpy.fill_diagonal(A, B[:,:,numpy.newaxis])


但我还是得到了同样的错误

0s7z1bwu

0s7z1bwu1#

fill_diagonal介绍了如何使用diag_indices。具体来说,np.diag_indices(3,ndim=3)为它设置的3个diag值生成3d索引。
让我们来看看2D索引:

In [266]: I,J=np.diag_indices(3,2)
In [267]: I,J
Out[267]: (array([0, 1, 2]), array([0, 1, 2]))

字符串
然后使用3D数组:

In [268]: A = np.zeros((3,3,3))

In [269]: A[:,I,J]
Out[269]: 
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])


选择(3,3)块。然后用a(3,3)设置它们:

In [270]: A[:,I,J]=np.arange(1,10).reshape(3,3)
In [271]: A
Out[271]: 
array([[[1., 0., 0.],
        [0., 2., 0.],
        [0., 0., 3.]],

       [[4., 0., 0.],
        [0., 5., 0.],
        [0., 0., 6.]],

       [[7., 0., 0.],
        [0., 8., 0.],
        [0., 0., 9.]]])


这就是你想要的对角线填充模式。
仔细阅读fill_diagonal等函数的文档。如果有令人困惑的部分,请查看它是用Python编写的,这样您就可以更好地了解它在做什么。

8nuwlpux

8nuwlpux2#

另一种方法,没有花哨的索引,但与大步发挥。

# My mre. I prefer to create array that way. Yours are not reusable (you ommited commas and stuff)
# Plus, it is preferable to have very distinguishable number, or else, I
# may end up, for example, copying the same layer again and again, without
# noticing it, since they are all the same
A=np.arange(27).reshape(3,3,3)
B=np.arange(30,39).reshape(3,3)

# Now we want diagonals of A replaced by B
# The trick for that is to create another view of A, showing only its diagonals
s1,s2,s3=A.strides
DiagA = np.lib.stride_tricks.as_strided(A, shape=B.shape, strides=(s1,s2+s3))

# And change the values of this view with those of B
DiagA[:]=B

# Since the data used by DiagA and A are the same (DiagA is a view, not
# a copy), this alter also A

字符串
棘手的部分显然是DiagA=...行。但它值得理解,因为当你理解它的作用时,它经常会有帮助。这是创建A的视图(因此与A的数据相同),形状以B的形状开始(因此3,3)。和使用给定步幅访问其元素。
ND数组arr的步幅是元组(s1,s2,...,sN),例如元素arr[i1,i2,...,iN]的地址是arr[0,0,...0] + i1*s1+i2*s2+...+iN*sN的地址。
所以A的跨距(s1,s2,s3)是这样的:A[i,j,k]的地址是第一个元素A[0,0,0] + i*s1+j*s2+k*s3的地址。
而(s1 ′,s2 ′)视图DiagA的跨距如DiagA[i,j]的地址是DiagA[0,0] + s1'*i+s2'*j的地址。在这里,我调整了步幅,使s1'=s1s2'=s2+s3。所以Diag[i,j]的地址是Diag[0,0] + s1*i+s2*j+s3*j的地址。这种情况发生,因为它是相同的数据(相同的[0...0]元素,也是A[i,j,j]的地址。
所以DiagA[i,j]只是A[i,j,j]的视图。
这个视图是读写的。所以我可以用DiagA[:]=B来修改那些A[i,j,j]
它的美妙之处在于,它不需要任何成本(当然,DiagA[:]=B的成本是在另一个2D数组上复制2D数组的成本)。但是DiagA的创建本身并没有花费任何成本。没有内存,也没有CPU(或几乎如此)。这只是步幅的一个调整,元数据的一个或两个变化,仅此而已。
Tbh,它需要真正的大数组才值得。Hpaulj的方法对于3x3x3数组快两倍。对于100x100x100阵列,情况大致相同。只有使用200x200x200阵列,我的方法才能达到更快的速度(90 μs vs 120 μs)。当你仔细想想,这两个都是非常快的,考虑到我们谈论的是8000000个元素的数组。因为两者,本质上只是处理它们的40000个元素的副本)

相关问题