掌握Matplotlib和Seaborn高级技巧,创建专业级数据可视化图表
Python数据可视化简介
数据可视化是数据分析中至关重要的一环,它通过图形化方式帮助人们理解和解释复杂数据。Python作为数据科学的首选语言,提供了多个强大的可视化库,其中Matplotlib和Seaborn是最受欢迎的两个库。
Matplotlib是Python中最基础、最广泛使用的绘图库,提供了类似MATLAB的绘图接口。Seaborn则是在Matplotlib基础上进行了高级封装,提供了更美观的默认样式和更简洁的API,特别适合统计可视化。
为什么选择Matplotlib和Seaborn?
- 功能全面 – 支持几乎所有类型的图表
- 高度可定制 – 可以调整每个图表元素的样式
- 与Pandas完美集成 – 直接处理DataFrame和Series数据
- 活跃的社区 – 丰富的文档和示例
- 免费开源 – 无需支付任何费用
环境配置与安装
安装必要的库
使用pip安装数据可视化所需的库:
# 安装核心库
pip install matplotlib seaborn pandas numpy
# 安装Jupyter Notebook(可选,但推荐用于交互式可视化)
pip install jupyterlab
# 安装额外样式库(可选)
pip install cycler
导入库与设置
在Python脚本或Jupyter Notebook中导入所需库:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# 设置Seaborn样式
sns.set_style("whitegrid")
# 设置中文字体支持(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 设置图表大小和DPI
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['figure.dpi'] = 100
# 在Jupyter Notebook中显示图表
%matplotlib inline
Matplotlib基础与核心概念
创建第一个图表
# 创建简单折线图
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]
plt.plot(x, y)
plt.title('简单折线图')
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.show()
多种图表类型
# 创建数据
data = [25, 30, 20, 15, 10]
labels = ['A', 'B', 'C', 'D', 'E']
# 创建子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 折线图
axes[0, 0].plot(labels, data, marker='o', color='blue')
axes[0, 0].set_title('折线图')
# 柱状图
axes[0, 1].bar(labels, data, color='green')
axes[0, 1].set_title('柱状图')
# 饼图
axes[1, 0].pie(data, labels=labels, autopct='%1.1f%%')
axes[1, 0].set_title('饼图')
# 散点图
axes[1, 1].scatter(range(len(data)), data, s=100, color='red')
axes[1, 1].set_title('散点图')
plt.tight_layout()
plt.show()
自定义图表样式
# 创建更专业的图表
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.figure(figsize=(12, 6))
# 绘制两条线
plt.plot(x, y1, label='sin(x)', linewidth=2, linestyle='-', color='blue')
plt.plot(x, y2, label='cos(x)', linewidth=2, linestyle='--', color='red')
# 添加标题和标签
plt.title('三角函数图像', fontsize=16, fontweight='bold')
plt.xlabel('X值', fontsize=12)
plt.ylabel('Y值', fontsize=12)
# 添加网格和图例
plt.grid(True, linestyle=':', alpha=0.7)
plt.legend(loc='best', fontsize=12)
# 设置坐标轴范围
plt.xlim(0, 10)
plt.ylim(-1.2, 1.2)
# 添加注释
plt.annotate('最大值', xy=(np.pi/2, 1), xytext=(np.pi/2+1, 0.8),
arrowprops=dict(facecolor='black', shrink=0.05))
plt.tight_layout()
plt.show()
Seaborn高级可视化
Seaborn基于Matplotlib,提供了更高级的API和更美观的默认样式,特别适合统计数据分析。
使用内置数据集
# 加载Seaborn内置数据集
tips = sns.load_dataset('tips')
iris = sns.load_dataset('iris')
print("Tips数据集前5行:")
print(tips.head())
print("nIris数据集前5行:")
print(iris.head())
分类数据可视化
# 创建多个子图
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 箱线图
sns.boxplot(x='day', y='total_bill', data=tips, ax=axes[0, 0])
axes[0, 0].set_title('每天消费金额箱线图')
# 小提琴图
sns.violinplot(x='day', y='total_bill', hue='sex', data=tips, ax=axes[0, 1])
axes[0, 1].set_title('按性别分类的小提琴图')
# 柱状图
sns.barplot(x='day', y='total_bill', hue='sex', data=tips, ax=axes[1, 0])
axes[1, 0].set_title('按性别分类的柱状图')
# 点图
sns.pointplot(x='day', y='total_bill', hue='sex', data=tips, ax=axes[1, 1])
axes[1, 1].set_title('按性别分类的点图')
plt.tight_layout()
plt.show()
分布可视化
# 创建分布图
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 直方图
sns.histplot(tips['total_bill'], kde=True, ax=axes[0, 0])
axes[0, 0].set_title('消费金额分布直方图')
# 核密度估计图
sns.kdeplot(data=tips, x='total_bill', hue='time', ax=axes[0, 1])
axes[0, 1].set_title('不同时间的消费金额分布')
# 散点图与回归线
sns.regplot(x='total_bill', y='tip', data=tips, ax=axes[1, 0])
axes[1, 0].set_title('消费金额与小费关系图')
# 热力图
corr = tips.corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', ax=axes[1, 1])
axes[1, 1].set_title('数值变量相关性热力图')
plt.tight_layout()
plt.show()
高级可视化技巧
多子图与复杂布局
# 创建复杂布局
fig = plt.figure(figsize=(16, 12))
# 使用GridSpec创建复杂布局
gs = fig.add_gridspec(3, 3)
# 主图 - 散点图
ax1 = fig.add_subplot(gs[0:2, 0:2])
sns.scatterplot(x='total_bill', y='tip', hue='day', size='size', data=tips, ax=ax1)
ax1.set_title('消费金额与小费关系(按天和人数)')
# 右上角 - 箱线图
ax2 = fig.add_subplot(gs[0, 2])
sns.boxplot(y='total_bill', data=tips, ax=ax2)
ax2.set_title('消费金额分布')
# 右下角 - 饼图(需要转换为Matplotlib)
ax3 = fig.add_subplot(gs[1, 2])
day_counts = tips['day'].value_counts()
ax3.pie(day_counts.values, labels=day_counts.index, autopct='%1.1f%%')
ax3.set_title('各天消费比例')
# 底部 - 柱状图
ax4 = fig.add_subplot(gs[2, 0:])
sns.countplot(x='day', hue='sex', data=tips, ax=ax4)
ax4.set_title('各天不同性别消费次数')
plt.tight_layout()
plt.show()
自定义颜色与样式
# 创建自定义颜色方案
custom_palette = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#F9A602", "#9B59B6"]
# 使用自定义调色板
plt.figure(figsize=(12, 6))
sns.set_palette(custom_palette)
# 创建分组柱状图
ax = sns.barplot(x='day', y='total_bill', hue='sex', data=tips,
ci=None, palette=custom_palette)
# 自定义样式
ax.set_title('自定义颜色的分组柱状图', fontsize=16, fontweight='bold')
ax.set_xlabel('星期', fontsize=12)
ax.set_ylabel('平均消费金额', fontsize=12)
# 添加数值标签
for p in ax.patches:
ax.annotate(f'{p.get_height():.2f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', xytext=(0, 10),
textcoords='offset points', fontsize=10)
plt.legend(title='性别')
plt.tight_layout()
plt.show()
交互式可视化
# 使用Plotly创建交互式图表(需要安装plotly)
try:
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# 创建交互式散点图
fig = px.scatter(tips, x='total_bill', y='tip', color='day',
size='size', hover_data=['time', 'sex'],
title='交互式消费数据可视化')
fig.show()
except ImportError:
print("Plotly未安装,请使用 'pip install plotly' 安装")
实战项目:电商销售数据分析可视化
下面我们使用模拟的电商销售数据,创建一个完整的数据可视化项目。
生成模拟数据
# 生成模拟电商数据
np.random.seed(42) # 确保结果可重现
# 创建日期范围
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
# 生成销售数据
n_days = len(dates)
data = {
'date': np.random.choice(dates, 5000),
'product_category': np.random.choice(['电子产品', '服装', '家居', '食品', '图书'], 5000),
'price': np.round(np.random.uniform(10, 500, 5000), 2),
'quantity': np.random.randint(1, 5, 5000),
'customer_age': np.random.randint(18, 70, 5000),
'customer_gender': np.random.choice(['男', '女'], 5000),
'payment_method': np.random.choice(['信用卡', '支付宝', '微信支付', '银行转账'], 5000)
}
# 创建DataFrame
df = pd.DataFrame(data)
df['revenue'] = df['price'] * df['quantity']
# 添加月份和星期几列
df['month'] = df['date'].dt.month
df['day_of_week'] = df['date'].dt.day_name()
print("数据前5行:")
print(df.head())
print("n数据基本信息:")
print(df.info())
print("n描述性统计:")
print(df.describe())
销售趋势分析
# 按月份分析销售趋势
monthly_sales = df.groupby('month').agg({
'revenue': 'sum',
'quantity': 'sum',
'date': 'count'
}).rename(columns={'date': 'transactions'})
# 创建销售趋势图
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 月度收入趋势
axes[0, 0].plot(monthly_sales.index, monthly_sales['revenue'], marker='o', linewidth=2)
axes[0, 0].set_title('月度收入趋势')
axes[0, 0].set_xlabel('月份')
axes[0, 0].set_ylabel('收入')
axes[0, 0].grid(True, linestyle=':', alpha=0.7)
# 月度交易量趋势
axes[0, 1].bar(monthly_sales.index, monthly_sales['transactions'], color='orange')
axes[0, 1].set_title('月度交易量')
axes[0, 1].set_xlabel('月份')
axes[0, 1].set_ylabel('交易次数')
# 产品类别销售额占比
category_revenue = df.groupby('product_category')['revenue'].sum()
axes[1, 0].pie(category_revenue, labels=category_revenue.index, autopct='%1.1f%%')
axes[1, 0].set_title('产品类别销售额占比')
# 支付方式分布
payment_counts = df['payment_method'].value_counts()
axes[1, 1].bar(payment_counts.index, payment_counts.values, color=['blue', 'green', 'red', 'purple'])
axes[1, 1].set_title('支付方式分布')
axes[1, 1].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
客户行为分析
# 客户行为分析
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 客户年龄分布
sns.histplot(df['customer_age'], bins=20, kde=True, ax=axes[0, 0])
axes[0, 0].set_title('客户年龄分布')
axes[0, 0].set_xlabel('年龄')
axes[0, 0].set_ylabel('频次')
# 不同性别消费金额
gender_sales = df.groupby('customer_gender')['revenue'].sum()
axes[0, 1].bar(gender_sales.index, gender_sales.values, color=['pink', 'lightblue'])
axes[0, 1].set_title('不同性别消费总额')
axes[0, 1].set_ylabel('消费金额')
# 不同年龄段的消费偏好
age_bins = [18, 25, 35, 45, 55, 70]
age_labels = ['18-25', '26-35', '36-45', '46-55', '56-70']
df['age_group'] = pd.cut(df['customer_age'], bins=age_bins, labels=age_labels)
age_category = df.groupby(['age_group', 'product_category'])['revenue'].sum().unstack()
age_category.plot(kind='bar', ax=axes[1, 0])
axes[1, 0].set_title('不同年龄段的产品偏好')
axes[1, 0].set_ylabel('消费金额')
axes[1, 0].legend(title='产品类别')
axes[1, 0].tick_params(axis='x', rotation=45)
# 一周中各天的销售情况
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
weekday_sales = df.groupby('day_of_week')['revenue'].sum().reindex(weekday_order)
axes[1, 1].plot(weekday_sales.index, weekday_sales.values, marker='o')
axes[1, 1].set_title('一周中各天的销售情况')
axes[1, 1].set_xlabel('星期')
axes[1, 1].set_ylabel('消费金额')
axes[1, 1].tick_params(axis='x', rotation=45)
axes[1, 1].grid(True, linestyle=':', alpha=0.7)
plt.tight_layout()
plt.show()
高级综合仪表板
# 创建综合仪表板
fig = plt.figure(figsize=(18, 14))
gs = fig.add_gridspec(3, 4)
# 1. 销售趋势图
ax1 = fig.add_subplot(gs[0, 0:2])
daily_sales = df.groupby('date')['revenue'].sum()
ax1.plot(daily_sales.index, daily_sales.values, linewidth=1)
ax1.set_title('每日销售趋势', fontweight='bold')
ax1.set_ylabel('销售额')
# 2. 产品类别销售占比
ax2 = fig.add_subplot(gs[0, 2:])
category_sales = df.groupby('product_category')['revenue'].sum()
ax2.pie(category_sales.values, labels=category_sales.index, autopct='%1.1f%%')
ax2.set_title('产品类别销售占比', fontweight='bold')
# 3. 客户年龄与消费关系
ax3 = fig.add_subplot(gs[1, 0:2])
sns.scatterplot(data=df, x='customer_age', y='revenue', hue='customer_gender', alpha=0.6, ax=ax3)
ax3.set_title('客户年龄与消费金额关系', fontweight='bold')
ax3.set_xlabel('年龄')
ax3.set_ylabel('消费金额')
# 4. 支付方式偏好
ax4 = fig.add_subplot(gs[1, 2:])
payment_by_gender = df.groupby(['payment_method', 'customer_gender']).size().unstack()
payment_by_gender.plot(kind='bar', ax=ax4)
ax4.set_title('不同性别的支付方式偏好', fontweight='bold')
ax4.set_ylabel('交易次数')
ax4.tick_params(axis='x', rotation=45)
# 5. 月度销售热力图
ax5 = fig.add_subplot(gs[2, 0:2])
df['week'] = df['date'].dt.isocalendar().week
heatmap_data = df.groupby(['month', 'week']).agg({'revenue': 'sum'}).unstack()
sns.heatmap(heatmap_data, cmap='YlOrRd', ax=ax5)
ax5.set_title('月度销售热力图', fontweight='bold')
ax5.set_xlabel('周')
ax5.set_ylabel('月')
# 6. 客单价分布
ax6 = fig.add_subplot(gs[2, 2:])
sns.boxplot(data=df, x='product_category', y='price', ax=ax6)
ax6.set_title('不同产品类别的价格分布', fontweight='bold')
ax6.set_ylabel('价格')
ax6.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.suptitle('电商销售数据分析仪表板', fontsize=20, fontweight='bold', y=0.98)
plt.show()