Matplotlib第六章场景案例显神通

一、展示趋势变化(Evolution)

1. 折线图 - Line chart

折线图(线图)是最基础的趋势类图表,由数据点 + 连接线段构成,和散点图的核心区别:

  • 数据点按 X 轴有序排列(通常是时间顺序)
  • 用线段连接数据点,直观展示变化
  • 核心用途:描述趋势变化(散点图用于描述变量相关性)

最常用于时间序列数据,展示指标随时间的变化趋势,X 轴通常为时间间隔(年 / 月 / 日等)。

注:

  1. X 轴数据必须有序:否则线段会混乱,无法正确展示趋势
  2. Y 轴是否从 0 开始:根据分析目的选择,截断 Y 轴会放大局部波动,需标注说明
  3. 避免双 Y 轴图表:多变量趋势对比时,双 Y 轴易造成视觉误导
  4. 警惕 "意大利面条图":单图线条过多(>5 条)会完全失去可读性,需用优化方案

(1)简单单线图

复制代码
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 1. 创建有序数据(X轴必须有序)
df = pd.DataFrame({'xdata': range(1,101), 'ydata': np.random.randn(100)})

# 2. 样式与画布设置
plt.style.use('seaborn-darkgrid')  # 可选 seaborn-whitegrid 等风格
plt.figure(figsize=(15, 10))      # 设置画布大小

# 3. 绘图核心参数
plt.plot(
    'xdata', 'ydata', data=df,
    color='blue',        # 线条颜色:red/skyblue/blue等
    alpha=0.3,           # 线条透明度(0-1)
    linestyle='-.',      # 线条样式:-- / - / -. / : 等
    linewidth=2,         # 线条粗细
    label='linestyle'    # 图例标签
)

# 4. 图表美化
plt.legend(loc='upper left', frameon=False)  # 图例设置
plt.title('Basic line plot')                  # 标题
plt.show()

效果:生成单条折线,清晰展示单变量随 X 轴的变化趋势,适合单一指标的趋势分析。

(2)突出重点的多线图

当需要对比多个变量的趋势时,直接在单图中画多条线会造成混乱

核心思路:将非重点线条设为灰色背景,重点线条用亮色 + 加粗突出,视觉焦点集中。

复制代码
# 1. 先将所有线条画成灰色背景
for column in df.drop('YYYY', axis=1):
    plt.plot(df['YYYY'], df[column], color='grey', linewidth=1, alpha=0.4)

# 2. 单独处理重点线条(PA州),用亮色+加粗
plt.plot(df['YYYY'], df['PA'], color='orange', linewidth=4, alpha=0.7)

# 3. 用文本标注替代传统图例,直接在线条末端标注名称
plt.text(2017.02, df.PA.tail(1), 'PA', color='orange')
# 其他州用灰色标注
for i in df.values[7][1:]:
    if name != 'PA':
        plt.text(2017.02, i, name, color='grey')

优势

  • 视觉焦点明确,一眼抓住核心趋势
  • 避免传统图例的来回对照,可读性大幅提升
  • 适合需要突出某一主体(如某产品、某地区)的场景

(3)多子图拆分

核心思路:将每条线拆分为独立子图,统一 X/Y 轴刻度,避免单图拥挤,同时保证对比公平。

复制代码
# 1. 初始化画布与颜色
plt.style.use('seaborn-darkgrid')
palette = plt.get_cmap('Set1')
plt.figure(figsize=(15, 10))

# 2. 循环绘制子图
num=0
for column in df.drop('YYYY', axis=1):
    num +=1
    plt.subplot(3,3, num)  # 3行3列的子图布局
    plt.plot(df['YYYY'], df[column], color=palette(num), linewidth=1.9, alpha=0.9)
    
    # 【关键】统一所有子图的X/Y轴范围,避免误导
    plt.xlim(2009.3, 2017.3)
    plt.ylim(0, 50000)
    plt.title(column, loc='left', color=palette(num))

# 3. 全局标题与坐标轴标注
plt.suptitle("How many DrugReports the 5 states have in past few years?", y=0.95)
plt.text(2014, -9500, 'Year', ha='center')
plt.text(1998, 60000, 'DrugReports', rotation='vertical', ha='center')
plt.show()

注:

  • 必须统一所有子图的 X/Y 轴刻度:否则会造成视觉误导,错误放大 / 缩小趋势
  • 适合多主体(如多产品、多地区)的趋势对比,每个子图独立清晰
  • 子图布局根据线条数量调整(如 2 行 3 列、3 行 2 列等)
  1. X 轴无序:折线图的核心是 "趋势",X 轴无序会导致线段混乱,完全失去意义
  2. 双 Y 轴滥用:多变量对比时,双 Y 轴会通过刻度缩放误导视觉,严禁使用
  3. Y 轴随意截断:截断 Y 轴会放大局部波动,若必须使用,需在图表中明确标注
  4. 线条过多:单图线条超过 5 条时,必须用 "突出重点" 或 "多子图" 优化,避免 "意大利面条图"
  5. 子图刻度不统一:多子图对比时,刻度不一致会造成严重误导,必须强制统一

2.面积图(Area Chart)

