Python 基础命令与 NumPy 广播:参考答案

目录

\[ \begin{align}\begin{aligned}\newcommand{\ba}{\boldsymbol{a}} \newcommand{\bb}{\boldsymbol{b}} \newcommand{\be}{\boldsymbol{e}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bw}{\boldsymbol{w}} \newcommand{\bx}{\boldsymbol{x}} \newcommand{\by}{\boldsymbol{y}} \newcommand{\bz}{\boldsymbol{z}} \newcommand{\bd}{\boldsymbol{d}} \newcommand{\bv}{\boldsymbol{v}} \newcommand{\bs}{\boldsymbol{s}}\\\newcommand{\btheta}{\boldsymbol{\theta}} \newcommand{\bbeta}{\boldsymbol{\beta}} \newcommand{\bgamma}{\boldsymbol{\gamma}} \newcommand{\bsigma}{\boldsymbol{\sigma}} \newcommand{\md}{\mbox{d}} \newcommand{\bmu}{\boldsymbol{\mu}} \newcommand{\bone}{\boldsymbol{1}} \newcommand{\bzero}{\boldsymbol{0}} \newcommand{\bepsilon}{\boldsymbol{\epsilon}} \newcommand{\bphi}{\boldsymbol{\phi}} \newcommand{\bh}{\boldsymbol{h}} \newcommand{\bc}{\boldsymbol{c}} \newcommand{\br}{\boldsymbol{r}} \newcommand{\bQ}{\boldsymbol{Q}} \newcommand{\bK}{\boldsymbol{K}} \newcommand{\bV}{\boldsymbol{V}} \newcommand{\bSigma}{\boldsymbol{\Sigma}} \newcommand{\bg}{\boldsymbol{g}} \newcommand{\bxi}{\boldsymbol{\xi}} \newcommand{\bvarepsilon}{\boldsymbol{\varepsilon}} \newcommand{\bdelta}{\boldsymbol{\delta}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bJ}{\boldsymbol{J}} \newcommand{\bp}{\boldsymbol{p}} \newcommand{\bi}{\boldsymbol{i}} \newcommand{\bo}{\boldsymbol{o}} \newcommand{\bE}{\boldsymbol{E}} \newcommand{\bH}{\boldsymbol{H}} \newcommand{\bL}{\boldsymbol{L}} \newcommand{\bu}{\boldsymbol{u}} \newcommand{\bLambda}{\boldsymbol{\Lambda}} \newcommand{\trans}{^{\rm\scriptsize T}} \newcommand{\var}{\mathrm{var}}\\\newcommand{\bA}{\boldsymbol{A}} \newcommand{\bB}{\boldsymbol{B}} \newcommand{\bC}{\boldsymbol{C}} \newcommand{\bD}{\boldsymbol{D}} \newcommand{\bG}{\boldsymbol{G}} \newcommand{\bI}{\boldsymbol{I}} \newcommand{\bM}{\boldsymbol{M}} \newcommand{\bP}{\boldsymbol{P}} \newcommand{\bS}{\boldsymbol{S}} \newcommand{\bU}{\boldsymbol{U}} \newcommand{\bW}{\boldsymbol{W}} \newcommand{\bX}{\boldsymbol{X}} \newcommand{\bY}{\boldsymbol{Y}} \newcommand{\bZ}{\boldsymbol{Z}} \newcommand{\cotp}{\textcolor[RGB]{48,209,88}{TP}} \newcommand{\cotn}{\textcolor[RGB]{100,210,255}{TN}} \newcommand{\cofp}{\textcolor[RGB]{94,92,230}{FP}} \newcommand{\cofn}{\textcolor[RGB]{191,90,242}{FN}}\\\newcommand{\numcotp}{\textcolor[RGB]{48,209,88}{50}} \newcommand{\numcotn}{\textcolor[RGB]{100,210,255}{30}} \newcommand{\numcofp}{\textcolor[RGB]{94,92,230}{10}} \newcommand{\numcofn}{\textcolor[RGB]{191,90,242}{10}} \DeclareMathOperator*{\argmin}{arg\,min}\end{aligned}\end{align} \]

Python 基础命令与 NumPy 广播:参考答案#

返回正文练习 · 返回答案索引

说明#

以下答案与正文题目逐题对应。以下代码均假定已经执行 import numpy as np除输出结果外,断言也用于检查维度和数值是否符合预期。

  1. 将列表转换为指定数据类型的数组,再用 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")
    
  2. 整数数组与浮点数组相加时,结果转换为能够表示两类数据的浮点类型;改为 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
    
  3. 三个轴的长度依次为 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)
    
  4. PythonNumPy 的索引都从 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
    
  5. 切片的结束位置不包含在结果中;步长 -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)
    
  6. 基本切片 BA 共享内存,而 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)
    
  7. 比较运算产生布尔数组,可用于选择或修改满足条件的元素。

    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
    
  8. 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)
    
  9. 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]
    
  10. 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)
    
  11. 这些运算都逐位置执行,因此结果维度保持为 (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,)
    
  12. 长度为 4 的数组 bX 的最后一个轴对齐,并在第 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)
    
  13. 维度为 (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)
    
  14. 前两组维度兼容,结果分别为 (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), "不兼容"]
    
  15. 沿第 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
    
  16. 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)
    
  17. 三种矩阵乘法写法给出相同的 (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 的维度不支持逐元素乘法")
    
  18. 相同种子和相同调用顺序会产生相同数组;更换种子后结果通常不同。

    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)
    
  19. 增加轴后,差值数组的维度为 (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]))
    
  20. 列均值和列标准差保留为 (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)