Python 基础命令与 NumPy 广播:参考答案#
说明#
以下答案与正文题目逐题对应。以下代码均假定已经执行 import numpy as np;除输出结果外,断言也用于检查维度和数值是否符合预期。
将列表转换为指定数据类型的数组,再用
ndarray.reshape()改变维度。import numpy as np values = [3, 1, 4, 1, 5, 9] a = np.array(values, dtype=np.int64) A = a.reshape(2, 3) print(A) print(A.ndim, A.shape, A.dtype) assert A.ndim == 2 assert A.shape == (2, 3) assert A.dtype == np.dtype("int64")
整数数组与浮点数组相加时,结果转换为能够表示两类数据的浮点类型;改为
float32后,每个元素占用的字节数减少。a = np.array([1, 2, 3], dtype=np.int64) b = np.array([0.5, 1.5, 2.5], dtype=np.float64) c = a + b print(c, c.dtype) before = a.nbytes + b.nbytes a32 = a.astype(np.float32) b32 = b.astype(np.float32) after = a32.nbytes + b32.nbytes print(before, after) assert c.dtype == np.dtype("float64") assert after < before
三个轴的长度依次为 2、3、4;沿某个轴求和后,该轴从结果维度中消失。
X = np.arange(24).reshape(2, 3, 4) print(X.ndim, X.shape, X.size) s0 = X.sum(axis=0) s1 = X.sum(axis=1) s2 = X.sum(axis=2) print(s0.shape, s1.shape, s2.shape) assert X.ndim == 3 and X.size == 24 assert s0.shape == (3, 4) assert s1.shape == (2, 4) assert s2.shape == (2, 3)
Python和NumPy的索引都从 0 开始,因此第 3 行对应索引 2,第 2 列对应索引 1。A = np.arange(1, 21).reshape(4, 5) third_row = A[2, :] second_col = A[:, 1] bottom_right = A[3, 4] bottom_right_negative = A[-1, -1] print(third_row) print(second_col) print(bottom_right) assert bottom_right == bottom_right_negative == 20
切片的结束位置不包含在结果中;步长
-1可以反转行的顺序。A = np.arange(1, 21).reshape(4, 5) middle = A[1:4, 1:4] even_numbered_cols = A[:, 1::2] reversed_rows = A[::-1, :] print(middle) print(even_numbered_cols) print(reversed_rows) assert middle.shape == (3, 3) assert even_numbered_cols.shape == (4, 2)
基本切片
B与A共享内存,而copy得到的C具有独立数据。A = np.arange(12).reshape(3, 4) B = A[:, 1:3] C = B.copy() B[0, 0] = -1 C[0, 1] = -2 print(A) print(B) print(C) assert A[0, 1] == -1 assert A[0, 2] != -2 assert np.shares_memory(A, B) assert not np.shares_memory(A, C)
比较运算产生布尔数组,可用于选择或修改满足条件的元素。
A = np.arange(1, 21).reshape(4, 5) multiples_of_three = A[A % 3 == 0] B = A.copy() B[B > 15] = 0 print(multiples_of_three) print(B) assert np.array_equal( multiples_of_three, np.array([3, 6, 9, 12, 15, 18]), ) assert A[-1, -1] == 20
X[rows, cols]逐对选择三个元素,而np.ix_构造两个索引的笛卡尔积。X = np.arange(16).reshape(4, 4) rows = np.array([0, 3, 1]) cols = np.array([2, 0, 3]) paired = X[rows, cols] submatrix = X[np.ix_(rows, cols)] print(paired) print(submatrix) assert np.array_equal(paired, np.array([2, 12, 7])) assert submatrix.shape == (3, 3)
transpose(1, 0, 2)交换前两个轴,并保留最后一个轴的位置。x = np.arange(24) X = x.reshape(2, 3, 4) Y = X.transpose(1, 0, 2) print(X.shape, Y.shape) assert Y.shape == (3, 2, 4) assert Y[1, 0, 2] == X[0, 1, 2]
axis=0增加行数,axis=1增加列数;沿列方向把后一结果等分即可恢复原数组。A = np.ones((2, 3), dtype=int) B = np.full((2, 3), 2) vertical = np.concatenate([A, B], axis=0) horizontal = np.concatenate([A, B], axis=1) left, right = np.hsplit(horizontal, 2) print(vertical) print(horizontal) assert vertical.shape == (4, 3) assert horizontal.shape == (2, 6) assert np.array_equal(left, A) assert np.array_equal(right, B)
这些运算都逐位置执行,因此结果维度保持为
(3,)。x = np.array([1.0, 2.0, 4.0]) y = np.array([2.0, 4.0, 8.0]) results = { "add": x + y, "multiply": x * y, "divide": x / y, "square": x ** 2, } for name, value in results.items(): print(name, value) assert value.shape == (3,)
长度为 4 的数组
b与X的最后一个轴对齐,并在第 0 轴上重复使用。X = np.arange(12).reshape(3, 4) b = np.array([10, 20, 30, 40]) Y = X + b explicit = X + np.tile(b, (3, 1)) print(Y) assert Y.shape == (3, 4) assert np.array_equal(Y, explicit)
维度为
(3, 1)的c在第 1 轴上广播;一维数组必须先增加长度为 1 的轴。X = np.arange(12).reshape(3, 4) c = np.array([[100], [200], [300]]) d = np.array([100, 200, 300]).reshape(-1, 1) result_c = X + c result_d = X + d print(result_c) assert result_c.shape == (3, 4) assert np.array_equal(result_c, result_d)
前两组维度兼容,结果分别为
(2, 3, 4)和(5, 4);最后一组的末轴 3 与 2 不兼容。pairs = [ (np.zeros((2, 1, 4)), np.zeros((1, 3, 1))), (np.zeros((5, 1)), np.zeros((4,))), (np.zeros((2, 3)), np.zeros((3, 2))), ] def add_and_report(a, b): try: result = a + b return result.shape except ValueError: return "不兼容" outcomes = [add_and_report(a, b) for a, b in pairs] print(outcomes) assert outcomes == [(2, 3, 4), (5, 4), "不兼容"]
沿第 1 轴聚合得到每一行的结果,沿第 0 轴聚合得到每一列的结果。
X = np.arange(1, 13).reshape(3, 4) row_sums = X.sum(axis=1) col_means = X.mean(axis=0) global_max = X.max() print(row_sums, col_means, global_max) assert np.array_equal(row_sums, np.array([10, 26, 42])) assert np.allclose(col_means, np.array([5., 6., 7., 8.])) assert global_max == 12
keepdims=True使均值和标准差保持维度(1, 4),从而可以直接与X广播。X = np.array([ [1., 2., 5., 7.], [3., 4., 9., 11.], [5., 8., 13., 15.], ]) mean = X.mean(axis=0, keepdims=True) std = X.std(axis=0, keepdims=True) Z = (X - mean) / std print(mean) print(std) print(Z) assert mean.shape == std.shape == (1, 4) assert Z.shape == X.shape assert np.allclose(Z.mean(axis=0), 0.0) assert np.allclose(Z.std(axis=0), 1.0)
三种矩阵乘法写法给出相同的
(2, 2)结果;逐元素乘法要求两个数组维度相同或可广播,不执行内维求和。A = np.array([[1., 2., 3.], [4., 5., 6.]]) B = np.array([[1., 2.], [3., 4.], [5., 6.]]) C1 = A @ B C2 = np.matmul(A, B) C3 = np.einsum("ik,kj->ij", A, B) print(C1) assert C1.shape == (2, 2) assert np.allclose(C1, C2) assert np.allclose(C1, C3) try: A * B except ValueError: print("A 与 B 的维度不支持逐元素乘法")
相同种子和相同调用顺序会产生相同数组;更换种子后结果通常不同。
rng1 = np.random.default_rng(2026) rng2 = np.random.default_rng(2026) rng3 = np.random.default_rng(2027) P = rng1.normal(size=(3, 4)) Q = rng2.normal(size=(3, 4)) R = rng3.normal(size=(3, 4)) print(P) assert np.array_equal(P, Q) assert not np.array_equal(P, R)
增加轴后,差值数组的维度为
(3, 2, 2);在最后一个轴上求平方和得到距离矩阵。X = np.array([[0., 0.], [1., 0.], [0., 2.]]) Y = np.array([[0., 1.], [2., 0.]]) differences = X[:, None, :] - Y[None, :, :] squared_distances = np.sum(differences ** 2, axis=2) nearest = np.argmin(squared_distances, axis=1) print(squared_distances) print(nearest) assert differences.shape == (3, 2, 2) assert np.array_equal( squared_distances, np.array([[1., 4.], [2., 1.], [1., 8.]]), ) assert np.array_equal(nearest, np.array([0, 1, 0]))
列均值和列标准差保留为
(1, 3),标准化结果与原特征矩阵同形;矩阵乘法得到每个样本的一个分数。X = np.array([ [1., 2., 3.], [2., 4., 5.], [4., 5., 7.], [5., 8., 9.], ]) w = np.array([0.5, -1., 2.]) b = 0.25 mean = X.mean(axis=0, keepdims=True) std = X.std(axis=0, keepdims=True) Z = (X - mean) / std scores = Z @ w + b predictions = scores >= 0 print(mean.shape, std.shape, Z.shape) print(scores) print(predictions) assert mean.shape == std.shape == (1, 3) assert Z.shape == (4, 3) assert scores.shape == predictions.shape == (4,) assert np.allclose(Z.mean(axis=0), 0.0)