面积图是折线图的延伸,核心区别是在折线下方填充了颜色 / 阴影区域,让数据变化趋势更直观、视觉冲击力更强。

  • 本质:折线图 + 区域填充
  • 核心优势:通过填充区域强化趋势感知,比纯折线图更易读
  • 适用场景:单指标 / 多指标的趋势展示、区间对比
  • 函数:fill_between

注:

  1. Y 轴截断问题:同折线图,截断 Y 轴会放大局部波动,需谨慎使用并标注
  2. 多类别对比
    • 优先用堆积面积图
    • 若必须用普通面积图,填充色必须设置透明度(alpha),确保所有线条可见
  3. 长宽比优化:合理设置画布长宽比,提升图表可读性
  4. 视觉一致性:线条与填充色保持统一,填充色设置适度透明度,兼顾美观与可读性

fill_between 是更灵活的实现方式,支持高度自定义,核心代码与参数说明:

复制代码
import numpy as np
import matplotlib.pyplot as plt

# 1. 创建有序数据(X轴必须有序)
x = range(1,15)
y = [1,4,6,7,4,9,3,2,4,1,5,4,8,7]

# 2. 基础填充面积图
# facecolor: 填充颜色;alpha: 透明度;hatch: 阴影样式
plt.fill_between(x, y, facecolor="skyblue", alpha=0.4, hatch='/')
plt.show()

# 3. 进阶:填充+折线叠加(更清晰)
plt.fill_between(x, y, facecolor="skyblue", alpha=0.2)
# 叠加同色系折线,强化趋势
plt.plot(x, y, color="skyblue", alpha=0.6, linewidth=1.5)
plt.show()

效果:

  • 左图:纯填充 + 阴影样式,适合强调区间
  • 右图:填充 + 折线叠加,兼顾趋势与区间,是更推荐的标准用法

3.堆积面积图(Stacked Area Chart)

堆积面积图是基础面积图的进阶,将多个类别的数据堆叠展示 ,完美解决多类别对比的遮挡问题,核心特点:

  1. 无遮挡:不同类别数据堆叠在下方类别之上,不会出现交叉遮挡
  2. 相对高度表达数值:类别数值由折线间的相对高度体现,而非绝对 Y 轴坐标
  3. 双重信息 :既展示每个类别的趋势,也能直观体现总体趋势类别间的占比 / 重要程度
  4. 函数: stackplot

适用场景:

  • 多类别时间序列数据的趋势对比
  • 展示各部分对总体的贡献(如各渠道销售额占总销售额的变化)
  • 体现类别间的结构占比随时间的演变

stackplot 是堆积面积图的专用函数,支持多 Y 序列输入,核心代码:

复制代码
import numpy as np
import matplotlib.pyplot as plt

plt.style.use('seaborn-darkgrid')
plt.figure(figsize=(10, 6))

# 方式一:多Y序列输入
x = range(1,6)
y = [
    [1,4,6,8,9],   # A类数据
    [2,2,7,10,12], # B类数据
    [2,8,5,10,6]   # C类数据
]

# 绘制堆积面积图
plt.stackplot(x, y, labels=['A','B','C'])
plt.legend(loc='upper left')
plt.show()

效果:

实战示例(真实数据)

复制代码
import matplotlib.pyplot as plt
import pandas as pd

# 1. 数据预处理(以DrugReports数据集为例)
Dataset = pd.read_csv('data/Drugs.csv')
group = Dataset.groupby(['YYYY','State']).agg('sum').reset_index()
df = group.pivot(index='YYYY', columns='State', values='DrugReports').reset_index()

# 2. 绘图设置
plt.style.use('seaborn-darkgrid')
plt.figure(figsize=(10, 6))

# 3. 绘制堆积面积图
plt.stackplot(
    df['YYYY'], 
    df['KY'], df['OH'], df['PA'], df['VA'], df['WV'],
    labels=df.iloc[:, 1:6].columns
)
plt.legend(loc='upper left')
plt.show()

效果:

  1. 多类别普通面积图禁用不透明填充 :必须设置alpha<0.5,否则下层线条完全被遮挡
  2. 堆积面积图类别数量控制:建议类别≤5 个,过多会导致分层过细、可读性下降
  3. Y 轴统一原则:多图对比时,必须保证 Y 轴刻度一致,避免视觉误导
  4. 堆积图绝对数值对比误区:堆积图的类别高度是相对值,不能直接对比不同类别的绝对数值,如需对比绝对数值,优先用多折线图 / 多子图
  5. 颜色选择:使用高对比度、区分度高的配色,避免相近色导致分层不清晰

二、展示分布关系(Distribution)

1.小提琴图(Violin Plot)

小提琴图是箱型图 + 核密度图的结合体,用于展示多组数据的分布状态与概率密度:

  • 核心优势:既保留了箱型图的分位数信息,又通过核密度曲线展示了数据的完整分布形态
  • 与箱型图的区别:箱型图仅展示分位数,小提琴图描述了基础数据分布的核密度估计,所有绘图单元对应实际数据点
  • 适用场景:多分类变量下连续数据的分布对比,适合数据量较大的场景
  • 函数:seaborn.violinplot

注:

  1. 不适合少组别数据:组别过少时,小提琴图的分布展示优势无法体现
  2. 按中位数排序:将组别按中位数排序,能让数据对比更直观
  3. inner 参数选择inner='quartile' 可在小提琴内部显示四分位数线,兼顾分布与分位数信息

(1)基础单分类小提琴图

