使用np.ravel()处理数组视图会创建副本?如何修改原数组?
问题:如何让numpy ravel()返回的数组修改同步到原数组?
我有一个全零填充的numpy数组,想要创建其视图后调用ravel()方法,修改ravel()返回的数组并让修改同步到原数组。不使用ravel()时逻辑正常:
import numpy as np zeros = np.zeros(shape=(10,10)) view = zeros[3:7,3:7] view[:] = 1 print(zeros)
输出结果:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.], [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.], [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.], [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
但使用ravel()后,修改无法同步到原数组:
import numpy as np zeros = np.zeros(shape=(10,10)) view = zeros[3:7,3:7].ravel() view[:] =1 print(zeros)
输出结果:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
此时view.flags["OWNDATA"]返回True,说明ravel()创建了副本,修改不会同步到原数组。我曾尝试view[:] = view[:]+1,依然无效。
原因分析
ravel()返回副本的核心原因是:你通过zeros[3:7,3:7]得到的二维视图在内存中不是连续存储的(numpy默认采用行优先的C顺序存储,该切片的元素在内存中被其他行的元素隔开)。而一维数组要求内存连续,numpy无法为非连续的内存块创建一维视图,因此ravel()只能返回副本。
解决方案
方法1:使用flat属性实现一维视图修改
flat是numpy数组的内置属性,返回一个指向原数组的一维迭代器视图,修改它会直接同步到原数组:
import numpy as np zeros = np.zeros(shape=(10,10)) # 先获取二维视图 view_2d = zeros[3:7, 3:7] # 通过flat得到一维可迭代视图 view_1d = view_2d.flat # 修改一维视图 view_1d[:] = 1 print(zeros)
执行后,原数组的对应区域会被正确修改为1,效果和不使用ravel()的示例一致。
方法2:先修改副本再同步回原视图(适合非实时同步场景)
如果必须得到一个独立的一维数组对象,可先修改副本,再将值写回原二维视图:
import numpy as np zeros = np.zeros(shape=(10,10)) view_2d = zeros[3:7, 3:7] # 创建一维副本 view_1d = view_2d.ravel() # 修改副本内容 view_1d[:] = 1 # 将副本的值同步回原二维视图 view_2d[:] = view_1d.reshape(view_2d.shape) print(zeros)
特殊情况:连续切片的视图可直接用ravel()
如果你的二维视图是连续内存的(比如取整行、整列的切片),ravel()会直接返回视图,修改会同步原数组:
import numpy as np zeros = np.zeros(shape=(10,10)) # 取整行切片,内存连续 view_2d = zeros[3:7, :] view_1d = view_2d.ravel() view_1d[:] = 1 print(zeros) # 原数组的3-6行全部被设为1
此时view_1d.flags["OWNDATA"]返回False,说明是原数组的视图。
内容的提问来源于stack exchange,提问作者Naru1243
相关产品推荐
相关产品推荐

