返回首页

Roadmap
更新于 2026年10月25日 全文 1449 字未阅读

第 1.2 节 编程练习#

预计学习时长: 40 分钟

练习方式: 新建一个 Jupyter Notebook,把下面的准备代码抄进去,然后每题独立完成。先写,写不出来再展开答案。

准备:先建好数据#

import numpy as np

后面第 3 题会用到这张表,先建好:

# 5 个城市 × 4 个季度的销售额(万元)
sales = np.array([
    [120.0, 135.0, 150.0, 165.0],
    [ 80.0,  92.0,  88.0, 105.0],
    [200.0, 210.0, 195.0, 230.0],
    [ 60.0,  72.0,  85.0,  78.0],
    [150.0, 145.0, 160.0, 175.0],
])

练习 1:将 Python 列表转换为 NumPy 数组#

题目: 完成下面 5 个任务:

  1. 把列表 [10, 20, 30, 40] 转成 NumPy 数组,存到 a;
  2. 打印 a 的 shape、ndim、dtype;
  3. 把嵌套列表 [[1, 2], [3, 4]] 转成数组,存到 b,打印它的 shape;
  4. 把 [1, 2, 3.5] 转成数组,打印 dtype;
  5. 打印 a * 2 和 [10, 20, 30, 40] * 2 的结果,对比一下。

思考题: 第 4 题里,列表里只有一个小数,为什么整个数组都变成浮点数了?

点击查看参考答案
# 1. 转成数组
a = np.array([10, 20, 30, 40])
print(a)
[10 20 30 40]
# 2. 三个必查属性
print(a.shape)    # (4,)
print(a.ndim)     # 1
print(a.dtype)    # int64
# 3. 嵌套列表 → 二维数组
b = np.array([[1, 2], [3, 4]])
print(b.shape)    # (2, 2)
print(b)
(2, 2)
[[1 2]
 [3 4]]
# 4. 混进一个小数,整列都变浮点
c = np.array([1, 2, 3.5])
print(c.dtype)    # float64
# 5. 列表的 * 是重复,数组的 * 是逐元素相乘
print(a * 2)
[20 40 60 80]
print([10, 20, 30, 40] * 2)
[10, 20, 30, 40, 10, 20, 30, 40]

要点: 第 4 题是因为 NumPy 数组所有元素必须是同一种类型。 只要有一个 3.5(浮点数),整数就得「升级」成浮点数,整列都是 float64。 这跟 Pandas 不同——Pandas 的 DataFrame 可以每一列一个类型,NumPy 是一整个数组一个类型。


练习 2:创建指定形状的全零数组和全一数组#

题目: 完成下面 5 个任务:

  1. 创建一个 3 行 4 列的全零数组,存到 z;
  2. 创建一个 2 行 3 列的全一数组,存到 o;
  3. 创建一个 2 行 2 列、每个元素都是 9 的数组;
  4. 把第 1 题的 z 改成整数类型(提示:dtype);
  5. 打印 z.shape、z.dtype、o.shape。

思考题: 如果把 np.zeros((3, 4)) 写成 np.zeros(3, 4),会得到什么?

点击查看参考答案
# 1. 3 行 4 列全零
z = np.zeros((3, 4))
print(z)
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
# 2. 2 行 3 列全一
o = np.ones((2, 3))
print(o)
[[1. 1. 1.]
 [1. 1. 1.]]
# 3. 全填指定值
print(np.full((2, 2), 9))
[[9 9]
 [9 9]]
# 4. 指定整数类型
zi = np.zeros((3, 4), dtype=int)
print(zi.dtype)     # int64
# 5. 属性
print(z.shape)      # (3, 4)
print(z.dtype)      # float64
print(o.shape)      # (2, 3)

要点: 第 5 题提醒你 np.zeros 的形状必须用元组。 写成 np.zeros(3, 4) 的话,NumPy 会把 3 当形状、4 当 dtype,报:

TypeError: Cannot interpret '4' as a data type

看到这种「说不清 4 是啥」的错,八成就是元组括号忘了写。


练习 3:使用 arange() 和 linspace() 生成数值序列#

题目: 完成下面 5 个任务:

  1. 用 arange() 生成 [0, 1, 2, 3, 4];
  2. 用 arange() 生成从 10 开始、每次加 5、一直到 30 之前的序列;
  3. 用 linspace() 生成从 0 到 100 之间包含两端的 5 个数;
  4. 用 linspace() 生成从 0 到 1 之间、6 个等分点(不含终点);
  5. 打印 np.arange(0, 10, 2) 的长度,和 np.linspace(0, 10, 5) 的长度,对比一下。

思考题: 第 2 题如果想包含 30,该怎么写?

点击查看参考答案
# 1. 数 5 个
print(np.arange(5))
[0 1 2 3 4]
# 2. 从 10 开始,步长 5,不含 30
print(np.arange(10, 30, 5))
[10 15 20 25]
# 3. 0 到 100 分 5 份,含两端
print(np.linspace(0, 100, 5))
[  0.  25.  50.  75. 100.]
# 4. 0 到 1 分 6 份,不含终点
print(np.linspace(0, 1, 6, endpoint=False))
[0.         0.16666667 0.33333333 0.5        0.66666667 0.83333333]
# 5. 长度对比
print(len(np.arange(0, 10, 2)))     # 5
print(len(np.linspace(0, 10, 5)))   # 5

要点: 第 2 题想包含 30,要把终点写成 31(或者用 np.arange(10, 31, 5)):

print(np.arange(10, 31, 5))     # [10 15 20 25 30]

因为 arange 的终点永远不包含。这个坑几乎每个人都会踩一次。


练习 4:生成指定形状、范围和分布的随机数组#

题目: 完成下面 5 个任务。先执行 np.random.seed(42),这样后面的随机数才是固定的,你才能和自己的输出对得上:

  1. 生成 5 个 [0, 10) 之间的随机整数;
  2. 生成一个 3 行 2 列、[1, 100) 的随机整数数组;
  3. 生成 4 个 [0, 1) 的随机浮点数;
  4. 生成 3 个 [10, 20) 的随机浮点数(提示:uniform);
  5. 设种子 np.random.seed(42),生成 3 个随机浮点数,连着写两遍对比结果。

思考题: 第 5 题两次结果一样吗?如果去掉 seed 再跑一次呢?

点击查看参考答案
# 先锁住随机数
np.random.seed(42)

# 1. 5 个 [0, 10) 的随机整数
print(np.random.randint(0, 10, size=5))
[6 3 7 4 6]
# 2. 3 行 2 列的随机整数
print(np.random.randint(1, 100, size=(3, 2)))
[[83 87]
 [75 75]
 [88 24]]
# 3. [0, 1) 的随机浮点数
print(np.random.rand(4))
[0.65088847 0.05641158 0.72199877 0.93855271]
# 4. [10, 20) 的随机浮点数
print(np.random.uniform(10, 20, size=3))
[10.00778766 19.92211559 16.1748151 ]
# 5. 设种子 → 结果固定
np.random.seed(42)
print(np.random.rand(3))
[0.37454012 0.95071431 0.73199394]
np.random.seed(42)
print(np.random.rand(3))
[0.37454012 0.95071431 0.73199394]

要点: 第 5 题两次完全一样,因为种子把随机序列「锁定」了。 如果你把中间那行 np.random.seed(42) 删掉再跑,两次结果就会不同。

设种子的意义:让代码可复现。调试时你会庆幸自己有设种子。


练习 5:使用 reshape() 将一维数组转换为多维数组#