复制代码
import seaborn as sns
import pandas as pd

# 加载数据(以经典鸢尾花数据集为例)
df = pd.read_csv('data/iris.csv')

# 绘制纵向小提琴图:x为分类变量,y为连续变量
sns.violinplot(
    x=df['species'], 
    y=df["sepal_length"], 
    inner='quartile'  # 内部显示四分位数线
)

(2)双分类嵌套小提琴图

复制代码
tips = pd.read_csv('data/tips.csv')

# 按day分组,smoker作为hue嵌套分类
ax = sns.violinplot(
    x="day", 
    y="total_bill", 
    hue="smoker",
    data=tips, 
    palette="muted"
)

(3)分割式小提琴图(split=True

复制代码
# split=True将同一x下的两个hue类别拆分到小提琴两侧,对比更直观
ax = sns.violinplot(
    x="day", 
    y="total_bill", 
    hue="smoker",
    data=tips, 
    palette="muted", 
    split=True
)

2.箱型图(Box Plot,盒须图)

箱型图是经典的分布展示图表,通过分位数直观展示数据分布,核心元素:

  • 箱体:显示数据集的上四分位数 (Q3)、中位数 (Q2)、下四分位数 (Q1)
  • 触须( whiskers ):延伸至 Q1-1.5IQR 和 Q3+1.5IQR(IQR = 四分位距 = Q3-Q1)
  • 异常值:超过触须范围的数据点,用于识别极端值
  • 函数seaborn.boxplot

作用:

  1. 异常值识别:通过 1.5IQR 规则自动标记异常数据
  2. 偏态与尾重分析:通过箱体位置、触须长度,粗略判断数据分布的偏态(左偏 / 右偏)和尾重
  3. 多组数据对比:并行排列的箱型图,可直观对比多组数据的中位数、分布区间、异常值

注:

  1. 隐藏数据量信息:箱型图不展示样本量,可通过标注或箱子宽度补充
  2. 隐藏分布细节:无法展示数据的多峰、聚类等分布特征,数据量小时用抖动图 (jitter),数据量大时用小提琴图补充
  3. 异常值处理:异常值仅为统计标记,需结合业务场景判断是否为真实异常

(1)基础单分类箱型图

复制代码
import seaborn as sns
import pandas as pd

sns.set(style="whitegrid")
tips = pd.read_csv('data/tips.csv')

# 按day分组绘制箱型图
ax = sns.boxplot(
    x="day", 
    y="total_bill", 
    data=tips
)

(2)双分类嵌套箱型图

复制代码
# 按day分组,smoker作为hue嵌套分类
ax = sns.boxplot(
    x="day", 
    y="total_bill", 
    hue="smoker",
    data=tips, 
    palette="Set2"
)

(3)箱型图 + 散点图叠加(展示原始数据)

复制代码
# 先画箱型图,再用swarmplot叠加原始数据点,兼顾分位数与原始分布
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax = sns.swarmplot(x="day", y="total_bill", data=tips, color=".25")

注:

  1. 小提琴图小样本禁用:样本量过小时,核密度估计会失真,无法反映真实分布
  2. 箱型图分布误读:箱型图无法区分均匀分布、正态分布、多峰分布,仅靠箱型图判断分布会出错
  3. 嵌套分类对比误区 :双分类嵌套时,优先用split=True的小提琴图或分组箱型图,避免视觉混淆
  4. 异常值误判:1.5IQR 是统计阈值,不是业务标准,异常值需结合业务场景验证
  5. 排序优化:多组对比时,按中位数 / 均值排序,让图表更易读

3.直方图 (Histogram)

直方图是最基础的数值分布展示图表,核心逻辑是:

  • 仅接收数值型变量,将数据按区间(分箱 / 箱子)切割
  • 每个箱子的高度代表该区间内的数据数量(频数 / 频率)
  • 核心作用:直观展示数据的分布形态(如正态、偏态、多峰)、集中趋势、离散程度

注:

  1. 分箱数量选择:分箱过多会导致噪声过多、分布不清晰;分箱过少会掩盖数据细节,需根据数据量调整
  2. 变量数量限制:不建议在单图中展示超过 5 个变量的分布,否则会严重拥挤、可读性极差
  3. 配色规范:避免使用过多彩色,优先用单色 / 低饱和度配色,保证图表简洁易读

函数:seaborn.histplot

复制代码
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_boston

# 加载数据(以波士顿房价数据集为例)
boston = load_boston()
y = boston['target']

# 创建3行1列的子图画布
f, axs = plt.subplots(3, 1, figsize=(10, 10))

# 1. 计数标准直方图(stat='count',展示频数)
sns.histplot(y, stat='count', ax=axs[0])

# 2. 归一化直方图(stat='probability',展示频率/概率)
sns.histplot(y, stat='probability', ax=axs[1])

# 3. 直方图+密度曲线(kde=True,同时展示分布与核密度估计)
sns.histplot(y, stat='probability', kde=True, ax=axs[2])

plt.show()

效果:

  • 第一行:标准计数直方图,直观展示各房价区间的样本数量
  • 第二行:归一化直方图,将频数转换为概率,方便不同样本量数据对比
  • 第三行:直方图 + KDE 曲线,结合了直方图的离散分箱与密度图的连续分布,是最推荐的用法

4.密度图(Density Plot / KDE Plot)

密度图(核密度估计图,KDE Plot)是直方图的平滑化延伸:

  • 同样用于展示数值型变量的分布,通过核密度估计将离散的直方图转换为连续平滑的曲线
  • 核心优势:更直观地展示数据的分布形态,不受分箱数量影响,适合多组分布对比
  • 支持一元 / 二元分布展示:一元用曲线,二元用等高线 / 热力图
  • 函数seaborn.kdeplot / jointplot

注:

  1. 带宽 (bw) 参数控制 :带宽决定了曲线的平滑程度:
    • 带宽过小:曲线过于尖锐,过度拟合噪声
    • 带宽过大:曲线过于平滑,掩盖数据的真实分布特征
    • 默认参数为自动最优估计,可根据需求手动调整
  2. 变量数量限制:同直方图,单图不建议展示超过 5 个变量的分布
  3. 配色规范:避免使用彩色,优先用高对比度单色,保证多曲线对比清晰

(1)一元密度图(带宽对比)

复制代码
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# 生成正态分布随机数据
x = np.random.normal(size=100)

# 绘制不同带宽的KDE曲线
sns.kdeplot(x, label="bw: default")
sns.kdeplot(x, bw_method=0.2, label="bw: 0.2")  # 小带宽,曲线尖锐
sns.kdeplot(x, bw_method=2, label="bw: 2")    # 大带宽,曲线平滑

plt.legend()
plt.show()

效果:

  • 蓝色(默认):平衡平滑度与数据特征,是通用选择
  • 橙色(bw=0.2):过度拟合,保留了数据噪声
  • 绿色(bw=2):过度平滑,丢失了数据的分布细节

(2) 二元密度图(等高线图)

复制代码
import numpy as np
import pandas as pd
import seaborn as sns

# 生成二元正态分布数据
mean, cov = [0, 1], [(1, .5), (.5, 1)]
data = np.random.multivariate_normal(mean, cov, 200)
df = pd.DataFrame(data, columns=["x", "y"])

# 绘制二元KDE等高线图,同时展示x/y的边缘分布
sns.jointplot(x="x", y="y", data=df, kind="kde")
plt.show()

效果:

  • 中心区域:等高线展示了 x 与 y 的联合分布,越密集的区域数据点越多
  • 上下 / 左右边缘:分别展示了 x、y 变量的一元 KDE 分布,同时呈现单变量与双变量分布

注:

  1. 直方图分箱陷阱 :分箱数量直接影响分布形态,切勿随意选择,可通过bins参数手动调整,或使用 Sturges 规则自动计算
  2. 密度图带宽误调:带宽是 KDE 图的核心参数,默认参数通常最优,手动调整需谨慎,避免过度拟合 / 过度平滑
  3. 多变量分布对比误区:单图展示超过 5 个变量的分布时,无论直方图还是密度图都会失去可读性,优先用多子图拆分
  4. 直方图≠柱状图:直方图用于数值型变量的分布,柱子连续无间隔;柱状图用于分类变量的计数,柱子独立有间隔,切勿混淆
  5. 二元密度图样本量要求:二元 KDE 图需要足够的样本量,小样本会导致估计失真,不适合小数据场景

三、展示相关关系(Correlation)

1.散点图(Scatter plot)

散点图用于直观展示两个数值型变量的相关性,还可以通过颜色、大小区分样本所属类别,是探索性数据分析(EDA)中最常用的图表之一。

(1)关键注意事项:Overplotting(点重叠问题)

当样本量极大时,散点会大量重叠,导致无法看清数据分布,这就是 Overplotting。解决方法有 3 种:

  1. 抽样作图 :从全量数据中随机抽取部分样本(如代码中 .sample(1000)
  2. 用热力图 / 蜂窝图替代:用密度 / 计数展示分布,而非单个点
  3. 调小样本点尺寸 :通过 s 参数缩小点的大小,减少遮挡

(2)基础散点图(matplotlib 原生实现)

复制代码
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# 读取数据并抽样1000条,避免overplotting
df = pd.read_csv('data\diamonds.csv').sample(1000)

# 绘制标准散点图:carat(克拉) vs price(价格),s=0.2缩小点尺寸
plt.scatter(df.carat, df.price, s=0.2)
plt.show()
  • 图表解读:可以清晰看到钻石克拉数与价格呈强正相关,克拉越大,价格整体越高,同时存在明显的长尾分布(大克拉钻石数量少、价格波动大)。

(3)带分类的散点图(seaborn lmplot 实现)

复制代码
# 用颜色区分不同切工(cut)的散点,fit_reg=False关闭拟合线
sns.lmplot(x='carat', y='price', data=df, hue='cut', fit_reg=False)
plt.show()
  • 图表解读:通过颜色区分了 5 种切工(Premium/Ideal/Fair/Good/Very Good),可以直观对比不同切工下克拉与价格的分布差异,比如 Ideal 切工的样本在中低克拉区间更集中。

2.热力图(Heatmap)

热力图通过颜色深浅表示数值大小,用于展示二维数据的分布、计数或相关性

  • 分类变量:展示两个分类变量的交叉计数(如切工 × 净度)
  • 数值变量:先分箱再统计,展示区间内的样本分布
  • 相关性矩阵:展示多个变量间的相关系数(最常用场景之一)

(1)关键注意事项

  1. 数据预处理:长尾分布数据建议先做标准化 / 对数变换
  2. 分箱策略:数值变量可选择等间隔分箱、分位数分箱;分类变量可按需合并类别
  3. 图表选择:大样本量用蜂窝热力图(hexbin)替代普通散点图,解决 overplotting

(2)分类变量交叉热力图

复制代码
# 统计切工(cut)与净度(clarity)的交叉计数
res = pd.crosstab(df.cut, df.clarity)
# 绘制热力图:cmap指定配色,annot=True显示数值
sns.heatmap(res, cmap='Greens', annot=True)
plt.show()
  • 图表解读:颜色越深代表该组合的样本量越多,比如 Ideal 切工 + VS2 净度的样本量高达 90,是数据中最常见的组合。

(3) 数值 + 分类变量分箱热力图

复制代码
# 将价格(price)等分为5箱,与净度(clarity)做交叉统计
res = pd.crosstab(pd.qcut(df.price, 5), df.clarity)
sns.heatmap(res, cmap='Greens', annot=True)
plt.show()
  • 图表解读:展示了不同价格区间下,各净度等级的样本分布,比如高价格区间中 SI1/SI2 净度的样本占比更高。

(4)蜂窝热力图(Hexbin Plot,解决 overplotting)

复制代码
# 数值变量的密度蜂窝图:price vs carat
sns.jointplot(x=df["price"], y=df["carat"], kind='hex')
plt.show()
  • 图表解读:用六边形格子的颜色深浅表示该区域的样本密度,完美解决大样本散点重叠问题,同时保留了变量的边缘分布(上下 / 左右的直方图)。

3.长尾分布数据的优化方法

钻石价格、克拉数天然是长尾分布(大部分样本集中在低区间,少数极端值在高区间),直接绘图会导致分布偏斜,3 种优化方案如下:

(1)对数变换(Log Transform)

复制代码
# 对price和carat做对数变换,压缩长尾
sns.jointplot(x=np.log(df["price"]), y=np.log(df["carat"]), kind='hex')
plt.show()
  • 效果:将非线性的正相关转化为近似线性,让数据分布更均匀,相关性更清晰。

(2) 标准差截断(Std Deviation Clipping)

复制代码
s1, s2 = df.price, df.carat
# 保留中位数±1倍标准差内的数据,过滤极端值
s1 = s1.mask((s1>(s1.median()+1*s1.std()))|(s1<(s1.median()-s1.std())))
s2 = s2.mask((s2>(s2.median()+1*s2.std()))|(s2<(s2.median()-s2.std())))
sns.jointplot(x=s1, y=s2, kind='hex')
plt.show()
  • 效果:剔除极端异常值,聚焦主体数据的分布规律。

(3) 分位数截断(Quantile Clipping)

复制代码
s1, s2 = df.price, df.carat
# 保留5%~95%分位数内的数据,过滤首尾极端值
s1 = s1.mask((s1>(s1.quantile(0.95)))|(s1<(s1.quantile(0.05))))
s2 = s2.mask((s2>(s2.quantile(0.95)))|(s2<(s2.quantile(0.05))))
sns.jointplot(x=s1, y=s2, kind='hex')
plt.show()
  • 效果:按比例过滤极端值,更灵活地聚焦核心数据,适合非正态分布的长尾数据。

4.气泡图(Bubble Plot)

气泡图是散点图的衍生 ,用于展示超过二维的特征关系

  • 横轴、纵轴:两个基础数值变量(如钻石的克拉数、价格)
  • 气泡大小:第三维数值特征
  • 气泡颜色:第四维数值 / 分类特征
  • 也可用于分类变量 + 数值变量的多维度展示

(1)关键注意事项

  1. 用面积而非直径映射数值:人眼对面积的感知更准确,避免用直径直接映射导致视觉偏差
  2. 避免 Overplotting:和散点图一样,大样本量需抽样、调小尺寸或分层展示
  3. 配色与透明度 :用渐变色映射数值,设置alpha透明度减少遮挡

(2)代码与效果演示

复制代码
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 读取钻石数据(承接前文)
df = pd.read_csv('data\diamonds.csv')

# 生成两个新特征,分别映射气泡大小和颜色
new_feature1 = np.random.randint(0, 10, 10)  # 用气泡大小显示该特征
new_feature2 = np.random.randint(0, 10, 10)  # 用气泡深浅显示该特征

# 绘制数值型气泡图:carat(克拉) vs price(价格)
plt.scatter(
    df.carat.sample(10), df.price.sample(10),
    s=new_feature1*100,  # 大小映射,*100放大视觉效果
    c=new_feature2, cmap="Blues",  # 颜色映射,Blues渐变色
    alpha=0.8, edgecolors="grey", linewidth=2  # 透明度、描边
)
plt.xlabel('Carat')
plt.ylabel('Price')
plt.show()

# 绘制分类+数值型气泡图:cut(切工) vs price(价格)
plt.scatter(
    df.cut.sample(10), df.price.sample(10),
    s=new_feature1*100, c=new_feature2, cmap="Blues",
    alpha=0.8, edgecolors="grey", linewidth=2
)
plt.xlabel('Cut')
plt.ylabel('Price')
plt.show()
  • 图表解读:气泡大小、颜色分别代表两个额外特征,直观展示了 4 维数据的分布关系,同时保留了基础变量的相关性。

四.展示排序信息类图表

1. 柱状图(Barplot)

展示一个分类变量和一个数值变量的关系,柱子长度代表类别对应的数值,是展示排序信息最经典、最有效的方式之一。

(1)关键注意事项

  1. 不要和直方图混淆:柱状图用于分类变量,直方图用于连续变量的分布
  2. 长标签用横向柱状图:避免标签重叠,提升可读性
  3. 必须排序展示:按数值从高到低排序,让读者快速抓住规律

(2)代码与效果演示

复制代码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 读取宝可梦数据,按Type 1分组,计算Total属性的平均值
pokemon = pd.read_csv('data/pokemon.csv')
data = pokemon.groupby('Type 1')['Total'].mean().sort_values(ascending=False).reset_index()

# 绘制柱状图
bars = data['Type 1']
pos = np.arange(len(bars))
plt.bar(pos, data['Total'])
plt.xticks(pos, bars, rotation=270)  # 旋转标签避免重叠
plt.xlabel('Pokemon Type')
plt.ylabel('Average Total')
plt.title('Average Total by Pokemon Type')
plt.show()
  • 图表解读:按宝可梦属性分组,展示了各属性的平均总能力值,排序后可以快速看出龙系(Dra)平均总能力最高,虫系(Bug)最低。

2. 雷达图(Radar Chart)

展示多个定量变量的二维对比,所有变量交汇于中心,适合对比少量样本的多维度特征(如宝可梦的 6 项属性)。

(1)关键注意事项

  1. 单图组别≤5 个:组别过多会导致图表混乱,可读性极差
  2. 标度一致:不同组别标度差异大时,禁止使用雷达图,会产生严重误导
  3. 变量数不宜过多:一般控制在 5-8 个维度,否则雷达图会过于密集

(2)代码与效果演示

复制代码
from math import pi
import pandas as pd
import matplotlib.pyplot as plt

# 读取宝可梦数据,选择2只宝可梦对比6项属性
pokemon = pd.read_csv('data/pokemon.csv')
data = pokemon.loc[[0,4]]  # 妙蛙种子、小火龙
categories = ['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']
N = 6

# 计算极坐标角度
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]  # 闭合图形

# 初始化极坐标子图
ax = plt.subplot(111, polar=True)
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)

