Matplotlib 画图
目录
1. 环境准备与基础设置
# 导入必要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as stats
from scipy import signal
import seaborn as sns
from mpl_toolkits.mplot3d import Axes3D
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 设置默认图表大小
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['figure.dpi'] = 100
# 设置随机种子
np.random.seed(42)
2. 基础图表
2.1 折线图 (Line Plot)
# 创建示例数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.sin(x) * np.exp(-x/10)
# 创建DataFrame
df_line = pd.DataFrame({
'x': x,
'sin(x)': y1,
'cos(x)': y2,
'damped_sin(x)': y3
})
# 绘制折线图
plt.figure(figsize=(12, 6))
# 使用不同的线型和标记
plt.plot(df_line['x'], df_line['sin(x)'], 'b-', label='sin(x)', linewidth=2)
plt.plot(df_line['x'], df_line['cos(x)'], 'r--', label='cos(x)', linewidth=2)
plt.plot(df_line['x'], df_line['damped_sin(x)'], 'g-.', label='阻尼正弦',
linewidth=2, marker='o', markevery=10, markersize=8)
# 添加标题和标签
plt.title('三角函数图像对比', fontsize=16, fontweight='bold')
plt.xlabel('X轴 (弧度)', fontsize=14)
plt.ylabel('Y轴 (幅值)', fontsize=14)
# 添加网格
plt.grid(True, alpha=0.3, linestyle='--')
# 添加图例
plt.legend(loc='upper right', fontsize=12, frameon=True, shadow=True)
# 设置坐标轴范围
plt.xlim(0, 10)
plt.ylim(-1.5, 1.5)
# 自定义刻度
plt.xticks(np.arange(0, 11, 2), ['0', '2π/5', '4π/5', '6π/5', '8π/5', '2π'])
plt.yticks(np.arange(-1.5, 2, 0.5))
# 添加注释
plt.annotate('最大值', xy=(np.pi/2, 1), xytext=(np.pi/2+1, 1.2),
arrowprops=dict(arrowstyle='->', color='black', lw=1.5),
fontsize=12, ha='center')
plt.tight_layout()
plt.show()
2.2 散点图 (Scatter Plot)
# 生成散点数据
n_points = 200
df_scatter = pd.DataFrame({
'x': np.random.randn(n_points),
'y': np.random.randn(n_points),
'size': np.random.randint(20, 200, n_points),
'color': np.random.randint(0, 5, n_points)
})
# 添加相关性
df_scatter['y'] = df_scatter['x'] * 0.5 + np.random.randn(n_points) * 0.5
plt.figure(figsize=(10, 8))
# 创建散点图
scatter = plt.scatter(df_scatter['x'], df_scatter['y'],
c=df_scatter['color'],
s=df_scatter['size'],
alpha=0.6,
cmap='viridis',
edgecolors='black',
linewidth=0.5)
# 添加颜色条
cbar = plt.colorbar(scatter, label='类别')
cbar.set_label('类别', fontsize=12)
# 添加回归线
z = np.polyfit(df_scatter['x'], df_scatter['y'], 1)
p = np.poly1d(z)
plt.plot(df_scatter['x'].sort_values(), p(df_scatter['x'].sort_values()),
"r--", linewidth=2, label=f'回归线: y={z[0]:.2f}x+{z[1]:.2f}')
# 设置标签
plt.title('散点图与回归分析', fontsize=16, pad=20)
plt.xlabel('X变量', fontsize=14)
plt.ylabel('Y变量', fontsize=14)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
2.3 柱状图 (Bar Plot)
# 创建分类数据
categories = ['产品A', '产品B', '产品C', '产品D', '产品E']
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
# 创建数据
data = np.random.randint(50, 150, (len(categories), len(quarters)))
df_bar = pd.DataFrame(data, index=categories, columns=quarters)
# 绘制分组柱状图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 子图1:分组柱状图
x = np.arange(len(categories))
width = 0.2
for i, quarter in enumerate(quarters):
ax1.bar(x + i*width, df_bar[quarter], width, label=quarter)
ax1.set_xlabel('产品类别', fontsize=12)
ax1.set_ylabel('销售额 (万元)', fontsize=12)
ax1.set_title('2024年各季度产品销售额对比', fontsize=14)
ax1.set_xticks(x + width * 1.5)
ax1.set_xticklabels(categories)
ax1.legend()
ax1.grid(True, axis='y', alpha=0.3)
# 添加数值标签
for i, quarter in enumerate(quarters):
for j, val in enumerate(df_bar[quarter]):
ax1.text(j + i*width, val + 1, str(val), ha='center', va='bottom')
# 子图2:堆叠柱状图
df_bar.T.plot(kind='bar', stacked=True, ax=ax2, width=0.6)
ax2.set_xlabel('季度', fontsize=12)
ax2.set_ylabel('累计销售额 (万元)', fontsize=12)
ax2.set_title('2024年季度累计销售额', fontsize=14)
ax2.set_xticklabels(quarters, rotation=0)
ax2.legend(title='产品', bbox_to_anchor=(1.05, 1), loc='upper left')
ax2.grid(True, axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
3. 统计图表
3.1 直方图与概率密度 (Histogram & KDE)
# 生成多个分布的数据
normal_data = np.random.normal(100, 15, 1000)
skewed_data = np.random.gamma(2, 2, 1000) * 10 + 50
bimodal_data = np.concatenate([np.random.normal(80, 10, 500),
np.random.normal(120, 10, 500)])
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 子图1:基础直方图
axes[0, 0].hist(normal_data, bins=30, density=True, alpha=0.7,
color='skyblue', edgecolor='black')
axes[0, 0].set_title('正态分布直方图', fontsize=14)
axes[0, 0].set_xlabel('数值', fontsize=12)
axes[0, 0].set_ylabel('概率密度', fontsize=12)
# 添加正态分布拟合曲线
mu, std = stats.norm.fit(normal_data)
xmin, xmax = axes[0, 0].get_xlim()
x = np.linspace(xmin, xmax, 100)
p = stats.norm.pdf(x, mu, std)
axes[0, 0].plot(x, p, 'r-', linewidth=2, label=f'正态拟合\nμ={mu:.1f}, σ={std:.1f}')
axes[0, 0].legend()
# 子图2:多个分布对比
axes[0, 1].hist([normal_data, skewed_data, bimodal_data], bins=30,
label=['正态分布', '偏态分布', '双峰分布'],
alpha=0.6, density=True)
axes[0, 1].set_title('多分布对比', fontsize=14)
axes[0, 1].set_xlabel('数值', fontsize=12)
axes[0, 1].set_ylabel('概率密度', fontsize=12)
axes[0, 1].legend()
# 子图3:2D直方图
x = np.random.randn(1000)
y = 2 * x + np.random.randn(1000)
h = axes[1, 0].hist2d(x, y, bins=30, cmap='Blues')
axes[1, 0].set_title('2D直方图(热力图)', fontsize=14)
axes[1, 0].set_xlabel('X变量', fontsize=12)
axes[1, 0].set_ylabel('Y变量', fontsize=12)
plt.colorbar(h[3], ax=axes[1, 0], label='频数')
# 子图4:累积分布函数
axes[1, 1].hist(normal_data, bins=50, density=True, cumulative=True,
alpha=0.7, color='green', label='经验CDF')
axes[1, 1].plot(x, stats.norm.cdf(x, mu, std), 'r-', linewidth=2, label='理论CDF')
axes[1, 1].set_title('累积分布函数', fontsize=14)
axes[1, 1].set_xlabel('数值', fontsize=12)
axes[1, 1].set_ylabel('累积概率', fontsize=12)
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
3.2 箱线图 (Box Plot)
# 创建分组数据
df_box = pd.DataFrame({
'部门A': np.random.normal(100, 20, 100),
'部门B': np.random.normal(110, 25, 100),
'部门C': np.random.normal(95, 15, 100),
'部门D': np.random.normal(105, 30, 100),
})
# 添加一些异常值
df_box.loc[95:99, '部门A'] = np.random.uniform(150, 180, 5)
df_box.loc[95:99, '部门D'] = np.random.uniform(20, 40, 5)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 子图1:基础箱线图
box_plot = ax1.boxplot(df_box.values, labels=df_box.columns,
patch_artist=True, notch=True, showmeans=True)
# 自定义颜色
colors = ['lightblue', 'lightgreen', 'pink', 'lightyellow']
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.7)
ax1.set_title('各部门绩效分数分布', fontsize=14)
ax1.set_xlabel('部门', fontsize=12)
ax1.set_ylabel('绩效分数', fontsize=12)
ax1.grid(True, axis='y', alpha=0.3)
# 添加统计信息
for i, col in enumerate(df_box.columns):
median = df_box[col].median()
ax1.text(i+1, median, f'{median:.1f}', ha='center', va='bottom', fontweight='bold')
# 子图2:小提琴图
parts = ax2.violinplot(df_box.values, positions=range(1, 5),
showmeans=True, showmedians=True, showextrema=True)
# 自定义小提琴图颜色
for i, pc in enumerate(parts['bodies']):
pc.set_facecolor(colors[i])
pc.set_alpha(0.7)
ax2.set_title('各部门绩效分数分布(小提琴图)', fontsize=14)
ax2.set_xlabel('部门', fontsize=12)
ax2.set_ylabel('绩效分数', fontsize=12)
ax2.set_xticks(range(1, 5))
ax2.set_xticklabels(df_box.columns)
ax2.grid(True, axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
3.3 热力图 (Heatmap)
# 创建相关性矩阵数据
n_vars = 8
df_corr = pd.DataFrame(np.random.randn(100, n_vars),
columns=[f'变量{i+1}' for i in range(n_vars)])
# 添加一些相关性
df_corr['变量2'] = df_corr['变量1'] * 0.8 + np.random.randn(100) * 0.3
df_corr['变量4'] = df_corr['变量3'] * -0.7 + np.random.randn(100) * 0.4
df_corr['变量6'] = df_corr['变量5'] * 0.6 + np.random.randn(100) * 0.5
# 计算相关系数矩阵
correlation_matrix = df_corr.corr()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
# 子图1:基础热力图
im1 = ax1.imshow(correlation_matrix, cmap='coolwarm', aspect='auto', vmin=-1, vmax=1)
ax1.set_xticks(np.arange(n_vars))
ax1.set_yticks(np.arange(n_vars))
ax1.set_xticklabels(correlation_matrix.columns, rotation=45, ha='right')
ax1.set_yticklabels(correlation_matrix.columns)
# 添加数值标签
for i in range(n_vars):
for j in range(n_vars):
text = ax1.text(j, i, f'{correlation_matrix.iloc[i, j]:.2f}',
ha='center', va='center', color='black' if abs(correlation_matrix.iloc[i, j]) < 0.5 else 'white')
ax1.set_title('变量相关性热力图', fontsize=14)
plt.colorbar(im1, ax=ax1, label='相关系数')
# 子图2:带聚类的热力图
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial.distance import squareform
# 计算距离矩阵和聚类
distances = 1 - np.abs(correlation_matrix)
condensed_distances = squareform(distances)
linkage_matrix = linkage(condensed_distances, method='average')
# 获取聚类顺序
dendrogram_result = dendrogram(linkage_matrix, no_plot=True)
cluster_order = dendrogram_result['leaves']
# 重新排序相关性矩阵
clustered_corr = correlation_matrix.iloc[cluster_order, cluster_order]
im2 = ax2.imshow(clustered_corr, cmap='coolwarm', aspect='auto', vmin=-1, vmax=1)
ax2.set_xticks(np.arange(n_vars))
ax2.set_yticks(np.arange(n_vars))
ax2.set_xticklabels(clustered_corr.columns, rotation=45, ha='right')
ax2.set_yticklabels(clustered_corr.columns)
ax2.set_title('聚类后的相关性热力图', fontsize=14)
plt.colorbar(im2, ax=ax2, label='相关系数')
plt.tight_layout()
plt.show()
4. 高级图表
4.1 饼图与环形图 (Pie & Donut Chart)
# 创建数据
categories = ['技术部', '销售部', '市场部', '人力资源', '财务部', '其他']
sizes = [35, 25, 20, 10, 7, 3]
explode = (0.1, 0, 0, 0, 0, 0) # 突出显示第一块
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
# 子图1:饼图
colors = plt.cm.Set3(np.linspace(0, 1, len(categories)))
wedges, texts, autotexts = ax1.pie(sizes, explode=explode, labels=categories,
colors=colors, autopct='%1.1f%%',
shadow=True, startangle=90)
# 美化文字
for text in texts:
text.set_fontsize(12)
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontsize(10)
autotext.set_weight('bold')
ax1.set_title('部门人员分布', fontsize=16, pad=20)
# 子图2:环形图
# 创建多层数据
size_inner = [sum(sizes[:3]), sum(sizes[3:])]
size_outer = sizes
# 绘制外圈
wedges_outer, texts_outer, autotexts_outer = ax2.pie(size_outer,
radius=1,
labels=categories,
colors=colors,
autopct='%1.1f%%',
pctdistance=0.85,
wedgeprops=dict(width=0.5))
# 绘制内圈
colors_inner = ['lightblue', 'lightcoral']
wedges_inner, texts_inner, autotexts_inner = ax2.pie(size_inner,
radius=0.5,
labels=['核心部门', '支持部门'],
colors=colors_inner,
autopct='%1.1f%%',
pctdistance=0.5,
wedgeprops=dict(width=0.3))
ax2.set_title('部门分类统计', fontsize=16, pad=20)
# 添加中心文字
ax2.text(0, 0, '总人数\n1000', ha='center', va='center', fontsize=14, weight='bold')
plt.tight_layout()
plt.show()
4.2 雷达图 (Radar Chart)
# 创建数据
categories = ['技术能力', '沟通技巧', '项目管理', '创新思维', '团队协作', '领导力']
N = len(categories)
# 创建多个员工的数据
employees = {
'张三': [90, 85, 88, 92, 85, 78],
'李四': [85, 90, 82, 78, 92, 85],
'王五': [78, 82, 90, 85, 88, 92]
}
# 计算角度
angles = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
angles += angles[:1] # 闭合图形
fig, ax = plt.subplots(figsize=(10, 10), subplot_kw=dict(projection='polar'))
# 绘制每个员工的雷达图
colors = ['blue', 'red', 'green']
for idx, (name, values) in enumerate(employees.items()):
values += values[:1] # 闭合数据
ax.plot(angles, values, 'o-', linewidth=2, label=name, color=colors[idx])
ax.fill(angles, values, alpha=0.15, color=colors[idx])
# 设置标签
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, fontsize=12)
# 设置刻度
ax.set_ylim(0, 100)
ax.set_yticks([20, 40, 60, 80, 100])
ax.set_yticklabels(['20', '40', '60', '80', '100'], fontsize=10)
# 添加网格
ax.grid(True, linestyle='--', alpha=0.7)
# 添加标题和图例
plt.title('员工能力评估雷达图', fontsize=16, pad=30)
plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1.1), fontsize=12)
plt.tight_layout()
plt.show()
4.3 面积图 (Area Plot)
# 创建时间序列数据
dates = pd.date_range('2024-01-01', periods=100, freq='D')
df_area = pd.DataFrame({
'产品A': np.random.randint(20, 50, 100).cumsum(),
'产品B': np.random.randint(15, 40, 100).cumsum(),
'产品C': np.random.randint(10, 30, 100).cumsum(),
'产品D': np.random.randint(5, 20, 100).cumsum()
}, index=dates)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
# 子图1:堆叠面积图
ax1.stackplot(df_area.index, df_area['产品A'], df_area['产品B'],
df_area['产品C'], df_area['产品D'],
labels=['产品A', '产品B', '产品C', '产品D'],
alpha=0.7)
ax1.set_title('2024年产品累计销售额(堆叠面积图)', fontsize=14)
ax1.set_xlabel('日期', fontsize=12)
ax1.set_ylabel('累计销售额(万元)', fontsize=12)
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)
# 格式化x轴日期
import matplotlib.dates as mdates
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax1.xaxis.set_major_locator(mdates.MonthLocator())
# 子图2:百分比堆叠面积图
df_pct = df_area.div(df_area.sum(axis=1), axis=0) * 100
ax2.stackplot(df_pct.index, df_pct['产品A'], df_pct['产品B'],
df_pct['产品C'], df_pct['产品D'],
labels=['产品A', '产品B', '产品C', '产品D'],
alpha=0.7)
ax2.set_title('2024年产品销售占比变化(百分比堆叠面积图)', fontsize=14)
ax2.set_xlabel('日期', fontsize=12)
ax2.set_ylabel('销售占比(%)', fontsize=12)
ax2.set_ylim(0, 100)
ax2.legend(loc='upper left')
ax2.grid(True, alpha=0.3)
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax2.xaxis.set_major_locator(mdates.MonthLocator())
plt.tight_layout()
plt.show()
5. 3D图表
5.1 3D曲面图 (3D Surface Plot)
# 创建网格数据
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
# 定义不同的函数
Z1 = np.sin(np.sqrt(X**2 + Y**2))
Z2 = np.exp(-(X**2 + Y**2)/10) * np.cos(2*X) * np.cos(2*Y)
fig = plt.figure(figsize=(16, 8))
# 子图1:3D曲面图
ax1 = fig.add_subplot(121, projection='3d')
surf1 = ax1.plot_surface(X, Y, Z1, cmap='viridis',
linewidth=0, antialiased=True, alpha=0.8)
ax1.set_title('3D曲面图:$z = \sin(\sqrt{x^2 + y^2})$', fontsize=14)
ax1.set_xlabel('X轴', fontsize=12)
ax1.set_ylabel('Y轴', fontsize=12)
ax1.set_zlabel('Z轴', fontsize=12)
fig.colorbar(surf1, ax=ax1, shrink=0.5, aspect=5)
# 子图2:3D曲面图带等高线
ax2 = fig.add_subplot(122, projection='3d')
surf2 = ax2.plot_surface(X, Y, Z2, cmap='coolwarm',
linewidth=0, antialiased=True, alpha=0.7)
# 添加等高线投影
contours = ax2.contour(X, Y, Z2, zdir='z', offset=-1, cmap='coolwarm', alpha=0.5)
ax2.set_title('3D曲面图带等高线投影', fontsize=14)
ax2.set_xlabel('X轴', fontsize=12)
ax2.set_ylabel('Y轴', fontsize=12)
ax2.set_zlabel('Z轴', fontsize=12)
ax2.set_zlim(-1, 1)
fig.colorbar(surf2, ax=ax2, shrink=0.5, aspect=5)
plt.tight_layout()
plt.show()
5.2 3D散点图 (3D Scatter Plot)
# 生成3D散点数据
n_points = 500
df_3d = pd.DataFrame({
'x': np.random.randn(n_points),
'y': np.random.randn(n_points),
'z': np.random.randn(n_points),
'cluster': np.random.choice(['A', 'B', 'C'], n_points)
})
# 为不同簇添加不同的中心
df_3d.loc[df_3d['cluster'] == 'A', ['x', 'y', 'z']] += [2, 2, 2]
df_3d.loc[df_3d['cluster'] == 'B', ['x', 'y', 'z']] += [-2, -2, 2]
df_3d.loc[df_3d['cluster'] == 'C', ['x', 'y', 'z']] += [0, 2, -2]
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# 为每个簇使用不同的颜色和标记
colors = {'A': 'red', 'B': 'blue', 'C': 'green'}
markers = {'A': 'o', 'B': '^', 'C': 's'}
for cluster in ['A', 'B', 'C']:
cluster_data = df_3d[df_3d['cluster'] == cluster]
ax.scatter(cluster_data['x'], cluster_data['y'], cluster_data['z'],
c=colors[cluster], marker=markers[cluster],
s=50, alpha=0.6, label=f'簇 {cluster}')
# 添加标签和标题
ax.set_xlabel('X维度', fontsize=12)
ax.set_ylabel('Y维度', fontsize=12)
ax.set_zlabel('Z维度', fontsize=12)
ax.set_title('3D聚类散点图', fontsize=16)
ax.legend(fontsize=12)
# 设置视角
ax.view_init(elev=20, azim=45)
plt.tight_layout()
plt.show()
6. 子图与布局
6.1 复杂子图布局
# 创建复杂布局
fig = plt.figure(figsize=(16, 12))
# 使用GridSpec创建自定义布局
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)
# 大图占据左上角2x2
ax_main = fig.add_subplot(gs[:2, :2])
# 右上角两个小图
ax_top_right1 = fig.add_subplot(gs[0, 2])
ax_top_right2 = fig.add_subplot(gs[1, 2])
# 底部三个图
ax_bottom1 = fig.add_subplot(gs[2, 0])
ax_bottom2 = fig.add_subplot(gs[2, 1])
ax_bottom3 = fig.add_subplot(gs[2, 2])
# 主图:时间序列
dates = pd.date_range('2024-01-01', periods=365, freq='D')
ts_data = np.cumsum(np.random.randn(365)) + 100
ax_main.plot(dates, ts_data, 'b-', linewidth=2)
ax_main.fill_between(dates, ts_data, 100, alpha=0.3)
ax_main.set_title('2024年股价走势', fontsize=16)
ax_main.set_xlabel('日期', fontsize=12)
ax_main.set_ylabel('股价', fontsize=12)
ax_main.grid(True, alpha=0.3)
# 右上1:收益分布
returns = np.diff(ts_data) / ts_data[:-1] * 100
ax_top_right1.hist(returns, bins=30, alpha=0.7, color='green')
ax_top_right1.set_title('日收益率分布', fontsize=12)
ax_top_right1.set_xlabel('收益率(%)', fontsize=10)
ax_top_right1.set_ylabel('频数', fontsize=10)
# 右上2:QQ图
stats.probplot(returns, dist="norm", plot=ax_top_right2)
ax_top_right2.set_title('QQ图', fontsize=12)
ax_top_right2.grid(True, alpha=0.3)
# 底部1:自相关图
from statsmodels.graphics.tsaplots import plot_acf
plot_acf(returns, lags=30, ax=ax_bottom1)
ax_bottom1.set_title('自相关函数', fontsize=12)
# 底部2:移动平均
ma_30 = pd.Series(ts_data).rolling(30).mean()
ma_90 = pd.Series(ts_data).rolling(90).mean()
ax_bottom2.plot(dates, ts_data, 'gray', alpha=0.5, label='原始数据')
ax_bottom2.plot(dates, ma_30, 'r-', label='30日均线')
ax_bottom2.plot(dates, ma_90, 'b-', label='90日均线')
ax_bottom2.set_title('移动平均', fontsize=12)
ax_bottom2.legend()
ax_bottom2.grid(True, alpha=0.3)
# 底部3:波动率
volatility = pd.Series(returns).rolling(30).std()
ax_bottom3.plot(volatility, 'purple')
ax_bottom3.fill_between(range(len(volatility)), volatility, alpha=0.3, color='purple')
ax_bottom3.set_title('30日滚动波动率', fontsize=12)
ax_bottom3.set_xlabel('天数', fontsize=10)
ax_bottom3.set_ylabel('波动率(%)', fontsize=10)
ax_bottom3.grid(True, alpha=0.3)
plt.suptitle('股票分析仪表板', fontsize=20, y=0.98)
plt.tight_layout()
plt.show()
6.2 共享轴子图
# 创建共享轴的子图
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
# 生成相关的时间序列数据
time = np.linspace(0, 10, 1000)
signal1 = np.sin(2 * np.pi * 1 * time) + 0.5 * np.sin(2 * np.pi * 3 * time)
signal2 = signal.square(2 * np.pi * 0.5 * time)
signal3 = signal1 + 0.5 * signal2 + 0.2 * np.random.randn(1000)
# 第一个子图:原始信号
axes[0].plot(time, signal1, 'b-', label='正弦波')
axes[0].plot(time, signal2, 'r-', label='方波')
axes[0].set_ylabel('幅值', fontsize=12)
axes[0].set_title('原始信号', fontsize=14)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# 第二个子图:合成信号
axes[1].plot(time, signal3, 'g-', alpha=0.7, label='合成信号+噪声')
axes[1].set_ylabel('幅值', fontsize=12)
axes[1].set_title('合成信号', fontsize=14)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# 第三个子图:频谱分析
from scipy.fft import fft, fftfreq
N = len(time)
yf = fft(signal3)
xf = fftfreq(N, time[1] - time[0])[:N//2]
axes[2].plot(xf, 2.0/N * np.abs(yf[0:N//2]), 'purple')
axes[2].set_ylabel('幅度', fontsize=12)
axes[2].set_xlabel('频率 (Hz)', fontsize=12)
axes[2].set_title('频谱分析', fontsize=14)
axes[2].set_xlim(0, 5)
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
7. 样式与美化
7.1 自定义样式和主题
# 展示不同的样式
styles = ['default', 'seaborn', 'ggplot', 'bmh']
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.ravel()
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
for idx, style in enumerate(styles):
with plt.style.context(style):
axes[idx].plot(x, y1, label='sin(x)')
axes[idx].plot(x, y2, label='cos(x)')
axes[idx].set_title(f'样式: {style}', fontsize=14)
axes[idx].legend()
axes[idx].grid(True)
plt.tight_layout()
plt.show()
7.2 自定义配色和样式
# 创建自定义样式的图表
fig, ax = plt.subplots(figsize=(12, 8))
# 自定义颜色
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57']
# 创建数据
categories = ['类别A', '类别B', '类别C', '类别D', '类别E']
values = [23, 45, 56, 78, 32]
# 创建水平柱状图
bars = ax.barh(categories, values, color=colors, height=0.6)
# 自定义样式
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_color('#CCCCCC')
# 移除y轴刻度
ax.tick_params(axis='y', which='both', left=False)
# 添加数值标签
for i, (bar, value) in enumerate(zip(bars, values)):
ax.text(value + 1, bar.get_y() + bar.get_height()/2,
f'{value}', ha='left', va='center', fontsize=12, fontweight='bold')
# 设置标题和标签
ax.set_title('自定义样式柱状图', fontsize=18, fontweight='bold', pad=20)
ax.set_xlabel('数值', fontsize=14)
# 添加背景网格
ax.grid(True, axis='x', alpha=0.2, linestyle='--')
ax.set_axisbelow(True)
plt.tight_layout()
plt.show()
7.3 注释和标记
# 创建带有丰富注释的图表
fig, ax = plt.subplots(figsize=(14, 8))
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x) * np.exp(-x/10)
# 绘制主线
line, = ax.plot(x, y, 'b-', linewidth=2, label='阻尼振荡')
# 找到极值点
from scipy.signal import find_peaks
peaks, _ = find_peaks(y)
valleys, _ = find_peaks(-y)
# 标记极值点
ax.plot(x[peaks], y[peaks], 'ro', markersize=8, label='极大值')
ax.plot(x[valleys], y[valleys], 'go', markersize=8, label='极小值')
# 添加各种注释
# 1. 简单注释
ax.annotate('起始点', xy=(0, 0), xytext=(0.5, 0.3),
arrowprops=dict(arrowstyle='->', color='black'),
fontsize=12, bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.7))
# 2. 曲线注释
ax.annotate('', xy=(x[peaks[0]], y[peaks[0]]), xytext=(x[peaks[1]], y[peaks[1]]),
arrowprops=dict(arrowstyle='<->', color='red', lw=2,
connectionstyle="arc3,rad=0.3"))
ax.text((x[peaks[0]] + x[peaks[1]])/2, (y[peaks[0]] + y[peaks[1]])/2 + 0.1,
'周期', ha='center', fontsize=12, color='red')
# 3. 添加文本框
textstr = '阻尼振荡方程:\n$y = \sin(x) \cdot e^{-x/10}$'
props = dict(boxstyle='round', facecolor='lightblue', alpha=0.8)
ax.text(0.7, 0.95, textstr, transform=ax.transAxes, fontsize=14,
verticalalignment='top', bbox=props)
# 4. 添加垂直线和水平线
ax.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
ax.axvline(x=np.pi, color='gray', linestyle='--', alpha=0.5)
ax.text(np.pi, -0.1, '$\pi$', ha='center', fontsize=12)
# 5. 填充区域
ax.fill_between(x, 0, y, where=(y > 0), alpha=0.3, color='blue', label='正值区域')
ax.fill_between(x, 0, y, where=(y < 0), alpha=0.3, color='red', label='负值区域')
# 设置标题和标签
ax.set_title('阻尼振荡分析图', fontsize=16, fontweight='bold')
ax.set_xlabel('时间 (秒)', fontsize=14)
ax.set_ylabel('振幅', fontsize=14)
ax.legend(loc='upper right', fontsize=12)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
总结
本教程详细介绍了Matplotlib的各种图表类型和设置方法,包括:
- 基础图表:折线图、散点图、柱状图
- 统计图表:直方图、箱线图、热力图
- 高级图表:饼图、雷达图、面积图
- 3D图表:3D曲面图、3D散点图
- 布局技巧:子图布局、共享轴
- 样式美化:自定义样式、注释标记
关键要点:
- 标签设置:使用
xlabel()
,ylabel()
,title()
设置基本标签 - 图例:使用
legend()
添加图例,可自定义位置和样式 - 刻度:使用
xticks()
,yticks()
自定义刻度标签 - 网格:使用
grid()
添加网格线 - 样式:通过各种参数控制颜色、线型、标记等
- 布局:使用
subplots()
和GridSpec
创建复杂布局