📊 第6章 图解数组计算模块 NumPy
📄 一句话概括

arange 管步长,linspace 管个数,logspace 管等比。三个函数三种需求。 arange 的终点不包含,和 Python 的 range 一样。 小数序列别用 arange 数个数——浮点误差会让它多给一个或少给一个。

📝 本章和前面几章的关系

上一篇是一个个给值。这一篇按规则批量造,画函数图像、造测试数据全靠它。 三个函数名字很像,区别只在「你关心步长还是关心个数」。

一、arange:管步长

np.arange(5) 给 0 到 4,np.arange(1, 10, 2) 给 1、3、5、7、9。 np.arange(0, 1, 0.25) 给 0、0.25、0.5、0.75。 终点不包含,和 Python 的 range 一致。要含终点就多加一步。

三个参数是起点、终点、步长。只给一个参数时,它被当成终点,起点默认 0。

二、小数步长的坑

np.arange(0, 1, 0.1) 打出 10 个数,最后一个是 0.90000000000000002。 它理论上是 0.9,实际存的比 0.9 略大一点点——二进制存不下 0.1。 所以这个数组到底是 10 个还是 11 个,取决于浮点误差累积到哪。

小数别用 arange 数个数。要「刚好 N 个」就用 linspace。 整数序列用 arange 完全没问题,整数没有精度问题。

三、linspace:管个数

np.linspace(0, 1, 5) 给 0、0.25、0.5、0.75、1,五个数,两端都含。 步长是 (1-0)/(5-1) = 0.25——这个数是除出来的,不是累加出来的。 所以它没有累加误差。画函数图像一律用它。

endpoint=False 去掉终点,变成 0、0.2、0.4、0.6、0.8。

四、logspace:等比

np.logspace(0, 2, 3) 给 1、10、100——从 10 的 0 次方到 10 的 2 次方。 np.logspace(0, 3, 4) 给 1、10、100、1000,每次乘 10。 默认底数是 10,可以用 base 参数改成别的。

画对数坐标、造跨几个数量级的测试数据时用它。等比数列就是这个。

五、动手 Demo:从数值范围创建数组

三个函数各造几个,把小数步长的浮点误差现场打出来。 最后对比 arange 和 linspace 造同一段区间,看结果差多少。

import numpy as np

print("arange(5)        %s" % np.arange(5))
print("arange(1,10,2)   %s" % np.arange(1, 10, 2))
print("arange(0,1,0.25) %s" % np.arange(0, 1, 0.25))

A = np.arange(0, 1, 0.1)
print("arange 小数:%d 个,最后 %.17f" % (len(A), A[-1]))

B = np.linspace(0, 1, 10)
print("linspace    :%d 个,最后 %.17f" % (len(B), B[-1]))
print("两者最后一个是否相等:%s" % (A[-1] == B[-1]))

print("linspace(0,1,5)           %s" % np.linspace(0, 1, 5))
print("linspace(0,1,5,endpoint=False) %s" % np.linspace(0, 1, 5, endpoint=False))

print("logspace(0,2,3) %s" % np.logspace(0, 2, 3))
print("logspace(0,3,4) %s" % np.logspace(0, 3, 4))

实跑输出:

--------------------------------------------------------------------
从数值范围创建数组
--------------------------------------------------------------------

  ① arange:管步长
  --------------------------------------------------------------------
  arange(5):[0 1 2 3 4]
  arange(1, 10, 2):[1 3 5 7 9]
  arange(0, 1, 0.25):[0.   0.25 0.5  0.75]
  arange 的终点不包含,和 Python 的 range 一样。
  ! 小数步长会有精度问题:arange(0, 1, 0.1) 最后一个可能不是 0.9 而是 0.9000000000000001。

  ② 小数步长的坑
  --------------------------------------------------------------------
  arange(0, 1, 0.1) 一共 10 个,最后一个是 0.90000000000000002。
  它理论上是 0.9,实际存的比 0.9 略大一点点——上面那 17 位小数就是证据,因为二进制存不下 0.1。
  要「刚好 N 个」就用 linspace,别用 arange。
  ! 小数别用 arange 数个数。它可能多给一个也可能少给一个,取决于浮点误差。

  ③ linspace:管个数
  --------------------------------------------------------------------
  linspace(0, 1, 5):[0.   0.25 0.5  0.75 1.  ],一共 5 个,两端都含。
  linspace(0, 1, 5, endpoint=False):[0.  0.2 0.4 0.6 0.8],不含终点。
  步长是 (1-0)/(5-1) = 0.25,这个数是算出来的,不是累加出来的。
  ! linspace 的分段是「除出来的」,所以没有累加误差。画函数图像一律用它。

  ④ logspace:指数步长
  --------------------------------------------------------------------
  logspace(0, 2, 3):[  1.  10. 100.],从 10 的 0 次方到 10 的 2 次方。
  logspace(0, 3, 4):[   1.   10.  100. 1000.],每次乘 10。
  它默认底数是 10,可以用 base 参数改。
  ! 画对数坐标的图、造跨几个数量级的测试数据时用它。等比数列就是这个。

  ⑤ 三个函数怎么选
  --------------------------------------------------------------------

| 你要什么 | 用哪个 |
|---|---|
| 固定步长,终点不关心 | arange |
| 固定个数,两端要卡准 | linspace |
| 等比(每次乘同一个倍数) | logspace |

  一句话:arange 数步长,linspace 数个数。
  ! 整数序列用 arange 没问题,小数的序列一律 linspace。

A[-1] == B[-1] 给 False——这就是浮点误差的直接证据,两个「0.9」并不相等。 要判两个小数相等,别用 ==,用 abs(a - b) < 1e-9 这种写法。

30 秒实验

  1. 把 arange(0, 1, 0.1) 换成 arange(0, 1.1, 0.1),看元素个数变成几个。
  2. 用 linspace 造 100 个数,算相邻两个的差,看是不是全都是 0.01。
  3. 想一想:为什么 linspace 没有累加误差?

常见误区

  1. 小数序列用 arange 数个数:个数不确定,随浮点误差浮动。
  2. 以为 arange 含终点:和 range 一样不含,要含得自己加。
  3. 两个小数用 == 比:0.1+0.2 和 0.3 判不相等。
  4. linspace 第三个参数理解错:它是「个数」不是「步长」。
自测三问(点开对答案) 1. arange 和 linspace 最本质的区别? —— arange 你给步长、它算个数;linspace 你给个数、它算步长。 2. 为什么小数序列推荐用 linspace? —— 它是除法算出来的端点,没有累加误差;arange 靠累加,误差会积累。 3. logspace(0, 2, 3) 给什么? —— [1, 10, 100]。从 10 的 0 次方到 10 的 2 次方,等比三个数。

范围能造了,但真实数据往往带随机性。下一篇讲随机数,还有那条铁律:给种子。

相关笔记