# 设置坐标轴标签
plt.xticks(angles[:-1], categories)
ax.set_rlabel_position(0)
plt.yticks([20,40,60,80], ["20","40","60","80"], color="grey", size=7)
plt.ylim(0,80)

# 绘制妙蛙种子的雷达曲线
values = data.loc[0, ['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed','HP']]
ax.plot(angles, values, linewidth=1, linestyle='solid', label=data.loc[0,'Name'])
ax.fill(angles, values, 'b', alpha=0.1)

# 绘制小火龙的雷达曲线
values = data.loc[4, ['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed','HP']]
ax.plot(angles, values, linewidth=1, linestyle='solid', label=data.loc[4,'Name'])
ax.fill(angles, values, 'r', alpha=0.1)

# 添加图例
plt.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1))
plt.show()
  • 图表解读:直观对比了妙蛙种子和小火龙的 6 项属性,妙蛙种子防御、特防更高,小火龙攻击、速度更优。

3. 平行坐标图(Parallel Coordinates)

雷达图的替代方案,更推荐在多维度对比时使用,用于比较样本在一组数值变量上的特征,适合多类别、多维度的对比。

(1)关键注意事项

  1. 不适合组别过多:类别过多会导致曲线交叉严重,无法区分
  2. 排序优化:可在 X 轴对数据排序,减少曲线交叉,提升可读性
  3. 配色区分:用清晰的配色区分不同类别,避免混淆

