📊 第5章 可视化数据分析图表
📄 一句话概括

两次 plot 落在同一个 ax 上就是两条线,label 一给图例才有内容。 一条 plot 传二维数组时,按列画线:列数就是线条数。 figsize 管多大,dpi 管多细。尺寸翻倍字节只涨三成多,因为文字开销是固定的。

📝 本章和前面几章的关系

上一篇只画了一条线。这一篇讲多组数据怎么画,还有画布尺寸怎么定。 材料是六个月的销售额:128000、116000、143000、151000、138000、162000。

一、一个 ax 上画多条线

连续两次 ax.plot(...),两条线就落在同一张图上。 每次 plot 给一个 label,最后 ax.legend() 统一调出图例。 不写 label,图例就是空的。这是「图例没出来」最常见的原因。

线多了要分配颜色,可以显式给 color,也可以让 Matplotlib 自动轮换。 自动轮换在超过五条线时会开始重复颜色,这时候得自己定。

二、一条 plot 传二维数据

传一个 6 行 2 列的数组,它按列画两条线,和两次 plot 效果一样。 6 行 2 列画两条,2 行 6 列画六条——列数才是线条数。 数据本来就在 ndarray 里时这样写省事,但行列关系容易搞混。

新手最常犯的错是把表转置忘了,画出一条横跨全屏的怪线。

三、画布尺寸:figsize

figsize 存成多少字节 面积
(4, 3) 12707 12
(6, 4) 17293 24
(8, 6) 23720 48

面积翻倍,字节只涨三成多——因为文字和坐标轴这些固定开销不随面积线性涨。 figsize 的单位是英寸。默认 (6.4, 4.8),差不多是一张 A4 纸的三分之一。 屏幕上看,宽 8 到 10 英寸已经够大;打印的话按实际版面尺寸给。

四、清晰度:dpi

dpi 是每英寸多少个点。默认 100,印刷标准是 300。 同样一张 6 乘 4 的图,dpi=100 是 17293 字节,dpi=300 是 63367 字节。 尺寸和 dpi 是两件事。尺寸管「多大」,dpi 管「多细」。

两个都调,文件会暴涨。屏幕上看的图,dpi 保持 100 就好。

五、动手 Demo:plot 函数与画布

先画同一张图上的两条线,再用二维数组合成地画一次。 最后用三种 figsize 存图,比字节数。

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

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

M = [128000, 116000, 143000, 151000, 138000, 162000]

fig, ax = plt.subplots()
ax.plot(range(6), M, label="今年", marker="o")
ax.plot(range(6), [m * 1.1 for m in M], label="去年", marker="s")
ax.legend()
fig.savefig("two_lines.png")
print("两条线 %d 字节" % os.path.getsize("two_lines.png"))

for w, h in [(4, 3), (6, 4), (8, 6)]:
    f, a = plt.subplots(figsize=(w, h))
    a.plot(range(6), M)
    f.savefig("size_%dx%d.png" % (w, h))
    print("figsize (%d,%d) -> %d 字节"
          % (w, h, os.path.getsize("size_%dx%d.png" % (w, h))))
    plt.close(f)

实跑输出:

--------------------------------------------------------------------
plot 函数与画布
--------------------------------------------------------------------

  ① 一条 plot 画多条线
  --------------------------------------------------------------------
  两次 plot 落在同一个 ax 上,就是两条线。
  上半年最低 116000、最高 162000;去年同期的线整体在上面。
  ! 图例要写在 plot 里(label=),不写 label 图例是空的。

  ② 一条 plot 传二维数据
  --------------------------------------------------------------------
  传一个 6 行 2 列的数组,它按列画两条线——和两次 plot 效果一样。
  数据在 ndarray 里时这样写省事,但列和线的对应关系容易搞混。
  ! 列数就是线条数。6 行 2 列画两条,2 行 6 列画六条。

  ③ 画布大小与清晰度
  --------------------------------------------------------------------
  figsize=(4, 3) 存成 12707 字节。
  figsize=(6, 4) 存成 17293 字节。
  figsize=(8, 6) 存成 23720 字节。
  figsize 单位是英寸,默认 (6.4, 4.8)。面积翻倍,字节只涨三成多——文字和坐标轴的开销是固定的。
  dpi 管清晰度,默认 100。要打印就设 300,屏幕上看 100 够了。
  ! 尺寸和 dpi 是两件事。尺寸管「多大」,dpi 管「多细」。两个都调,文件会暴涨。

循环里每张图都要 plt.close(f),不然画布会一直攒着,几十张之后内存爆掉。 plt.close() 是批量画图必写的一行。单张图不写也行,循环里不写会出事。

30 秒实验

  1. 把二维数组转置一下再 plot,看线条数变成几条。
  2. 给两条线显式指定 color='red' 和 color='blue',看能不能覆盖默认配色。
  3. 想一想:为什么 figsize 从 (4,3) 到 (8,6) 面积翻四倍,字节只涨不到一倍?

常见误区

  1. 忘了 label 就调 legend:图例框出来是空的。
  2. 二维数组行列搞反:线条数不对,画出一堆横线。
  3. 循环里不 close 画布:内存一路涨,跑到几十张就卡死。
  4. 以为 figsize 是像素:它是英寸,乘上 dpi 才是像素。
自测三问(点开对答案) 1. 两条线怎么画在同一张图上? —— 两次调用同一个 ax 的 plot,各自给 label,最后 ax.legend()。 2. 一条 plot 传 6 行 2 列的数组会画几条线? —— 两条。按列画,列数就是线条数。 3. figsize 和 dpi 各管什么? —— figsize 管图的物理尺寸(英寸),dpi 管每英寸多少点,两者相乘才是像素尺寸。

图能画了,但坐标轴上的字可能对不上。下一篇专门收拾刻度。

相关笔记