📈 第9章 直方图
📄 一句话概括

直方图就是把数轴切成等宽的箱,数每个箱里落了几个人。它不假设任何分布,纯粹是数出来的。 60 条加薪记录画出来是「双峰带凹陷」:3~6 一个峰,9~11 一个小峰,中间 7~9 塌下去。 计零的两格(12~14.5 和 14.5~17)正好隔开谈判组和极端案例——数据上的缺口往往对应现实里的分界。

📝 本章和前面几章的关系

上一篇把「该看图」这件事说清了。这一篇就画,并把图上的每一格翻译成人话。 书里这段用 R 的 hist()。这里用标准库分箱 + matplotlib 出 PNG,分箱逻辑和 hist() 一样。

一、把数轴切成等宽的箱

取 2.0 到 27.0 这一段(覆盖全部数据),切成 10 个等宽的箱,每箱 2.5 个百分点。 然后逐个数据点判断它落进哪一箱,把计数加一。这就是全部算法,十行代码。 计数结果:2.0~4.5 有 20 人,4.5~7.0 有 20 人,7.0~9.5 有 6 人,9.5~12.0 有 9 人,12~17 两格为 0, 17 以上零散 5 人。

横轴是加薪幅度,纵轴是人数。柱子高,说明这个区间人多。

二、形状:两个峰,中间一个凹陷

人数最多的是头两箱,各 20 人——这是第一座山。 接下来 7.0~9.5 掉到 6 人,9.5~12.0 又回到 9 人——第二座小山。 这叫「双峰带凹陷」。它不是拟合出来的,是数出来的:每一格的人数摆在那里,谁也改不了。

双峰意味着一件事:这批人可能来自两类不同的过程。普调和谈判,本来就是两件事。

三、缺口说明什么

计零的箱有两个:12.0~14.5 和 14.5~17.0。 这两格空着,正好把谈判组(8.0 到 11.5)和最右边那几个极端案例(18 以上)隔开。 数据上的缺口,往往对应现实里的一个分界。这里一个人都没有,说明「14% 的加薪」在这家公司根本不存在。

反过来说,如果制度上应该有这一档却没人,那就是个值得追的问题——是没人够格,还是流程卡住了。

四、画成图存下来

用 matplotlib 把同一份计数画成柱状图,存成 PNG。 图里的字一律用英文:这台机器没装中文字体,写中文会变成一排方框。 这一步不算分析,是把已经数出来的东西变成能给人看的东西。先有数,再有图,顺序不能反。

五、从图里读出三件事

第一,形状是双峰的,不是钟形。这种数据不能用「平均 ± 标准偏差」描述。 第二,两个峰的位置分别在 3~6 和 9~11,中间 7~9 明显塌下去。 第三,最右边还有几个孤立的小柱,那是 5 条极端记录,它们的量级和主体完全不同。

这三条没有一条能从平均数里读出来。这就是直方图存在的理由。

六、动手 Demo:直方图到底说明什么

60 条记录切成 10 个箱,逐格数人数,再把图存成 PNG。 顺便把计零的箱找出来——缺口的位置往往比峰的位置更有信息。

import os
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

BASE = [round(2.5 + 0.1 * i, 1) for i in range(40)]
NEGO = [round(8.0 + 0.25 * i, 1) for i in range(15)]
DATA = BASE + NEGO + [18.0, 20.0, 22.0, 24.0, 26.0]

def hist(vals, lo, hi, bins):
    w = (hi - lo) / float(bins)
    edges = [lo + w * i for i in range(bins + 1)]
    counts = [0] * bins
    for v in vals:
        if lo <= v < hi:
            k = min(int((v - lo) / w), bins - 1)
            counts[k] += 1
    return edges, counts

LO, HI, BINS = 2.0, 27.0, 10
edges, counts = hist(DATA, LO, HI, BINS)
for i in range(BINS):
    print("%5.1f ~ %5.1f  %2d 人  %s"
          % (edges[i], edges[i + 1], counts[i], "#" * counts[i]))

gaps = [i for i in range(BINS) if counts[i] == 0]
print("计零的箱:%d 个" % len(gaps))

