如何同时为Numpy数组指定行列赋值?含两类场景问题
场景1:修改指定子矩阵值但原数组未更新的问题
问题描述
期望修改数组的指定子区域后得到如下输出:
array([[ 0, 1, 2, 3], [ 4, 999, 999, 7], [ 8, 999, 999, 11], [12, 13, 14, 15]])
但执行以下代码后,原数组并未发生变化:
import numpy as np A = np.array(range(16)).reshape((4,4)) A[[1,3],:][:, [1,3]] = [[999,999],[999,999]] print(A)
实际输出仍是初始数组:
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])
原因分析
A[[1,3],:]这种花式索引返回的是原数组的副本而非视图,后续对副本的切片赋值操作不会影响原数组。链式索引会产生多个副本,修改的只是最后一个副本,原数组根本没被改动。此外,原代码中的行索引[1,3]和预期输出不匹配,预期输出中999位于第1、2行(索引1和2)的第1、2列,需注意索引对应关系。
解决方案
需要直接对原数组的目标区域进行索引赋值,避免链式索引。提供以下几种方式:
方法1:使用连续切片(高效且简洁)
因为目标区域是连续的行(索引1到2)和列(索引1到2),直接用切片返回视图,赋值直接作用于原数组:
import numpy as np A = np.array(range(16)).reshape((4,4)) A[1:3, 1:3] = 999 print(A)
方法2:使用np.ix_生成可广播索引
如果目标行/列不连续,用np.ix_生成对应所有组合的索引,确保返回视图:
import numpy as np A = np.array(range(16)).reshape((4,4)) rows = [1, 2] cols = [1, 2] A[np.ix_(rows, cols)] = 999 print(A)
场景2:花式索引赋值时形状不匹配的问题
问题描述
执行A[[1,3], [1,3]] = inner时抛出异常:shape mismatch: value array of shape (2,2) could not be broadcast to indexing result of shape (2,),期望输出为:
array([[ 0, 1, 2, 3], [ 4, 999, 6, 999], [ 8, 9, 10, 11], [12, 999, 14, 999]])
原因分析
A[[1,3], [1,3]]这种索引方式是花式索引的配对索引,会选取(1,1)和(3,3)两个元素,返回形状为(2,),而赋值的inner是(2,2)的数组,形状不匹配导致无法广播。
解决方案
要实现给(1,1)、(1,3)、(3,1)、(3,3)四个位置赋值,需让索引生成的形状和赋值数组匹配,提供以下方式:
方法1:使用np.ix_生成二维索引
np.ix_会生成广播后的索引数组,对应所有行和列的组合,返回区域形状为(2,2),和inner形状匹配:
import numpy as np A = np.array(range(16)).reshape((4,4)) inner = np.array([[999, 999], [999, 999]]) A[np.ix_([1,3], [1,3])] = inner print(A)
方法2:展平赋值数组并指定所有目标索引
手动列出所有需要赋值的位置索引,将赋值数组展平为一维后匹配赋值:
import numpy as np A = np.array(range(16)).reshape((4,4)) inner = np.array([[999, 999], [999, 999]]).flatten() A[[1,1,3,3], [1,3,1,3]] = inner print(A)
方法3:直接赋值标量(简化版)
因为所有目标位置都赋值999,直接用标量即可,NumPy会自动广播到目标区域:
import numpy as np A = np.array(range(16)).reshape((4,4)) A[np.ix_([1,3], [1,3])] = 999 print(A)
内容的提问来源于stack exchange,提问作者bkehufi3evsd

