在3D列表和numpy数组之间解包和赋值的奇怪情况

huangapple go评论54阅读模式
英文:

Unpacking and assignment oddity between 3D list and numpy array

问题

I get the following, which is what I want

print(a)
[[[5, 6], [3, 4]], [[9, 10], [7, 8]], [[1, 2], [11, 12]]]

Now, I do the same thing, this time, using numpy arrays

b = np.arange(1,13).reshape((3,2,2))
print(b)
[[[ 1 2]
[ 3 4]]

[[ 5 6]
[ 7 8]]

[[ 9 10]
[11 12]]]

The shift is the same as before but using numpy indexing syntax

b[0,0],b[1,0],b[2,0] = b[1,0],b[2,0],b[0,0]
print(b)
[[[ 5 6]
[ 3 4]]

[[ 9 10]
[ 7 8]]

[[ 5 6]
[11 12]]]

英文:

I create a normal python list

a = [[[1,2],[3,4]],[[5,6],[7,8]],[[9,10],[11,12]]]

Now I want to shift the first row of each 2x2 array to the previous 2x2 array, wrapping the first back to the last. I use the following unpacking assignment statement:

a[0][0],a[1][0],a[2][0] = a[1][0],a[2][0],a[0][0]

I get the following, which is what I want

print(a)
[[[5, 6], [3, 4]], [[9, 10], [7, 8]], [[1, 2], [11, 12]]]

Now, I do the same thing, this time, using numpy arrays

b = np.arange(1,13).reshape((3,2,2))
print(b)
[[[ 1  2]
  [ 3  4]]

 [[ 5  6]
  [ 7  8]]

 [[ 9  10]
  [11 12]]]

The shift is the same as before but using numpy indexing syntax

b[0,0],b[1,0],b[2,0] = b[1,0],b[2,0],b[0,0]
print(b)
[[[ 5  6]
  [ 3  4]]

 [[ 9 10]
  [ 7  8]]

 [[ 5  6]
  [11 12]]]

As you can see, this assignment left the first and last 2x2 arrays with the same first row.

Would you expect this behavior difference? Why does numpy do this but not the regular list type? How could the numpy assignment be done to result in the same result as the list?

答案1

得分: 4

我推理的方式是,由于 b 是一个 (3,2,2) 数组,

每个

b[1,0],b[2,0],b[0,0]

都是 b 的 'view'。也就是说,3 个 (2,) 数组,但每个都使用了 b 数据缓冲区的不同部分。

在赋值过程中,将 b[0,0] 设置为 b[1,0] 处的值。当它分配给 b[2,0] 时,B[0,0] 现在具有新的值 [5,6]

我以前偶尔见过这种情况。这是另一种数组与列表不完全相同的情况。列表包含对列表的指针,而数组索引要么生成副本,要么生成视图。视图很方便,但可能会影响到基于列表/引用的直觉。

幸运的是,数组可以一次赋值多个值,所以我们可以这样做:

b[[0,1,2],0] = b[[1,2,0],0]

或者等价的切片方式 b[:,0]=b[::-1,0]

先前的问题更侧重于正确的交换方式,而不是为什么存在差异。因此不会将此标记为重复。

https://stackoverflow.com/questions/14933577/swap-slices-of-numpy-arrays

https://stackoverflow.com/questions/21288044/row-exchange-in-numpy/21288605

英文:

The way I reason this out is that since b is a (3,2,2) array

each of

b[1,0],b[2,0],b[0,0]

is a view of b. That is, 3 (2,) arrays, but each uses a different part of the b data_buffer.

During the assignment, b[0,0] is set to the values at b[1,0]. By the time it assigns to b[2,0], B[0,0] now has the new values, [5,6].

I've seen this on occasion before. It's another case where arrays aren't exactly the same as lists. Lists contain pointers to lists, where as array indexing either produces copies or views. Views are convenient, but can mess with list/reference based intuitions.

Fortunately arrays can assign several values at once, so we can do:

b[[0,1,2],0] = b[[1,2,0],0]

or the equivalent with slices b[:,0]=b[::-1,0]

Earlier questions focus more on the correct way of doing the swap, and less on why there's a difference. So won't mark this as duplicate

https://stackoverflow.com/questions/14933577/swap-slices-of-numpy-arrays

https://stackoverflow.com/questions/21288044/row-exchange-in-numpy/21288605

答案2

得分: 3

在Python中,列表(list)在内部实际上是一组指向对象的指针!当你使用 a[1][0],a[2][0],a[0][0] 这样的语法时,你实际上创建了一个包含三个对象的元组 - 分别是指向列表 [5,6]、指向列表 [9,10] 和指向列表 [1,2] 的指针。然后,你可以使用 a[0][0],a[1][0],a[2][0] = ... 将这些指针分配给 a 中的位置。

而NumPy不同。切片代表的是内存中的位置,而不是对象的指针。下面的代码与你的示例相同:当你将 b[0,0] 赋值为 5,6 时,b[0,0] 引用的内存中的值已经改变。因此,当你将 b[2,0] 赋值为 b[0,0] 中的值时,你得到的是 5,6

import numpy as np
b = np.arange(1,13).reshape((3,2,2))
rhs = b[1,0],b[2,0],b[0,0]
print("before first assign", rhs[2])
b[0,0] = rhs[0]
print("after first assign", rhs[2])
b[1,0] = rhs[1]
b[2,0] = rhs[2]
print(b)

输出如下:

before first assign [1 2]
after first assign [5 6]
[[[ 5  6]
  [ 3  4]]

 [[ 9 10]
  [ 7  8]]

 [[ 5  6]
  [11 12]]]
英文:

Simple: What is a python list under the hood? A bunch of pointers to objects! When you say a[1][0],a[2][0],a[0][0], you are (basically) creating a tuple of three objects - a pointer to the list [5,6], a pointer to the list [9,10], and a pointer to the list [1,2]. You then assign these pointers to spots in a with a[0][0],a[1][0],a[2][0] = ...

Numpy is different. Slices represent locations in memory, not pointers to objects. The statement below is identical to your code: when you assign b[0,0] to 5,6, the value in memory referenced by b[0,0] has changed. Therefore, when you assign b[2,0] to what's in b[0,0] you get 5,6.

b = np.arange(1,13).reshape((3,2,2))
rhs = b[1,0],b[2,0],b[0,0]
print("before first assign", rhs[2])
b[0,0] = rhs[0]
print("after first assign", rhs[2])
b[1,0] = rhs[1]
b[2,0] = rhs[2]
print(b)
before first assign [1 2]
after first assign [5 6]
[[[ 5  6]
  [ 3  4]]

 [[ 9 10]
  [ 7  8]]

 [[ 5  6]
  [11 12]]]

huangapple
  • 本文由 发表于 2023年6月12日 05:55:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76452656.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定