一、展示趋势变化(Evolution)
1. 折线图 - Line chart
折线图(线图)是最基础的趋势类图表,由数据点 + 连接线段构成,和散点图的核心区别:
- 数据点按 X 轴有序排列(通常是时间顺序)
- 用线段连接数据点,直观展示变化
- 核心用途:描述趋势变化(散点图用于描述变量相关性)
最常用于时间序列数据,展示指标随时间的变化趋势,X 轴通常为时间间隔(年 / 月 / 日等)。
注:
- X 轴数据必须有序:否则线段会混乱,无法正确展示趋势
- Y 轴是否从 0 开始:根据分析目的选择,截断 Y 轴会放大局部波动,需标注说明
- 避免双 Y 轴图表:多变量趋势对比时,双 Y 轴易造成视觉误导
- 警惕 "意大利面条图":单图线条过多(>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 列等)
- X 轴无序:折线图的核心是 "趋势",X 轴无序会导致线段混乱,完全失去意义
- 双 Y 轴滥用:多变量对比时,双 Y 轴会通过刻度缩放误导视觉,严禁使用
- Y 轴随意截断:截断 Y 轴会放大局部波动,若必须使用,需在图表中明确标注
- 线条过多:单图线条超过 5 条时,必须用 "突出重点" 或 "多子图" 优化,避免 "意大利面条图"
- 子图刻度不统一:多子图对比时,刻度不一致会造成严重误导,必须强制统一
2.面积图(Area Chart)
面积图是折线图的延伸,核心区别是在折线下方填充了颜色 / 阴影区域,让数据变化趋势更直观、视觉冲击力更强。
- 本质:折线图 + 区域填充
- 核心优势:通过填充区域强化趋势感知,比纯折线图更易读
- 适用场景:单指标 / 多指标的趋势展示、区间对比
- 函数:
fill_between
注:
- Y 轴截断问题:同折线图,截断 Y 轴会放大局部波动,需谨慎使用并标注
- 多类别对比 :
- 优先用堆积面积图;
- 若必须用普通面积图,填充色必须设置透明度(
alpha),确保所有线条可见
- 长宽比优化:合理设置画布长宽比,提升图表可读性
- 视觉一致性:线条与填充色保持统一,填充色设置适度透明度,兼顾美观与可读性
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)
堆积面积图是基础面积图的进阶,将多个类别的数据堆叠展示 ,完美解决多类别对比的遮挡问题,核心特点:
- 无遮挡:不同类别数据堆叠在下方类别之上,不会出现交叉遮挡
- 相对高度表达数值:类别数值由折线间的相对高度体现,而非绝对 Y 轴坐标
- 双重信息 :既展示每个类别的趋势,也能直观体现总体趋势 和类别间的占比 / 重要程度
- 函数:
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()
效果:
- 多类别普通面积图禁用不透明填充 :必须设置
alpha<0.5,否则下层线条完全被遮挡- 堆积面积图类别数量控制:建议类别≤5 个,过多会导致分层过细、可读性下降
- Y 轴统一原则:多图对比时,必须保证 Y 轴刻度一致,避免视觉误导
- 堆积图绝对数值对比误区:堆积图的类别高度是相对值,不能直接对比不同类别的绝对数值,如需对比绝对数值,优先用多折线图 / 多子图
- 颜色选择:使用高对比度、区分度高的配色,避免相近色导致分层不清晰
二、展示分布关系(Distribution)
1.小提琴图(Violin Plot)
小提琴图是箱型图 + 核密度图的结合体,用于展示多组数据的分布状态与概率密度:
- 核心优势:既保留了箱型图的分位数信息,又通过核密度曲线展示了数据的完整分布形态
- 与箱型图的区别:箱型图仅展示分位数,小提琴图描述了基础数据分布的核密度估计,所有绘图单元对应实际数据点
- 适用场景:多分类变量下连续数据的分布对比,适合数据量较大的场景
- 函数:
seaborn.violinplot
注:
- 不适合少组别数据:组别过少时,小提琴图的分布展示优势无法体现
- 按中位数排序:将组别按中位数排序,能让数据对比更直观
- 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.5IQR 规则自动标记异常数据
- 偏态与尾重分析:通过箱体位置、触须长度,粗略判断数据分布的偏态(左偏 / 右偏)和尾重
- 多组数据对比:并行排列的箱型图,可直观对比多组数据的中位数、分布区间、异常值
注:
- 隐藏数据量信息:箱型图不展示样本量,可通过标注或箱子宽度补充
- 隐藏分布细节:无法展示数据的多峰、聚类等分布特征,数据量小时用抖动图 (jitter),数据量大时用小提琴图补充
- 异常值处理:异常值仅为统计标记,需结合业务场景判断是否为真实异常
(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")
注:
- 小提琴图小样本禁用:样本量过小时,核密度估计会失真,无法反映真实分布
- 箱型图分布误读:箱型图无法区分均匀分布、正态分布、多峰分布,仅靠箱型图判断分布会出错
- 嵌套分类对比误区 :双分类嵌套时,优先用
split=True的小提琴图或分组箱型图,避免视觉混淆- 异常值误判:1.5IQR 是统计阈值,不是业务标准,异常值需结合业务场景验证
- 排序优化:多组对比时,按中位数 / 均值排序,让图表更易读
3.直方图 (Histogram)
直方图是最基础的数值分布展示图表,核心逻辑是:
- 仅接收数值型变量,将数据按区间(分箱 / 箱子)切割
- 每个箱子的高度代表该区间内的数据数量(频数 / 频率)
- 核心作用:直观展示数据的分布形态(如正态、偏态、多峰)、集中趋势、离散程度
注:
- 分箱数量选择:分箱过多会导致噪声过多、分布不清晰;分箱过少会掩盖数据细节,需根据数据量调整
- 变量数量限制:不建议在单图中展示超过 5 个变量的分布,否则会严重拥挤、可读性极差
- 配色规范:避免使用过多彩色,优先用单色 / 低饱和度配色,保证图表简洁易读
函数: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
注:
- 带宽 (bw) 参数控制 :带宽决定了曲线的平滑程度:
- 带宽过小:曲线过于尖锐,过度拟合噪声
- 带宽过大:曲线过于平滑,掩盖数据的真实分布特征
- 默认参数为自动最优估计,可根据需求手动调整
- 变量数量限制:同直方图,单图不建议展示超过 5 个变量的分布
- 配色规范:避免使用彩色,优先用高对比度单色,保证多曲线对比清晰
(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 分布,同时呈现单变量与双变量分布
注:
- 直方图分箱陷阱 :分箱数量直接影响分布形态,切勿随意选择,可通过
bins参数手动调整,或使用 Sturges 规则自动计算- 密度图带宽误调:带宽是 KDE 图的核心参数,默认参数通常最优,手动调整需谨慎,避免过度拟合 / 过度平滑
- 多变量分布对比误区:单图展示超过 5 个变量的分布时,无论直方图还是密度图都会失去可读性,优先用多子图拆分
- 直方图≠柱状图:直方图用于数值型变量的分布,柱子连续无间隔;柱状图用于分类变量的计数,柱子独立有间隔,切勿混淆
- 二元密度图样本量要求:二元 KDE 图需要足够的样本量,小样本会导致估计失真,不适合小数据场景
三、展示相关关系(Correlation)
1.散点图(Scatter plot)
散点图用于直观展示两个数值型变量的相关性,还可以通过颜色、大小区分样本所属类别,是探索性数据分析(EDA)中最常用的图表之一。
(1)关键注意事项:Overplotting(点重叠问题)
当样本量极大时,散点会大量重叠,导致无法看清数据分布,这就是 Overplotting。解决方法有 3 种:
- 抽样作图 :从全量数据中随机抽取部分样本(如代码中
.sample(1000)) - 用热力图 / 蜂窝图替代:用密度 / 计数展示分布,而非单个点
- 调小样本点尺寸 :通过
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)关键注意事项
- 数据预处理:长尾分布数据建议先做标准化 / 对数变换
- 分箱策略:数值变量可选择等间隔分箱、分位数分箱;分类变量可按需合并类别
- 图表选择:大样本量用蜂窝热力图(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)关键注意事项
- 用面积而非直径映射数值:人眼对面积的感知更准确,避免用直径直接映射导致视觉偏差
- 避免 Overplotting:和散点图一样,大样本量需抽样、调小尺寸或分层展示
- 配色与透明度 :用渐变色映射数值,设置
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)关键注意事项
- 不要和直方图混淆:柱状图用于分类变量,直方图用于连续变量的分布
- 长标签用横向柱状图:避免标签重叠,提升可读性
- 必须排序展示:按数值从高到低排序,让读者快速抓住规律
(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)关键注意事项
- 单图组别≤5 个:组别过多会导致图表混乱,可读性极差
- 标度一致:不同组别标度差异大时,禁止使用雷达图,会产生严重误导
- 变量数不宜过多:一般控制在 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)关键注意事项
- 不适合组别过多:类别过多会导致曲线交叉严重,无法区分
- 排序优化:可在 X 轴对数据排序,减少曲线交叉,提升可读性
- 配色区分:用清晰的配色区分不同类别,避免混淆
(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)关键注意事项
- 必须排序:排序后展示效果最佳,否则不如直接用柱状图
- 点的样式:用清晰的圆点标记,线的颜色与点区分,提升可读性
- 适合类别数适中:类别过多时,线会过于密集,可读性下降
(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/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)关键注意事项
- 百分比总和必须为 100%:数据需归一化,避免出现逻辑错误
- 禁止使用 3D 效果和图例:3D 会扭曲扇形面积,图例增加阅读成本,直接在扇区标注标签
- 类别数不宜过多:超过 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)关键注意事项
和饼图完全一致:
- 百分比总和必须为 100%
- 禁止 3D 效果和图例,直接标注标签
- 类别数不宜过多,多层嵌套时控制子组数量
(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)关键注意事项
- 集合数≤3 个:超过 3 个集合的文氏图会极度复杂,完全不便于理解
- 标注清晰:图中数字必须明确代表交集的元素个数
- 数据类型 :支持
set或tuple格式,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)关键注意事项
- 层级≤3 级:超过 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()
- 图表解读:用颜色深浅映射数值大小,数值越大颜色越深,同时用面积展示占比,实现双维度可视化。