题目: 完成下面 6 个任务:

  1. 生成 0 到 11 共 12 个数字的一维数组;
  2. 把它 reshape 成 3 行 4 列,打印结果;
  3. 把它 reshape 成 4 行 3 列,打印结果;
  4. 把它 reshape 成 2 行 2 行 3 列(三维),打印 shape;
  5. 用 -1 把它 reshape 成 3 行(列数让 NumPy 自己算),打印 shape;
  6. 试着把它 reshape(5, 3),看看报什么错。

思考题: 第 6 步为什么报错?12 个元素能摆成 5 行 3 列吗?

点击查看参考答案
# 1. 造一维数组
nums = np.arange(12)
print(nums)
[ 0  1  2  3  4  5  6  7  8  9 10 11]
# 2. 3 行 4 列
print(nums.reshape(3, 4))
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
# 3. 4 行 3 列
print(nums.reshape(4, 3))
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
# 4. 三维:2 × 2 × 3
print(nums.reshape(2, 2, 3).shape)     # (2, 2, 3)
# 5. 用 -1 让 NumPy 算
print(nums.reshape(3, -1).shape)       # (3, 4)
# 6. 元素总数对不上
nums.reshape(5, 3)
ValueError: cannot reshape array of size 12 into shape (5,3)

要点: 第 6 步是因为 5 × 3 = 15 ≠ 12。 reshape 只改变「怎么摆」,不改变「有几个」,所以总数必须严格守恒。 12 个元素能摆成的形状:12、6×2、4×3、3×4、2×6、2×2×3……


加分练习:给一张空表填上季度数据#

题目: 做一个「季度进度表」,要求:

  1. 创建一个 5 行 4 列的全零数组 table(形状对上 5 个城市 × 4 个季度);
  2. 用 arange 生成 1, 2, 3, 4 四个季度序号;
  3. 把「北京」这一行填成 [100, 200, 300, 400];
  4. 把「成都」这一行填成 [50, 60, 70, 80];
  5. 打印整个表和 table.sum(axis=1)(每行合计);
  6. 打印 table.sum()(全表合计)。

思考题: 第 5 步的 axis=1 和 axis=0 分别算的是什么?

点击查看参考答案
# 1. 空表
table = np.zeros((5, 4))
print(table.shape)     # (5, 4)
# 2. 季度序号
quarters = np.arange(1, 5)
print(quarters)        # [1 2 3 4]
# 3 & 4. 填两行
table[0] = np.array([100, 200, 300, 400])   # 北京
table[4] = np.array([50, 60, 70, 80])        # 成都
print(table)
[[100. 200. 300. 400.]
 [  0.   0.   0.   0.]
 [  0.   0.   0.   0.]
 [  0.   0.   0.   0.]
 [ 50.  60.  70.  80.]]
# 5. 每行合计
print(table.sum(axis=1))
[1000.    0.    0.    0.  260.]
# 6. 全表合计
print(table.sum())     # 1260.0

要点: axis=1 是「沿着列的方向看每一行」,所以结果是每一行的合计(5 个数); axis=0 是「沿着行的方向看每一列」,结果是每一列的合计(4 个数)。 这个记法第 4 章会详细讲,这里先记住结论就行。

另外注意第 3、4 步:table[0] = [...] 是整行赋值,等号右边长度要和列数一致(4 个),否则会报 ValueError: could not broadcast input array from shape (5,) into shape (4,)。


练习小结#

题目用到的核心语法
练习 1np.array()、shape / ndim / dtype
练习 2np.zeros()、np.ones()、np.full()、dtype=int
练习 3np.arange()、np.linspace()、endpoint=False
练习 4np.random.randint/rand/uniform、np.random.seed()
练习 5reshape()、-1、元素总数守恒

做完这 5 题,下一步 现在你能造数组了。接下来要解决「怎么从数组里取数据」——a[0]、a[1:3]、a[[0, 2]]、a[a > 10] 这些写法各有各的脾气,那正是第 2 章「索引与切片」的内容。

阅读 --