Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
151 views
in Technique[技术] by (71.8m points)

python - Create a permuted shallow copy of a numpy array

I am looking to have two different views of the same data with the rows in a different order such that changes done through one view will be reflected in the other. Specifically, the following code

# Create original array
A = numpy.array([[0, 1, 2],
                 [3, 4, 5],
                 [6, 7, 8]])
B = A.view()[[0, 2, 1], :] # Permute the rows
print("(before) B =
", B)

# Change a value in A
A[1, 2] = 143
print("(after) A =
", A)
print("(after) B =
", B)

has the following output:

(before) B =
 [[0 1 2]
  [6 7 8]
  [3 4 5]]
(after) A =
 [[  0   1   2]
  [  3   4 143]
  [  6   7   8]]
(after) B =
 [[0 1 2]
  [6 7 8]
  [3 4 5]]

but I would like the last bit of that to be

(after) B =
 [[0   1   2]
  [6   7   8]
  [3   4 143]]

Answers to this question state that getting a view at specific indices is not possible, though the OP for that question is asking about a subset of the array, whereas I would like a view of the entire array. (It seems that the key difference here is slicing vs. smart indexing)

A different post asking about slicing by rows and then columns vs columns and then rows has an accepted answer that states "All that matters is whether you slice by rows or by columns...". So I tried dealing with a flattened view of the array..

A = numpy.array([[0, 1, 2],
                 [3, 4, 5],
                 [6, 7, 8]])
B = A.view()
B.shape = (A.size,)

A[1, 2] = 198
print("(After first) A =
", A)
print("(After first) B =
", B)

# Identity index map
all_idx = numpy.arange(A.size).reshape(A.shape)

# Swapped and flattened index map
new_row_idx = all_idx[[0, 2, 1]].flatten()

C = B[new_row_idx]

print("(Before second) C =
", C)

# Manipulate through 'B'
B[7] = 666

print("(After second) B =
", B)
print("(After second) C =
", C)

which gives the following output:

(After first) A =
 [[  0   1   2]
 [  3   4 198]
 [  6   7   8]]
(After first) B =
 [  0   1   2   3   4 198   6   7   8]
(Before second) C =
 [  0   1   2   6   7   8   3   4 198]
(After second) B =
 [  0   1   2   3   4 198   6 666   8]
(After second) C =
 [  0   1   2   6   7   8   3   4 198]

As you can see, the 4th entry of C is unaltered. The suggested solution to the first post I mentioned is to create a copy, make changes, and then update the original array. I can write functions to wrap this, but this doesn't eliminate the number of times I will be making copies. All it does is hide it from the user.

What am I missing here? Should I be using the data attribute of these arrays? If so, what is a good starting point for understanding how to do this?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

An array has a shape, strides, dtype and 1d data_buffer. A view will have its own shape, strides, dtype, and pointer to some place in the base's data_buffer. Indexing with a slice can be achieved with just these attributes.

But indexing with a list such as your [0,2,1] cannot be achieved this way. So numpy makes a new array with its own data_buffer, a copy. That [0,2,1] index list/array is not stored with the copy.

In [43]: A = np.arange(9).reshape(3,3)
In [44]: B = A[[0,2,1],:]
In [45]: A
Out[45]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
In [46]: B
Out[46]: 
array([[0, 1, 2],
       [6, 7, 8],
       [3, 4, 5]])

ravel shows the order of elements in the data_base:

In [47]: A.ravel()
Out[47]: array([0, 1, 2, 3, 4, 5, 6, 7, 8])

The order of elements in B is different.

In [48]: B.ravel()
Out[48]: array([0, 1, 2, 6, 7, 8, 3, 4, 5])

In contrast, consider a row reordering with a slice:

In [49]: C = A[::-1,:]
In [50]: C
Out[50]: 
array([[6, 7, 8],
       [3, 4, 5],
       [0, 1, 2]])

In [52]: A.strides
Out[52]: (24, 8)

This is achieved by simply changing the strides:

In [53]: C.strides
Out[53]: (-24, 8)

Transpose is also a view, with changed strides:

In [54]: D = A.T
In [55]: D.strides
Out[55]: (8, 24)

I was going to show the C.ravel(), but realized that reshape makes a copy (even though C is a view).

The fundamental point is that anything that numpy describes as advanced indexing will make a copy. Changes to the copy will not appear in the original array. https://numpy.org/doc/stable/reference/arrays.indexing.html#advanced-indexing


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...