fig, ax = plt.subplots(figsize=(5.0, 3.2), dpi=110)
ax.hist(DATA, bins=BINS, range=(LO, HI), color="#7f9fd0", edgecolor="white")
ax.set_xlabel("raise percent", fontsize=8)
ax.set_ylabel("headcount", fontsize=8)
ax.set_title("60 raise records (bins=10)", fontsize=9)
fig.tight_layout()
fig.savefig("hist10.png")
plt.close(fig)
print("直方图存成 %d 字节" % os.path.getsize("hist10.png"))

实跑输出:

--------------------------------------------------------------------
直方图到底说明什么
--------------------------------------------------------------------

  ① 把数轴切成等宽的箱
  --------------------------------------------------------------------

| 区间(%) | 人数 | 条形 |
|---|---|---|
| 2.0 ~ 4.5 | 20 | #################### |
| 4.5 ~ 7.0 | 20 | #################### |
| 7.0 ~ 9.5 | 6 | ###### |
| 9.5 ~ 12.0 | 9 | ######### |
| 12.0 ~ 14.5 | 0 |  |
| 14.5 ~ 17.0 | 0 |  |
| 17.0 ~ 19.5 | 1 | # |
| 19.5 ~ 22.0 | 1 | # |
| 22.0 ~ 24.5 | 2 | ## |
| 24.5 ~ 27.0 | 1 | # |

  十个箱,横轴是加薪幅度,纵轴是落在这个区间的人数。

  ② 形状:两个峰,中间一个凹陷
  --------------------------------------------------------------------
  人数最多的是 2.0~4.5 和 4.5~7.0 两箱,各 20 人。
  然后 7.0~9.5 掉到 6 人,9.5~12.0 又回到 9 人——第二个小峰。
  这种形状叫「双峰带凹陷」。它不是拟合出来的,是数出来的:每一格的人数就摆在那里。
  ! 双峰意味着一件事:这批人可能来自两类不同的过程。普调和谈判,本来就是两件事。

  ③ 缺口说明什么
  --------------------------------------------------------------------
  计零的箱有 2 个,分别是 12.0~14.5 和 14.5~17.0。
  这两格空着,正好把谈判组(8.0~11.5)和最右边那几个极端案例(18 以上)隔开了。
  数据上的缺口,往往对应现实里的一个分界:这里一个人都没有,说明「14% 的加薪」在这家公司不存在。

  ④ 画成图存下来
  --------------------------------------------------------------------
  直方图存成 PNG:11525 字节。
  图里的字用英文:这台机器没装中文字体,中文会变成方框。

  ⑤ 从图里读出的三件事
  --------------------------------------------------------------------
  第一,形状是双峰的,不是钟形。用「平均值 ± 标准偏差」描述这种数据会误导。
  第二,两个峰的位置分别在 3~6 和 9~11,中间 7~9 明显凹下去。
  第三,最右边还有几个孤立的小柱,那 5 条极端记录。

打印出来的 ASCII 柱状图和 PNG 是同一份计数。先看文本,再看图——文本里每一格的人数写得清清楚楚。 计零的两格在 12~17。注意:这两个空档不是「公司里没人加薪 13%」,是「这 60 条样本里恰好没有」。

30 秒实验

  1. 把箱数固定为 10,但把范围改成 0 到 30,形状会怎么变?为什么峰看起来挪了?
  2. 把「12.0~14.5」这一格单独去看:是真的没有人,还是数据本来就没覆盖这一段?
  3. 想一想:如果直方图有一个计零的箱,它在业务上可能意味着什么?

常见误区

  1. 把直方图当概率分布:它画的是这批样本的计数,不是总体的概率。换一批样本形状会变。
  2. 忽略箱宽:箱宽是画图人定的,换个宽度可能从单峰变双峰。
  3. 用平均描述双峰数据:平均值往往落在中间那个凹陷里,谁都不代表。
  4. 看到缺口就当结论:缺口可能是样本太少造成的,也可能是真实的分界,要看数据来源。
自测三问(点开对答案) 1. 直方图和柱状图有什么区别? —— 柱状图的横轴是分类,直方图的横轴是连续区间。直方图的柱子之间没有间隔,因为区间是连续的。 2. 这组数据的形状是什么? —— 双峰带凹陷:3~6 一个主峰,9~11 一个次峰,7~9 塌下去。 3. 计零的箱意味着什么? —— 说明这个区间里一个样本都没有。可能是样本量不够,也可能现实中真的没有这一档。

形状看出来了,可箱宽是我定的。下一篇换个宽度再画一遍,看结论还站不站得住。

相关笔记