【问题标题】:Create a permuted shallow copy of a numpy array创建一个 numpy 数组的置换浅表副本
【发布时间】:2021-04-12 22:21:30
【问题描述】:

我希望对同一数据有两个不同的视图,其中行的顺序不同,这样通过一个视图所做的更改将反映在另一个视图中。具体如下代码

# 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 =\n", B)

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

有以下输出:

(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]]

但我希望最后一点是

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

this question 的回答指出,无法在 特定索引 处查看视图,尽管该问题的 OP 询问的是数组的一个子集,而我想要查看整个阵列。 (看来这里的关键区别是切片与智能索引)

different post 询问关于按 行然后列列然后行 进行切片的问题,得到了一个公认的答案,即 “重要的是你是否按行或按列切片..."。所以我尝试处理数组的扁平视图..

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 =\n", A)
print("(After first) B =\n", 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 =\n", C)

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

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

给出以下输出:

(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]

如您所见,C 的第 4 个条目没有改变。 first post I mentioned 的建议解决方案是创建一个副本,进行更改,然后更新原始数组。我可以编写函数来包装它,但这并不能消除我复制的次数。它所做的只是对用户隐藏它。

我在这里缺少什么?我应该使用这些数组的data 属性吗?如果是这样,了解如何执行此操作的良好起点是什么?

【问题讨论】:

  • 小心第二个链接中的 Zwinck 答案。它狭隘地解决了 OP 问题,不应被视为一般解释。阅读 cmets。
  • 基本上没有办法做到这一点,至少在这个问题的狭隘范围内。 Numpy 在连续的内存块上工作;有一些变通方法,但数据的任意排列总是会破坏连续性。真的就这么简单……
  • 但是,如果您的基本目标是以置换的方式写入数组,那么您可以很容易地做到这一点——比在问题底部的代码中要容易得多。 (也就是说,除非我误解了您的目标。)但这与您提出的问题完全不同。
  • 你想对你的置换数组做什么样的操作?像这样简单的一对一作业还是更复杂的事情?你可以子类化。
  • 我的目标是支持不同库使用的不同节点 ID 编号方案,这些库支持有限元网格上的操作。节点坐标的集合没有改变,但分配给它们的全局 ID 不同。我想在不复制网格数据的情况下执行此操作,因为在某些情况下这可能会相当大。

标签: python numpy numpy-slicing numpy-indexing


【解决方案1】:

一个数组有一个shapestridesdtype 和一维数据缓冲区。 view 将拥有自己的shapestridesdtype,以及指向基址数据缓冲区中某个位置的指针。仅使用这些属性即可实现使用slice 进行索引。

但是无法以这种方式实现使用 [0,2,1] 之类的列表进行索引。所以numpy 用自己的data_buffer 创建了一个新数组,即copy。 [0,2,1] 索引列表/数组不与副本一起存储。

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 显示数据库中元素的顺序:

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

B中的元素顺序不同。

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

相比之下,考虑使用切片重新排序的行:

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)

这可以通过简单地改变strides来实现:

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

Transpose 也是一个视图,但步幅发生了变化:

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

我打算展示 C.ravel(),但意识到 reshape 会复制(尽管 C 是一个视图)。

基本点是numpy 描述为advanced indexing 的任何内容都会复制。对副本的更改不会出现在原始数组中。 https://numpy.org/doc/stable/reference/arrays.indexing.html#advanced-indexing

【讨论】:

    猜你喜欢
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 2015-07-23
    • 2013-12-06
    • 2017-12-30
    • 2023-03-22
    相关资源
    最近更新 更多