(2)代码与效果演示

复制代码
from pandas.plotting import parallel_coordinates
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

# 读取鸢尾花数据集
data = pd.read_csv('data/iris.csv')

# 绘制平行坐标图
parallel_coordinates(data, 'species', colormap=plt.get_cmap("Set2"))
plt.title('Parallel Coordinates of Iris Dataset')
plt.show()
  • 图表解读:用不同颜色的曲线代表 3 种鸢尾花,每条曲线对应一个样本的 4 项花部特征,直观展示了不同物种的特征差异(如 setosa 的花瓣长度明显更短)。

4. 棒棒糖图(Lollipop Plot)

柱状图的优化版本,用线 + 点替代柱子,视觉上更简洁、美观,适合展示排序后的类别数值。

(1)关键注意事项

  1. 必须排序:排序后展示效果最佳,否则不如直接用柱状图
  2. 点的样式:用清晰的圆点标记,线的颜色与点区分,提升可读性
  3. 适合类别数适中:类别过多时,线会过于密集,可读性下降

(2)代码与效果演示

复制代码
import pandas as pd
import matplotlib.pyplot as plt

# 读取宝可梦数据,按Type 1分组计算Total平均值
pokemon = pd.read_csv('data/pokemon.csv')
data = pokemon.groupby('Type 1')['Total'].mean().reset_index()

