数据可视化是数据分析和机器学习的核心环节,Matplotlib和Seaborn是Python生态中最常用的可视化工具------Matplotlib侧重灵活定制图表,Seaborn专注统计可视化,二者结合能高效绘制特征分布图、损失曲线、相关性热力图等高频图表。
一、环境准备与基础配置
1. 安装与导入
确保安装核心库,建议搭配Pandas处理数据:
bash
pip install matplotlib seaborn pandas numpy
导入库并配置全局样式(避免中文乱码、优化图表美观度):
python
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# 全局配置(关键:解决中文显示问题)
plt.rcParams["font.sans-serif"] = ["SimHei"] # 适配Windows中文
# plt.rcParams["font.sans-serif"] = ["PingFang SC"] # 适配Mac中文
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示异常
sns.set_style("whitegrid") # 设置Seaborn默认样式(网格清晰,适合数据分析)
sns.set_palette("Set2") # 设置配色方案(避免默认配色单调)
2. 基础绘图逻辑
Matplotlib采用"画布-子图"层级结构,Seaborn基于Matplotlib封装,核心逻辑一致:
python
# 1. 创建画布和子图(1行1列的子图)
fig, ax = plt.subplots(figsize=(10, 6)) # figsize控制图表尺寸(宽, 高)
# 2. 绘制图表(以简单折线图为例)
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y, label="正弦曲线", color="#2E86AB", linewidth=2)
# 3. 图表美化(标题、标签、图例、网格)
ax.set_title("基础折线图示例", fontsize=14, fontweight="bold")
ax.set_xlabel("X轴", fontsize=12)
ax.set_ylabel("Y轴", fontsize=12)
ax.legend(loc="upper right", fontsize=10)
ax.grid(alpha=0.3) # 网格透明度0.3,避免遮挡数据
# 4. 保存与显示(保存为高清图片,避免模糊)
plt.tight_layout() # 自动调整布局,防止标签被截断
plt.savefig("基础折线图.png", dpi=300, bbox_inches="tight") # dpi=300保证高清
plt.show()
二、特征分布图绘制(数据分析核心)
特征分布是探索性数据分析(EDA)的第一步,用于了解数据的分布形态、异常值、集中趋势,常用图表包括直方图、核密度图、箱线图、小提琴图等。
1. 单特征分布:直方图+核密度图(最常用)
适用于数值型特征(如薪资、年龄、销售额),展示数据分布规律:
python
# 构造示例数据(模拟员工月薪数据)
data = pd.DataFrame({
"月薪": np.random.normal(10000, 2000, 1000) # 正态分布数据
})
# 方法1:Matplotlib绘制直方图+核密度图
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制直方图(bins=组数,density=True归一化)
ax.hist(data["月薪"], bins=30, density=True, alpha=0.7, color="#A23B72", label="直方图")
# 计算核密度曲线
kernel = data["月薪"].plot.kde(ax=ax, color="#F18F01", linewidth=2, label="核密度曲线")
# 美化配置
ax.set_title("员工月薪分布", fontsize=14, fontweight="bold")
ax.set_xlabel("月薪(元)", fontsize=12)
ax.set_ylabel("密度", fontsize=12)
ax.legend(fontsize=10)
ax.axvline(data["月薪"].mean(), color="red", linestyle="--", label="均值") # 标注均值线
ax.legend()
plt.tight_layout()
plt.savefig("月薪分布_直方图+核密度.png", dpi=300)
plt.show()
# 方法2:Seaborn一键绘制(更简洁,自带美化)
plt.figure(figsize=(10, 6))
sns.histplot(data["月薪"], bins=30, kde=True, color="#A23B72", alpha=0.7)
plt.title("员工月薪分布(Seaborn版)", fontsize=14, fontweight="bold")
plt.xlabel("月薪(元)", fontsize=12)
plt.ylabel("频数", fontsize=12)
plt.tight_layout()
plt.savefig("月薪分布_Seaborn版.png", dpi=300)
plt.show()
2. 多特征对比:箱线图/小提琴图
适用于对比不同类别下的特征分布(如不同城市、不同部门的薪资分布):
python
# 构造多类别数据
data = pd.DataFrame({
"城市": np.random.choice(["北京", "上海", "广州", "深圳"], 1000),
"月薪": np.random.normal(10000, 2000, 1000) + np.random.randint(0, 3000, 1000)
})
# 技巧1:箱线图(展示四分位数、异常值)
plt.figure(figsize=(12, 7))
sns.boxplot(x="城市", y="月薪", data=data, palette="Set2")
plt.title("各城市员工月薪分布(箱线图)", fontsize=14, fontweight="bold")
plt.xlabel("城市", fontsize=12)
plt.ylabel("月薪(元)", fontsize=12)
# 标注异常值数量
for i, city in enumerate(data["城市"].unique()):
city_data = data[data["城市"] == city]["月薪"]
q1 = city_data.quantile(0.25)
q3 = city_data.quantile(0.75)
iqr = q3 - q1
outliers = city_data[(city_data < q1-1.5*iqr) | (city_data > q3+1.5*iqr)]
plt.text(i, city_data.min(), f"异常值:{len(outliers)}", ha="center", fontsize=9)
plt.tight_layout()
plt.savefig("各城市月薪_箱线图.png", dpi=300)
plt.show()
# 技巧2:小提琴图(结合箱线图+核密度,展示分布形态)
plt.figure(figsize=(12, 7))
sns.violinplot(x="城市", y="月薪", data=data, palette="Set2", inner="quartile") # inner显示四分位数
plt.title("各城市员工月薪分布(小提琴图)", fontsize=14, fontweight="bold")
plt.xlabel("城市", fontsize=12)
plt.ylabel("月薪(元)", fontsize=12)
plt.tight_layout()
plt.savefig("各城市月薪_小提琴图.png", dpi=300)
plt.show()
3. 分类特征分布:计数图/饼图
适用于离散型特征(如城市、性别、部门),展示类别占比:
python
# 计数图(Seaborn)
plt.figure(figsize=(10, 6))
sns.countplot(x="城市", data=data, palette="Set2", order=data["城市"].value_counts().index)
plt.title("各城市员工数量分布", fontsize=14, fontweight="bold")
plt.xlabel("城市", fontsize=12)
plt.ylabel("人数", fontsize=12)
# 标注数值
for i, v in enumerate(data["城市"].value_counts().values):
plt.text(i, v + 5, str(v), ha="center", fontsize=10)
plt.tight_layout()
plt.savefig("各城市人数_计数图.png", dpi=300)
plt.show()
# 饼图(Matplotlib,适合展示占比)
city_counts = data["城市"].value_counts()
plt.figure(figsize=(8, 8))
plt.pie(
city_counts.values,
labels=city_counts.index,
autopct="%1.1f%%", # 显示百分比
colors=sns.color_palette("Set2"),
explode=[0.05, 0, 0, 0], # 突出第一个类别
shadow=True, # 阴影效果
startangle=90 # 起始角度
)
plt.title("各城市员工占比", fontsize=14, fontweight="bold")
plt.axis("equal") # 保证饼图为正圆形
plt.tight_layout()
plt.savefig("各城市人数_饼图.png", dpi=300)
plt.show()
三、损失曲线绘制(机器学习核心)
损失曲线用于监控模型训练过程,判断模型是否过拟合、欠拟合,是调参的重要依据,核心是绘制训练集/验证集的损失随迭代次数的变化。
1. 基础损失曲线(单模型)
python
# 模拟训练数据(迭代次数、训练损失、验证损失)
epochs = range(1, 51)
train_loss = np.random.randn(50).cumsum() + 10 # 训练损失(逐渐下降)
train_loss = np.sort(train_loss)[::-1] # 反转,模拟损失下降
val_loss = train_loss + np.random.randn(50) * 0.5 # 验证损失(略高于训练损失)
# 绘制损失曲线
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(epochs, train_loss, label="训练损失", color="#E94B3C", linewidth=2, marker=".")
ax.plot(epochs, val_loss, label="验证损失", color="#2E86AB", linewidth=2, marker=".")
# 关键标注:验证损失最小值点
min_val_loss_idx = np.argmin(val_loss)
min_val_loss = val_loss[min_val_loss_idx]
ax.scatter(epochs[min_val_loss_idx], min_val_loss, color="red", s=100, zorder=5) # 标注最小值
ax.annotate(
f"最小值:{min_val_loss:.2f}\n迭代次数:{epochs[min_val_loss_idx]}",
xy=(epochs[min_val_loss_idx], min_val_loss),
xytext=(epochs[min_val_loss_idx]+5, min_val_loss+1),
arrowprops=dict(arrowstyle="->", color="black")
)
# 美化配置
ax.set_title("模型训练损失曲线", fontsize=14, fontweight="bold")
ax.set_xlabel("迭代次数(Epoch)", fontsize=12)
ax.set_ylabel("损失值(Loss)", fontsize=12)
ax.legend(fontsize=10)
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("单模型损失曲线.png", dpi=300)
plt.show()
2. 多模型损失对比(调参/对比实验)
适用于对比不同超参数、不同模型的训练效果:
python
# 模拟3个模型的损失数据
model1_train = np.sort(np.random.randn(50).cumsum() + 10)[::-1]
model1_val = model1_train + np.random.randn(50) * 0.5
model2_train = np.sort(np.random.randn(50).cumsum() + 9)[::-1]
model2_val = model2_train + np.random.randn(50) * 0.4
model3_train = np.sort(np.random.randn(50).cumsum() + 11)[::-1]
model3_val = model3_train + np.random.randn(50) * 0.6
# 绘制多模型对比曲线
fig, ax = plt.subplots(figsize=(12, 7))
ax.plot(epochs, model1_train, label="模型1-训练", color="#E94B3C", linewidth=2, linestyle="-")
ax.plot(epochs, model1_val, label="模型1-验证", color="#E94B3C", linewidth=2, linestyle="--")
ax.plot(epochs, model2_train, label="模型2-训练", color="#2E86AB", linewidth=2, linestyle="-")
ax.plot(epochs, model2_val, label="模型2-验证", color="#2E86AB", linewidth=2, linestyle="--")
ax.plot(epochs, model3_train, label="模型3-训练", color="#F18F01", linewidth=2, linestyle="-")
ax.plot(epochs, model3_val, label="模型3-验证", color="#F18F01", linewidth=2, linestyle="--")
# 美化配置
ax.set_title("多模型损失曲线对比", fontsize=14, fontweight="bold")
ax.set_xlabel("迭代次数(Epoch)", fontsize=12)
ax.set_ylabel("损失值(Loss)", fontsize=12)
ax.legend(fontsize=9, ncol=2) # 图例分2列,避免遮挡
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("多模型损失曲线对比.png", dpi=300)
plt.show()
四、高频拓展可视化(业务实战)
1. 相关性热力图(特征关系分析)
适用于分析数值型特征间的线性关系,是特征选择的重要工具:
python
# 构造多特征数据
data = pd.DataFrame({
"年龄": np.random.randint(20, 50, 1000),
"月薪": np.random.normal(10000, 2000, 1000) + np.random.randint(0, 3000, 1000),
"工作年限": np.random.randint(1, 30, 1000),
"绩效得分": np.random.normal(80, 10, 1000)
})
# 计算相关性矩阵
corr_matrix = data.corr()
# 绘制热力图
plt.figure(figsize=(9, 7))
sns.heatmap(
corr_matrix,
annot=True, # 显示相关系数数值
cmap="coolwarm", # 配色(红=正相关,蓝=负相关)
fmt=".2f", # 数值保留2位小数
linewidths=0.5, # 格子边框宽度
vmin=-1, vmax=1 # 数值范围(-1到1)
)
plt.title("特征相关性热力图", fontsize=14, fontweight="bold")
plt.tight_layout()
plt.savefig("特征相关性热力图.png", dpi=300)
plt.show()
2. 时间序列图(趋势分析)
适用于销售数据、用户增长、指标监控等时序场景:
python
# 构造时序数据
dates = pd.date_range(start="2024-01-01", end="2024-12-31", freq="D")
sales = np.random.randn(366).cumsum() + 1000 # 模拟销售额趋势
sales_data = pd.DataFrame({"日期": dates, "销售额": sales})
# 绘制时序图
plt.figure(figsize=(15, 7))
sns.lineplot(x="日期", y="销售额", data=sales_data, color="#A23B72", linewidth=2)
plt.title("2024年每日销售额趋势", fontsize=14, fontweight="bold")
plt.xlabel("日期", fontsize=12)
plt.ylabel("销售额(元)", fontsize=12)
# 按月标注(避免x轴标签拥挤)
plt.xticks(pd.date_range(start="2024-01-01", end="2024-12-31", freq="MS"), rotation=45)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("销售额时序图.png", dpi=300)
plt.show()
五、可视化优化技巧(避坑指南)
1. 避免常见问题
- 中文乱码 :必须配置
plt.rcParams["font.sans-serif"]和plt.rcParams["axes.unicode_minus"]; - 图表模糊 :保存时设置
dpi=300,避免默认dpi=100; - 标签截断 :使用
plt.tight_layout()或bbox_inches="tight"; - 配色混乱:优先使用Seaborn预设配色(如Set2、coolwarm),避免高对比度配色。
2. 批量绘制子图
适用于一次性展示多个特征分布:
python
# 批量绘制4个子图(2行2列)
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
axes = axes.flatten() # 展平轴数组,方便遍历
# 子图1:月薪直方图
axes[0].hist(data["月薪"], bins=30, alpha=0.7, color="#A23B72")
axes[0].set_title("月薪分布", fontsize=12, fontweight="bold")
# 子图2:年龄箱线图
sns.boxplot(y="年龄", data=data, ax=axes[1], color="#F18F01")
axes[1].set_title("年龄分布", fontsize=12, fontweight="bold")
# 子图3:工作年限计数图
sns.countplot(x="工作年限", data=data, ax=axes[2], palette="Set2")
axes[2].set_title("工作年限分布", fontsize=12, fontweight="bold")
axes[2].tick_params(axis="x", rotation=45) # x轴标签旋转
# 子图4:绩效得分核密度图
sns.kdeplot(data["绩效得分"], ax=axes[3], color="#2E86AB")
axes[3].set_title("绩效得分分布", fontsize=12, fontweight="bold")
# 全局标题
fig.suptitle("员工数据多特征分布", fontsize=16, fontweight="bold", y=0.98)
plt.tight_layout()
plt.savefig("多特征分布子图.png", dpi=300)
plt.show()