使3维numpy数组的每第n个切片连续。

3

说明

假设我们有一个三维 Numpy 数组 A,其形状为 (X, Y, Z)。现在我们想要创建一个新数组 B,也具有形状 (X, Y, Z)

我们希望 B 的前 n 个(:n)零轴切片与每个 m 切片(::m)的零轴切片对应于 A

我们还希望 B 的切片 n:2*n 对应于 A 的每个 m+1 切片(1::m)。对于数组的其余部分也是如此。

使用向量化的 Numpy 计算,最好的方法是什么?

示例

通过以下示例更易理解上面的语句。让我们从建立一些示例数组 A 开始:

import numpy as np

# Create array A with shape (15, 3, 3)
n = 3; m = 5
a = np.array([i * np.eye(3) for i in range(1, 1+m)])
A = np.tile(a, (n, 1, 1))

如果我们查看 A 的某些零切片,则有以下内容:
print(A[0])
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
print(A[1])
[[2. 0. 0.]
 [0. 2. 0.]
 [0. 0. 2.]]

...

print(A[4])
[[5. 0. 0.]
 [0. 5. 0.]
 [0. 0. 5.]]
print(A[5])
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

等等。

A 中的值不重要,但应有助于说明原始语句。

我想知道是否可以仅使用numpy函数创建矩阵B。 数组B 应具有切片:

print(B[0])
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
print(B[1])
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
print(B[2])
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
print(B[3])
[[2. 0. 0.]
 [0. 2. 0.]
 [0. 0. 2.]]

等等。

有没有一种使用纯numpy解决方案从A生成B的方法?

我尝试过的

下面的代码可以按预期生成B,但是当m变大时会变得很繁琐:

# vstack solution
B = np.vstack((A[::m], A[1::m], A[2::m], A[3::m], A[4::m]))

使用列表推导式也可以,但我希望避免使用循环:
# List comprehension solution
B = np.vstack([A[i::m] for i in range(m)])

那么在你的例子中,n 是 3 吗?但是当它说 print(B[4])print(B[5]) 时,不应该是 print(B[2])print(B[3]) 吗? - jdehesa
抱歉,编辑不够仔细。您是正确的。我会修改这篇文章。 - jwalton
3个回答

1
我认为这样做可以满足您的需求:

我认为这样做可以满足您的需求:

import numpy as np

# Create array A with shape (15, 3, 3)
a = np.array([i * np.eye(3) for i in range(1, 6)])
A = np.tile(a, (3, 1, 1))

B = np.swapaxes(A.reshape(3, 5, 3, 3), 0, 1)
B = B.reshape(-1, 3, 3)
print(B)
# [[[1. 0. 0.]
#   [0. 1. 0.]
#   [0. 0. 1.]]
#
#  [[1. 0. 0.]
#   [0. 1. 0.]
#   [0. 0. 1.]]
#
#  [[1. 0. 0.]
#   [0. 1. 0.]
#   [0. 0. 1.]]
#
#  [[2. 0. 0.]
#   [0. 2. 0.]
#   [0. 0. 2.]]
# ...

非常好。谢谢,这是一个很棒的解决方案。将形状重塑为4D并交换轴非常聪明! - jwalton

1
如果我理解正确,也许你可以只使用 numpy.sort 函数:
B = np.sort(A, axis = 0)

嗨,感谢您提供的解决方案。您说得对,它将A转换为了示例中的B。但是,数组中的数字并不重要,这并不能解决我在问题开头提出的更一般的陈述。我认为这是我的错,因为示例可能会误导人。@jdehesa 在下面提供了一个很好的解决方案。 - jwalton

1
安装设置。
n = 3; m = 5
a = np.array([i * np.eye(n) for i in range(1, 1+m)])

不要使用tile,而是沿着轴0使用np.repeat,并使用Fortran样式的顺序进行重塑。

np.repeat(a, n, 0).reshape(m*n, n, n, order='F')

array([[[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]],

       [[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]],

       [[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]],

       [[2., 0., 0.],
        [0., 2., 0.],
        [0., 0., 2.]],    
       ...    
       [[5., 0., 0.],
        [0., 5., 0.],
        [0., 0., 5.]]])

验证。请回答不超过200个字符。
# your approach
A = np.tile(a, (n, 1, 1))
B = np.vstack((A[::m], A[1::m], A[2::m], A[3::m], A[4::m]))

# my approach
usr_B = np.repeat(a, n, 0).reshape(m*n, n, n, order='F')

>>> np.array_equal(B, usr_B)
True

时间安排
%%timeit
A = np.tile(a, (n, 1, 1))
B = np.vstack((A[::m], A[1::m], A[2::m], A[3::m], A[4::m]))
19 µs ± 57.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%%timeit
A = np.tile(a, (3, 1, 1))
B = np.swapaxes(A.reshape(3, 5, 3, 3), 0, 1)
B = B.reshape(-1, 3, 3)
11 µs ± 74.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit np.repeat(a, n, 0).reshape(m*n, n, n, order='F')
2.68 µs ± 21.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

谢谢你的回答。也许你可以为我澄清一些我认为我不理解的问题。在你的解决方案中,我没有看到任何时候创建数组A --- 这是这个问题的起点。我对 .reshape(m*n, n, n, order='F') 命令的目的也不是很清楚:它似乎并没有改变 np.repeat(a, n, 0) - jwalton
@jwalton3141 你不需要创建A,使用这个解决方案,我只使用原始数组a来获得你想要的输出。重塑工作正常,你只需要将结果重新分配给另一个变量以查看输出,它不会在原地修改数组。 - user3483203
我明白了。我想我的问题可能误导了您,因为A是这个问题的起点。我想知道如何从A到达B,而不是从a到达B。虽然我在问题中试图表达清楚这一点。 - jwalton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接