# 排序(棒棒糖图的核心要求)
data = data.sort_values(by='Total')
my_range = range(1, len(data.index)+1)

# 绘制棒棒糖图
plt.hlines(y=my_range, xmin=0, xmax=data['Total'], color='skyblue')
plt.plot(data['Total'], my_range, "o")  # 圆点标记
plt.yticks(my_range, data['Type 1'])
plt.title("A vertical lollipop plot", loc='left')
plt.xlabel('Average value of Total')
plt.ylabel('Type')
plt.show()
  • 图表解读:用线连接原点与数值点,用圆点标记最终数值,视觉上比柱状图更清爽,同时保留了排序信息。

5. 圆形柱状图(Circular Barplot)

柱状图的创意变体,用极坐标绘制环形柱状图,视觉吸引力强,但仅适合特定场景

  • 类别数量多(>40 个)
  • 有明显突出的类别,需要视觉上强调
  • 仅用于展示,不适合精确对比

(1)关键注意事项

  1. 内圈比例≥外圈的 1/3:避免柱子过长导致图表失衡
  2. 仅适合大量类别:类别少的时候,用普通柱状图更清晰
  3. 不适合精确对比:人眼对环形长度的感知远差于线性长度,仅适合展示趋势

(2)代码与效果演示

复制代码
import pandas as pd
import numpy as np
from math import pi
import matplotlib.pyplot as plt

