numpy 的 std 默认 ddof=0,pandas 默认 ddof=1,同一列数会给出两个结果。 percentile 传 0 到 100 的百分数;pandas 的 quantile 传 0 到 1 的比例。 axis=0 压掉行、axis=1 压掉列,记「压掉哪一维」比记方向靠谱。
这一篇收尾:先把常用统计函数列成表,再拿两个案例把 axis 用熟。 案例用的是同一批 10 个学生的三科成绩,和第 4 章那批数是同一组。
一、一张表看完常用统计函数
sum 合计 827,mean 均值 82.70,median 中位数 84.0。 std 标准差 8.53,var 方差 72.81,两者差一个开平方。 min 和 max 给 68 和 95;argmin 和 argmax 给位置 5 和 6。
arg 系列给的是下标,不是值。要和名字对上,还得拿位置回去查。 percentile 给 87.55,也就是 65 分位那个数。
二、ddof 那点差别
同一列数,numpy 的 std 给 8.53,pandas 的 std 给 8.99。 原因只在分母:numpy 默认除以 n,pandas 默认除以 n 减 1。 这叫自由度(ddof)。numpy 默认 ddof=0,pandas 默认 ddof=1。
n 是 10 的时候差得挺明显;n 到几千,两个数几乎一样。
要两个工具对上,就显式写:A.std(ddof=1)。别靠猜默认值。
三、axis 方向再确认一次
2 行 3 列的矩阵,sum(axis=0) 给 [5, 7, 9],三个数。 sum(axis=1) 给 [6, 15],两个数。 axis=0 压掉行,所以结果有列数个;axis=1 压掉列,结果有行数个。
结果长度不一样,这是最快的判断办法:数一下出来几个,就知道压掉了哪一维。
四、案例一:一次算完三科
10 个学生 3 科成绩,组成一个 10 行 3 列的数组。
mean(axis=0) 一次给三个均值:82.7、83.0、81.9。
sum(axis=1) 一次给十个总分,最高那个 277 分。
不用循环三次。这是数组相对列表最实在的好处。 argmax 给的位置是 1,从 0 数起,也就是第 2 个学生。位置和序号差 1,这里最容易看错。
五、案例二:按列标准化
标准化的公式是:(每个数减所在列的均值) 除以所在列的标准差。
写成 (S - S.mean(axis=0)) / S.std(axis=0),一行就完。
这就是广播的用处。10 行 3 列减一个长度 3 的数组,每列各减各的。
算完每列均值是 -0.0、0.0、-0.0,标准差都是 1.0,验算通过。 均值里出现 -0.0 不是 bug,是浮点表示法里负零的写法,数值上就是 0。 标准化是机器学习预处理的第一步:不同量纲的列,标准化之后才能放在一起比。
六、第 6 章到此结束
NumPy 是 Pandas、Matplotlib、scikit-learn 共同的底座,这一章挖的是地基。 回头看:第 3 到 5 章用的每一列数据,底下都是一个 ndarray。 第 7 章换个方向,从「怎么算」转到「怎么判断」:抽样、检验、相关性。
判断自己掌握了没有:能不能不查文档写出「按列标准化」和「按行求和」这两句。
七、动手 Demo:统计函数与两个综合案例
先把常用统计函数一次打完,顺便把 ddof 的差别摆出来。 两个案例:一次算完三科均值与每人总分,再整表标准化。
import numpy as np
A = np.array([88, 92, 75, 85, 90, 68, 95, 72, 83, 79])
print("合计 %d,均值 %.2f" % (A.sum(), A.mean()))
print("标准差 %.2f(ddof=0),方差 %.2f" % (A.std(), A.var()))
print("最小 %d,最大 %d" % (A.min(), A.max()))
print("最小位置 %d,最大位置 %d" % (A.argmin(), A.argmax()))
print("中位数 %.1f,65 分位 %.2f" % (np.median(A), np.percentile(A, 65)))
print("ddof=1 时标准差 %.3f" % A.std(ddof=1))
M = np.arange(1, 7).reshape(2, 3)
print("按列求和 %s" % M.sum(axis=0).tolist())
print("按行求和 %s" % M.sum(axis=1).tolist())
S = np.array([[88, 92, 85], [92, 95, 90], [75, 68, 80], [85, 88, 79],
[90, 73, 95], [68, 82, 71], [95, 91, 88], [72, 65, 60],
[83, 86, 84], [79, 90, 87]])
print("每科均值 %s" % np.round(S.mean(axis=0), 2).tolist())
print("每人总分 %s" % S.sum(axis=1).tolist())
print("最高分位置 %d,%d 分" % (S.sum(axis=1).argmax(), S.sum(axis=1).max()))
Z = (S - S.mean(axis=0)) / S.std(axis=0)
print("标准化后每列均值 %s" % np.round(Z.mean(axis=0), 10).tolist())
print("标准化后每列标准差 %s" % np.round(Z.std(axis=0), 4).tolist())
实跑输出:
--------------------------------------------------------------------
统计函数与两个综合案例
--------------------------------------------------------------------
① 常用的统计函数
--------------------------------------------------------------------
| 函数 | 给什么 | 这次的值 |
|---|---|---:|
| sum | 合计 | 827 |
| mean | 均值 | 82.70 |
| std | 标准差(ddof=0) | 8.53 |
| var | 方差 | 72.81 |
| min / max | 最小最大 | 68 / 95 |
| argmin / argmax | 最小最大的位置 | 5 / 6 |
| median | 中位数 | 84.0 |
| percentile | 分位数 | 87.55 |
numpy 的 std 默认 ddof=0,pandas 默认 ddof=1。同一列数两个结果,差在分母。
! percentile 第二个参数是 0 到 100 的百分数;pandas 的 quantile 是 0 到 1 的比例。
② axis 方向再确认一次
--------------------------------------------------------------------
原矩阵:
[1, 2, 3]
[4, 5, 6]
sum(axis=0):[5, 7, 9]——三列各自的和。
sum(axis=1):[6, 15]——两行各自的和。
axis=0 压掉行,axis=1 压掉列。记「压掉哪一维」比记「方向」靠谱。
! 两个方向的结果长度不同:axis=0 给 3 个(列数),axis=1 给 2 个(行数)。
③ 案例一:一次算完三科
--------------------------------------------------------------------
10 个学生 3 科,组成一个 10 行 3 列的数组。
每科均值:[82.7, 83.0, 81.9]
每人总分:[265, 277, 223, 252, 258, 221, 274, 197, 253, 256]
总分最高的第 1 个人,277 分。
一次 mean(axis=0) 就出三科均值,不用循环三次。
! argmax 给位置不给值。要名字还得拿位置回原表查——和第 4 章的 idxmax 一个道理。
④ 案例二:标准化
--------------------------------------------------------------------
标准化 =(每个数减均值)除以标准差。
标准化之后每列的均值:[-0.0, 0.0, -0.0]
标准化之后每列的标准差:[1.0, 1.0, 1.0]
这就是广播的用处:一个 10 行 3 列减一个长度 3 的数组,每列各减各的均值。
! 标准化之后不同量纲的列才能放在一起比。这是机器学习预处理的第一步。
⑤ 第 6 章到此结束
--------------------------------------------------------------------
NumPy 是 Pandas、Matplotlib、scikit-learn 共同的底座,这里是它的地基。
第 7 章换个方向:从「算数」转到「统计推断」——抽样、检验、相关性。
! 判断自己掌握了没有:能不能不查文档写出「按列标准化」和「按行求和」这两句。
最后两行是验算:标准化之后均值该是 0、标准差该是 1。打出来对上了,说明公式没写错。
标准化用的是 S.std(axis=0),不是 pandas 的 std,所以这里是 ddof=0。换成 pandas 数会略差一点。
np.round(..., 10) 是为了把 -0.0 附近那些浮点尾巴抹平,不然会打出一串 1e-17。
30 秒实验
- 把 percentile 的 65 改成 0.65,看会不会报错或者给出别的数。
- 把标准化的分母换成 S.std(axis=0, ddof=1),看每列标准差还算不算 1。
- 想一想:如果某一列的标准差是 0(所有值都一样),标准化会出什么问题?
常见误区
- 以为 numpy 和 pandas 的 std 一样:一个 ddof=0 一个 ddof=1,小样本差得明显。
- percentile 传 0.65:它要 0 到 100,传 0.65 等于要 0.65 分位,结果不对。
- argmax 的返回值当序号用:它从 0 数起,和「第几个」差 1。
- 标准化时忘了按列:写成 axis=1 就变成每行各减各的,整张表都歪了。
自测三问(点开对答案)
1. numpy 和 pandas 的 std 差在哪? —— 分母不同。numpy 默认 ddof=0 除以 n,pandas 默认 ddof=1 除以 n 减 1。 2. 怎么判断 axis=0 和 axis=1 哪个是压掉行? —— 看结果长度:出来几个数就说明留下了几维。2 行 3 列,给 3 个数的是压掉行。 3. 标准化之后,每列的均值和标准差应该是多少? —— 均值 0、标准差 1。算完要验算这两条,对上了才说明公式没写错。第 6 章到此收尾。第 7 章从「算数」转到「判断」:一组数字能不能代表整体。
- ☐ (本篇完,下一篇 7.1 对比分析:TOP10 怎么排)