第 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 个任务:
- 把列表
[10, 20, 30, 40]转成 NumPy 数组,存到a; - 打印
a的shape、ndim、dtype; - 把嵌套列表
[[1, 2], [3, 4]]转成数组,存到b,打印它的shape; - 把
[1, 2, 3.5]转成数组,打印dtype; - 打印
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 个任务:
- 创建一个 3 行 4 列的全零数组,存到
z; - 创建一个 2 行 3 列的全一数组,存到
o; - 创建一个 2 行 2 列、每个元素都是
9的数组; - 把第 1 题的
z改成整数类型(提示:dtype); - 打印
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 个任务:
- 用
arange()生成[0, 1, 2, 3, 4]; - 用
arange()生成从10开始、每次加5、一直到30之前的序列; - 用
linspace()生成从0到100之间包含两端的5个数; - 用
linspace()生成从0到1之间、6个等分点(不含终点); - 打印
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),这样后面的随机数才是固定的,你才能和自己的输出对得上:
- 生成 5 个
[0, 10)之间的随机整数; - 生成一个 3 行 2 列、
[1, 100)的随机整数数组; - 生成 4 个
[0, 1)的随机浮点数; - 生成 3 个
[10, 20)的随机浮点数(提示:uniform); - 设种子
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 个任务:
- 生成
0到11共 12 个数字的一维数组; - 把它
reshape成 3 行 4 列,打印结果; - 把它
reshape成 4 行 3 列,打印结果; - 把它
reshape成 2 行 2 行 3 列(三维),打印shape; - 用
-1把它reshape成 3 行(列数让 NumPy 自己算),打印shape; - 试着把它
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……
加分练习:给一张空表填上季度数据#
题目: 做一个「季度进度表」,要求:
- 创建一个 5 行 4 列的全零数组
table(形状对上 5 个城市 × 4 个季度); - 用
arange生成1, 2, 3, 4四个季度序号; - 把「北京」这一行填成
[100, 200, 300, 400]; - 把「成都」这一行填成
[50, 60, 70, 80]; - 打印整个表和
table.sum(axis=1)(每行合计); - 打印
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,)。
练习小结#
| 题目 | 用到的核心语法 |
|---|---|
| 练习 1 | np.array()、shape / ndim / dtype |
| 练习 2 | np.zeros()、np.ones()、np.full()、dtype=int |
| 练习 3 | np.arange()、np.linspace()、endpoint=False |
| 练习 4 | np.random.randint/rand/uniform、np.random.seed() |
| 练习 5 | reshape()、-1、元素总数守恒 |
做完这 5 题,下一步 现在你能造数组了。接下来要解决「怎么从数组里取数据」——
a[0]、a[1:3]、a[[0, 2]]、a[a > 10]这些写法各有各的脾气,那正是第 2 章「索引与切片」的内容。