# 读取宝可梦数据,按Type 1分组计算Total平均值
pokemon = pd.read_csv('data/pokemon.csv')
data = pokemon.groupby('Type 1')['Total'].mean().reset_index()

# 绘制圆形柱状图
N = len(data)
bottom = 250  # 内圈基准值
value = data['Total']
theta = np.linspace(0.0, 2 * pi, N, endpoint=False)
width = (2*pi) / N - 0.02

plt.figure(figsize = (16, 10))
ax = plt.subplot(111, polar=True)
bars = ax.bar(theta, value, width=width, bottom=bottom)

# 设置极坐标方向与标签
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ticks = data['Type 1']

# 添加类别标签
for theta, tick, value in zip(theta, ticks, value):
    ax.text(theta+0.03, value+380, tick)

plt.axis('off')
plt.show()
  • 图表解读:将柱状图环形排列,视觉上更具设计感,但仅适合展示,不适合精确对比数值差异。

五.展示组成关系

1.饼图(Pie Chart)

饼图是最经典的组成关系图表,用圆形分割成多个扇形,直观展示各部分占整体的百分比,适合类别数少(≤5 个)、占比差异明显的场景。

(1)关键注意事项

  1. 百分比总和必须为 100%:数据需归一化,避免出现逻辑错误
  2. 禁止使用 3D 效果和图例:3D 会扭曲扇形面积,图例增加阅读成本,直接在扇区标注标签
  3. 类别数不宜过多:超过 5 个类别时,饼图可读性急剧下降,建议换用柱状图 / 树图

(2) matplotlib 原生实现

复制代码
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()  # 创建1×1画布
size = 0.3
vals = np.array([[60., 32.], [37., 40.], [29., 10.]])  # 3×2数据

# 配置配色
cmap = plt.get_cmap("tab20c")
outer_colors = cmap(np.arange(3)*4)
inner_colors = cmap(np.array([1,2,5,6,9,10]))

# 绘制外层饼图(按行求和)
ax.pie(vals.sum(axis=1))
plt.show()

效果:

(3) pandas 快速绘图

复制代码
import pandas as pd

# 构造4分类数据
df = pd.DataFrame([8,8,1,2], index=['a', 'b', 'c', 'd'], columns=['x'])

# 直接绘制饼图
df.plot(kind='pie', subplots=True, figsize=(8, 8))
plt.show()
  • 图表解读:直观展示了 a、b、c、d 四个类别的占比,a 和 b 占比最高,c、d 占比较小。

2.甜甜圈图(Donut Chart)

饼图的变体,在饼图中心挖空形成环形,视觉上更美观,同时保留了饼图的所有特性,还支持多层嵌套展示「组 - 子组」二级组成关系。

(1)关键注意事项

和饼图完全一致:

  1. 百分比总和必须为 100%
  2. 禁止 3D 效果和图例,直接标注标签
  3. 类别数不宜过多,多层嵌套时控制子组数量

(2)基础甜甜圈图

复制代码
import matplotlib.pyplot as plt

# 数据
names = ['groupA', 'groupB', 'groupC', 'groupD']
size = [12, 11, 3, 30]

# 中心画白色圆实现甜甜圈效果
my_circle = plt.Circle((0,0), 0.7, color='white')

# 绘制外层饼图
plt.pie(size, labels=names, colors=['red','green','blue','skyblue'])
p = plt.gcf()
p.gca().add_artist(my_circle)
plt.show()

效果:

(3) 多层嵌套甜甜圈图(组 + 子组)

复制代码
import matplotlib.pyplot as plt

# 数据:3个主组,10个子组
group_names = ['groupA', 'groupB', 'groupC']
group_size = [12, 11, 30]
subgroup_names = ['A.1', 'A.2', 'A.3', 'B.1', 'B.2', 'C.1', 'C.2', 'C.3', 'C.4', 'C.5']
subgroup_size = [4, 3, 5, 6, 5, 10, 5, 5, 4, 6]

# 配色:同组用同色系渐变
a, b, c = [plt.cm.Blues, plt.cm.Reds, plt.cm.Greens]

