交换Numpy数组的切片

38 投票
4 回答
13035 浏览
提问于 2025-04-17 16:14

我非常喜欢Python处理变量交换的方式:a, b = b, a

我想用这种功能来交换数组中的值,不仅仅是一个一个地交换,而是一次交换多个值(而且不想使用临时变量)。但是结果并不是我预期的(我希望第三个维度的两个条目都能交换):

import numpy as np
a = np.random.randint(0, 10, (2, 3,3))
b = np.random.randint(0, 10, (2, 5,5))
# display before
a[:,0, 0]
b[:,0,0]
a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:,0,0] #swap
# display after
a[:,0, 0]
b[:,0,0]

有没有人知道怎么做?当然我可以引入一个额外的变量,但我在想有没有更优雅的方法来实现这个。

4 个回答

1

user4815162342的回答确实是“正确的”答案。不过,如果你真的想要一个简洁的解决方案,可以考虑这个:

a[arange(2),0,0], b[arange(2), 0, 0] = b[arange(2), 0, 0], a[arange(2),0,0] #swap

不过,这种方法的效率明显低一些:

In [12]: %timeit a[arange(2),0,0], b[arange(2), 0, 0] = b[arange(2), 0, 0], a[arange(2),0,0]
10000 loops, best of 3: 32.2 µs per loop

In [13]: %timeit t = np.copy(a[:,0,0]); a[:,0,0] = b[:,0,0]; b[:,0,0] = t
The slowest run took 4.14 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 13.3 µs per loop

(但要注意关于“最慢运行”的说明……如果你尝试用“-n 1 -r 1”来调用 %timeit,你会看到更接近的结果——虽然我的方案还是慢了大约50%——这表明缓存确实会影响时间的测量)

11

我觉得这个是最简单的:

a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:, 0, 0].copy() #swap

时间比较:

%timeit a[:,0,0], b[:, 0, 0] = b[:, 0, 0], a[:, 0, 0].copy() #swap
The slowest run took 10.79 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 1.75 µs per loop

%timeit t = np.copy(a[:,0,0]); a[:,0,0] = b[:,0,0]; b[:,0,0] = t
The slowest run took 10.88 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 2.68 µs per loop
30

Python会正确理解你的代码,就好像你使用了额外的变量一样,所以这个交换的代码实际上等同于:

t1 = b[:,0,0]
t2 = a[:,0,0]
a[:,0,0] = t1
b[:,0,0] = t2

但是,即使是这个代码也不能正确交换值!这是因为Numpy的切片操作并不会立即复制数据,它们只是创建了对现有数据的视图。只有在将切片赋值时才会进行复制,但在交换时,如果没有中间的缓冲区,复制操作会破坏你的数据。这就是为什么你不仅需要一个额外的变量,还需要一个额外的Numpy缓冲区,而普通的Python语法对此是无法理解的。例如,下面的代码就能按预期工作:

t = np.copy(a[:,0,0])
a[:,0,0] = b[:,0,0]
b[:,0,0] = t

撰写回答