# 外层环(主组)
fig, ax = plt.subplots()
ax.axis('equal')
mypie, _ = ax.pie(group_size, radius=1.3, labels=group_names, colors=[a(0.6), b(0.6), c(0.6)] )
plt.setp( mypie, width=0.3, edgecolor='white')

# 内层环(子组)
mypie2, _ = ax.pie(subgroup_size, radius=1.3-0.3, labels=subgroup_names, labeldistance=0.7, 
                   colors=[a(0.5), a(0.4), a(0.3), b(0.5), b(0.4), c(0.6), c(0.5), c(0.4), c(0.3), c(0.2)])
plt.setp( mypie2, width=0.4, edgecolor='white')
plt.margins(0,0)

plt.show()
  • 图表解读:外层展示 3 个主组的占比,内层展示每个主组下子组的占比,完美实现二级组成关系的可视化。

3.文氏图(Venn Diagram)

用于展示不同集合之间的逻辑关系与交集,每个集合用一个圆表示,圆的大小反映集合规模,重叠部分代表两个 / 多个集合的共同元素。

(1)关键注意事项

  1. 集合数≤3 个:超过 3 个集合的文氏图会极度复杂,完全不便于理解
  2. 标注清晰:图中数字必须明确代表交集的元素个数
  3. 数据类型 :支持settuple格式,venn2对应 2 集合,venn3对应 3 集合

(2)两集合文氏图(venn2)

复制代码
import matplotlib.pyplot as plt
from matplotlib_venn import venn2, venn3

# 两集合:(A独有, B独有, AB交集)
venn2(subsets=(3, 2, 4, 1), set_labels=('A', 'B'), set_colors = ('r','g'))
plt.show()
  • 图表解读:A 集合独有 3 个元素,B 集合独有 2 个元素,AB 交集有 4 个元素。

(3)三集合文氏图(venn3)

复制代码
import matplotlib.pyplot as plt
from matplotlib_venn import venn2, venn3

# 三集合:(A独有, B独有, AB交集, C独有, AC交集, BC交集, ABC交集)
venn3(subsets=(1,2,3,4,5,6,0), set_labels=('A', 'B', 'C'), set_colors = ('r','g','b'))
plt.show()

# 直接传入集合对象
venn3(subsets=[set([3, 2, 1,4,5,6]),set([2,3,4]),set([1,2,3,4,5])], 
      set_labels=('A', 'B', 'C'), set_colors = ('lightpink','pink','pink'))
plt.show()

效果:

4.树图(Treemap)

嵌套矩形的面积 展示数据大小,支持多层级(组 - 子组)展示,最大优势是空间利用率极高,适合在有限空间内展示大量类别(几十上百个)的组成关系。

(1)关键注意事项

  1. 层级≤3 级:超过 3 个层级会导致图形不可读
  2. 优先展示最高层级:核心数据放在最外层,子层级仅作补充
  3. 配色映射:用颜色深浅辅助展示数值大小,提升可读性

(2)基础树图

复制代码
import matplotlib.pyplot as plt
import squarify  # 需先安装:pip install squarify

# 数据:4个分组
sizes = [13, 22, 10, 5]
labels = ["group A", "group B", "group C", "group D"]
colors = ["red", "green", "blue", "grey"]

# 绘制树图
squarify.plot(sizes=sizes, label=labels, color=colors, alpha=.4)
plt.axis('off')  # 隐藏坐标轴
plt.show()
  • 图表解读:矩形面积对应数值大小,group B 面积最大,group D 最小,直观展示了 4 组的占比。

(3) 渐变色树图(数值映射颜色)

复制代码
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import squarify

# 构造数据:1-99的立方值
my_values = [i**3 for i in range(1,100)]

# 配置渐变色(Blues色系)
cmap = matplotlib.cm.Blues
mini = min(my_values)
maxi = max(my_values)
norm = matplotlib.colors.Normalize(vmin=mini, vmax=maxi)
colors = [cmap(norm(value)) for value in my_values]

# 绘制树图
squarify.plot(sizes=my_values, alpha=.8, color=colors)
plt.axis('off')
plt.show()
  • 图表解读:用颜色深浅映射数值大小,数值越大颜色越深,同时用面积展示占比,实现双维度可视化。
相关推荐
蜡笔小新..2 小时前
Linux下Matplotlib使用Times New Roman字体的解决方案
linux·运维·matplotlib
李昊哲小课1 天前
Pandas数据分析 - 第十一章:数据可视化
信息可视化·数据挖掘·数据分析·pandas·matplotlib
绛橘色的日落(。・∀・)ノ1 天前
Matplotlib 第五章 样式色彩秀芳华
matplotlib
书到用时方恨少!2 天前
Python Matplotlib 使用指南:数据可视化的画笔
python·信息可视化·matplotlib
人工干智能3 天前
科普:%%matplotlib inline:魔法命令 (Cell Magic)
python·matplotlib
绛橘色的日落(。・∀・)ノ5 天前
Matplotlib 第四章 文字图例尽眉目
matplotlib
喝凉白开都长肉的大胖子6 天前
在 Matplotlib 中fontweight一般怎么设置
python·matplotlib
绛橘色的日落(。・∀・)ノ6 天前
Matplotlib 第三章 布局格式定方圆
matplotlib
绛橘色的日落(。・∀・)ノ7 天前
Matplotlib 第二章 艺术画笔见乾坤
matplotlib