matplotlib,一个神奇的 Python 库!
一、库的简介
Matplotlib是Python数据可视化领域最经典、最强大的库之一,它提供了从简单到复杂的各种图形绘制功能,将枯燥的数据转化为直观的视觉表达。在实际生活中,Matplotlib的应用场景无处不在:科研人员用它绘制实验数据图表,金融分析师用它展示股票趋势,数据分析师用它呈现业务报告,工程师用它可视化系统性能。当你在学术论文中看到精美的图表,在商业报告中看到清晰的趋势分析,或是在技术博客中看到复杂数据的可视化呈现时,背后往往都有Matplotlib的身影。它的设计哲学是"让简单的事情简单,让复杂的事情可能",通过层次化的API设计,既为初学者提供了简单的入门方式,又为专家用户提供了无限的自定义能力。
二、安装库
安装Matplotlib有多种方式,可根据具体需求选择:
python
# 基础安装
pip install matplotlib
# 安装完整版(包含所有可选依赖)
pip install matplotlib[all]
# 使用conda安装
conda install matplotlib
# 验证安装
import matplotlib
print(f"Matplotlib版本: {matplotlib.__version__}")
# 查看后端配置
print(f"当前后端: {matplotlib.get_backend()}")
# 安装可选依赖以支持更多功能
pip install pillow # 支持更多图像格式
pip install pandas # 与pandas更好集成
pip install seaborn # 更美观的统计图表
对于需要生成出版质量图表的用户,建议安装LaTeX支持:
bash
# Ubuntu/Debian sudo apt-get install texlive-latex-extra dvipng # macOS brew install mactex # Windows(通过MiKTeX或TeX Live)
三、基本用法
1. 创建基本图表
python
import matplotlib.pyplot as plt
import numpy as np
# 设置中文字体和符号显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制折线图
ax.plot(x, y, label='正弦函数', color='blue', linewidth=2)
# 设置图表元素
ax.set_xlabel('X轴', fontsize=12)
ax.set_ylabel('Y轴', fontsize=12)
ax.set_title('基本正弦函数图', fontsize=14, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
# 添加注释
ax.annotate('最大值', xy=(np.pi/2, 1), xytext=(np.pi/2 + 1, 0.8),
arrowprops=dict(arrowstyle='->', color='red'))
# 显示图表
plt.tight_layout()
plt.show()
# 保存图表
fig.savefig('sine_wave.png', dpi=300, bbox_inches='tight')
2. 多子图和多种图表类型
python
# 创建2x2的子图布局
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 准备数据
x = np.arange(0, 10, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.exp(-x/5) * np.sin(x)
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 33]
# 子图1:双曲线图
axes[0, 0].plot(x, y1, 'b-', label='sin(x)')
axes[0, 0].plot(x, y2, 'r--', label='cos(x)')
axes[0, 0].set_title('三角函数比较')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 子图2:散点图
np.random.seed(42)
x_scatter = np.random.randn(100)
y_scatter = np.random.randn(100)
colors = np.random.rand(100)
sizes = 1000 * np.random.rand(100)
axes[0, 1].scatter(x_scatter, y_scatter, c=colors, s=sizes, alpha=0.6, cmap='viridis')
axes[0, 1].set_title('散点图(带颜色和大小)')
axes[0, 1].set_xlabel('X')
axes[0, 1].set_ylabel('Y')
# 子图3:柱状图
axes[1, 0].bar(categories, values, color=['skyblue', 'lightgreen', 'lightcoral', 'gold', 'violet'])
axes[1, 0].set_title('分类数据柱状图')
axes[1, 0].set_xlabel('类别')
axes[1, 0].set_ylabel('数值')
# 添加数值标签
for i, v in enumerate(values):
axes[1, 0].text(i, v + 1, str(v), ha='center')
# 子图4:填充图
axes[1, 1].fill_between(x, y3, 0, where=(y3 > 0), color='green', alpha=0.5, label='正区域')
axes[1, 1].fill_between(x, y3, 0, where=(y3 <= 0), color='red', alpha=0.5, label='负区域')
axes[1, 1].plot(x, y3, 'k-', linewidth=1)
axes[1, 1].set_title('填充区域图')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle('多种图表类型展示', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
3. 样式定制和美化
python
# 创建更美观的图表
plt.style.use('seaborn-v0_8-darkgrid')
# 创建数据
x = np.linspace(0, 10, 200)
functions = {
'线性函数': 2 * x + 1,
'二次函数': 0.5 * x**2,
'指数函数': np.exp(x/3),
'对数函数': np.log(x + 1),
'正弦函数': 5 * np.sin(x)
}
# 创建图形
fig, ax = plt.subplots(figsize=(12, 8))
# 绘制多条曲线
colors = plt.cm.Set2(np.linspace(0, 1, len(functions)))
line_styles = ['-', '--', '-.', ':', '-']
markers = ['o', 's', '^', 'D', 'v']
for (name, y), color, ls, marker in zip(functions.items(), colors, line_styles, markers):
ax.plot(x, y, label=name, color=color, linestyle=ls,
linewidth=2, marker=marker, markersize=4, markevery=10)
# 自定义图表元素
ax.set_xlabel('自变量 X', fontsize=12, fontweight='bold')
ax.set_ylabel('函数值 Y', fontsize=12, fontweight='bold')
ax.set_title('多种数学函数可视化', fontsize=16, fontweight='bold', pad=20)
# 设置图例
ax.legend(loc='upper left', fontsize=10, frameon=True,
fancybox=True, shadow=True, borderpad=1)
# 设置坐标轴
ax.set_xlim([0, 10])
ax.set_ylim([-2, 25])
ax.tick_params(axis='both', which='major', labelsize=10)
# 添加网格
ax.grid(True, which='both', linestyle='--', linewidth=0.5, alpha=0.7)
# 添加文本注释
ax.text(2, 20, '指数增长最快', fontsize=10, style='italic',
bbox=dict(boxstyle='round,pad=0.5', facecolor='yellow', alpha=0.5))
# 添加箭头
ax.annotate('最小值', xy=(0, 0), xytext=(1, 5),
arrowprops=dict(facecolor='black', shrink=0.05, width=1.5, headwidth=8),
fontsize=10)
plt.tight_layout()
plt.show()
4. 交互式功能
python
from matplotlib.widgets import Slider, Button, RadioButtons
import matplotlib.pyplot as plt
import numpy as np
# 创建初始数据
fig, ax = plt.subplots(figsize=(10, 7))
plt.subplots_adjust(left=0.25, bottom=0.35)
t = np.arange(0.0, 10.0, 0.01)
amplitude = 5.0
frequency = 1.0
phase = 0.0
s = amplitude * np.sin(2 * np.pi * frequency * t + phase)
line, = ax.plot(t, s, lw=2, color='red')
ax.set_xlabel('时间 (s)', fontsize=12)
ax.set_ylabel('振幅', fontsize=12)
ax.set_title('交互式正弦波', fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3)
ax.set_xlim([0, 10])
ax.set_ylim([-10, 10])
# 创建滑动条位置
axcolor = 'lightgoldenrodyellow'
ax_amp = plt.axes([0.25, 0.2, 0.65, 0.03], facecolor=axcolor)
ax_freq = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
ax_phase = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
# 创建滑动条
samp = Slider(ax_amp, '振幅', 0.1, 10.0, valinit=amplitude)
sfreq = Slider(ax_freq, '频率', 0.1, 5.0, valinit=frequency)
sphase = Slider(ax_phase, '相位', 0.0, 2*np.pi, valinit=phase)
# 更新函数
def update(val):
amp = samp.val
freq = sfreq.val
phase = sphase.val
line.set_ydata(amp * np.sin(2 * np.pi * freq * t + phase))
fig.canvas.draw_idle()
# 注册更新函数
samp.on_changed(update)
sfreq.on_changed(update)
sphase.on_changed(update)
# 重置按钮
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, '重置', color=axcolor, hovercolor='0.975')
def reset(event):
samp.reset()
sfreq.reset()
sphase.reset()
button.on_clicked(reset)
# 颜色选择
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('红色', '蓝色', '绿色'), active=0)
def colorfunc(label):
line.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
四、高级用法
1. 3D可视化
python
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
# 创建3D图形
fig = plt.figure(figsize=(14, 10))
# 子图1:3D曲面图
ax1 = fig.add_subplot(221, projection='3d')
# 创建网格数据
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# 绘制曲面
surf = ax1.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=True, alpha=0.8)
# 添加颜色条
fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=5)
ax1.set_title('3D曲面图: z = sin(√(x²+y²))', fontsize=12, fontweight='bold')
ax1.set_xlabel('X轴')
ax1.set_ylabel('Y轴')
ax1.set_zlabel('Z轴')
# 子图2:3D散点图
ax2 = fig.add_subplot(222, projection='3d')
np.random.seed(42)
n_points = 200
x = np.random.randn(n_points)
y = np.random.randn(n_points)
z = np.random.randn(n_points)
colors = np.sqrt(x**2 + y**2 + z**2)
scatter = ax2.scatter(x, y, z, c=colors, cmap='viridis',
s=50, alpha=0.7, edgecolors='w', linewidth=0.5)
ax2.set_title('3D散点图', fontsize=12, fontweight='bold')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax2.set_zlabel('Z')
# 子图3:3D柱状图
ax3 = fig.add_subplot(223, projection='3d')
_x = np.arange(4)
_y = np.arange(5)
_xx, _yy = np.meshgrid(_x, _yy)
x, y = _xx.ravel(), _yy.ravel()
top = np.random.rand(20)
bottom = np.zeros_like(top)
width = depth = 0.8
ax3.bar3d(x, y, bottom, width, depth, top,
shade=True, color=plt.cm.tab20c.colors)
ax3.set_title('3D柱状图', fontsize=12, fontweight='bold')
ax3.set_xlabel('X类别')
ax3.set_ylabel('Y类别')
ax3.set_zlabel('值')
# 子图4:3D线框图
ax4 = fig.add_subplot(224, projection='3d')
# 创建螺旋线数据
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax4.plot(x, y, z, label='螺旋线', linewidth=2, color='purple')
ax4.set_title('3D线图', fontsize=12, fontweight='bold')
ax4.set_xlabel('X')
ax4.set_ylabel('Y')
ax4.set_zlabel('Z')
ax4.legend()
plt.suptitle('3D可视化集合', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
2. 动画制作
python
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# 创建图形和坐标轴
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 准备数据
x = np.linspace(0, 4*np.pi, 200)
line1, = ax1.plot([], [], 'r-', linewidth=2)
line2, = ax2.plot([], [], 'b-', linewidth=2)
# 设置坐标轴
ax1.set_xlim(0, 4*np.pi)
ax1.set_ylim(-1.5, 1.5)
ax1.set_title('正弦波传播', fontsize=12, fontweight='bold')
ax1.set_xlabel('位置')
ax1.set_ylabel('振幅')
ax1.grid(True, alpha=0.3)
ax2.set_xlim(0, 4*np.pi)
ax2.set_ylim(-1.5, 1.5)
ax2.set_title('驻波形成', fontsize=12, fontweight='bold')
ax2.set_xlabel('位置')
ax2.set_ylabel('振幅')
ax2.grid(True, alpha=0.3)
# 初始化函数
def init():
line1.set_data([], [])
line2.set_data([], [])
return line1, line2
# 动画更新函数
def update(frame):
# 第一个图:行波
k = 1.0 # 波数
omega = 0.5 # 角频率
t = frame * 0.1
# 行波方程
y1 = np.sin(k * x - omega * t)
line1.set_data(x, y1)
# 第二个图:驻波
# 两个相反方向传播的波叠加
y2_forward = np.sin(k * x - omega * t)
y2_backward = np.sin(k * x + omega * t)
y2 = y2_forward + y2_backward
line2.set_data(x, y2)
# 添加时间文本
time_text = f'时间: {t:.1f}s'
ax1.text(0.02, 0.95, time_text, transform=ax1.transAxes, fontsize=10,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
return line1, line2
# 创建动画
ani = animation.FuncAnimation(fig, update, frames=100,
init_func=init, blit=True, interval=50)
# 保存动画(需要ffmpeg)
# ani.save('wave_animation.mp4', writer='ffmpeg', fps=20)
plt.tight_layout()
plt.show()
3. 自定义绘图元素
python
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.path import Path
import numpy as np
# 创建图形
fig, ax = plt.subplots(figsize=(12, 8))
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.set_title('自定义图形元素展示', fontsize=16, fontweight='bold')
# 1. 基本形状
# 矩形
rect = patches.Rectangle((1, 1), 2, 1, linewidth=2,
edgecolor='blue', facecolor='lightblue', alpha=0.7)
ax.add_patch(rect)
ax.text(2, 1.5, '矩形', ha='center', va='center', fontsize=10)
# 圆形
circle = patches.Circle((4, 1.5), 0.8, linewidth=2,
edgecolor='red', facecolor='lightcoral', alpha=0.7)
ax.add_patch(circle)
ax.text(4, 1.5, '圆形', ha='center', va='center', fontsize=10)
# 椭圆
ellipse = patches.Ellipse((6.5, 1.5), 2, 1, angle=30, linewidth=2,
edgecolor='green', facecolor='lightgreen', alpha=0.7)
ax.add_patch(ellipse)
ax.text(6.5, 1.5, '椭圆', ha='center', va='center', fontsize=10)
# 2. 多边形
# 六边形
hexagon = patches.RegularPolygon((2, 4), 6, radius=1, orientation=np.pi/6,
edgecolor='purple', facecolor='lavender', linewidth=2)
ax.add_patch(hexagon)
ax.text(2, 4, '六边形', ha='center', va='center', fontsize=10)
# 五角星
star = patches.RegularPolygon((4, 4), 5, radius=1,
edgecolor='orange', facecolor='gold', linewidth=2)
ax.add_patch(star)
ax.text(4, 4, '五边形', ha='center', va='center', fontsize=10)
# 3. 箭头
# 简单箭头
arrow = patches.Arrow(6, 3.5, 2, 1, width=0.3,
edgecolor='black', facecolor='gray')
ax.add_patch(arrow)
ax.text(7, 4, '箭头', ha='center', va='center', fontsize=10)
# 4. 自定义路径
# 创建心形路径
verts = [
(1, 6), # 起点
(1.5, 6.5),
(2, 7),
(2.5, 7.2),
(3, 7),
(3.5, 6.5),
(4, 6),
(3.5, 5.5),
(3, 5),
(2.5, 4.8),
(2, 5),
(1.5, 5.5),
(1, 6), # 回到起点
]
codes = [
Path.MOVETO,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
Path.CLOSEPOLY,
]
path = Path(verts, codes)
patch = patches.PathPatch(path, facecolor='pink', edgecolor='red', linewidth=2, alpha=0.7)
ax.add_patch(patch)
ax.text(2.5, 6, '心形', ha='center', va='center', fontsize=10)
# 5. 扇形
wedge = patches.Wedge((6, 6), 1.5, 45, 135, edgecolor='brown',
facecolor='sandybrown', linewidth=2, alpha=0.7)
ax.add_patch(wedge)
ax.text(6, 6, '扇形', ha='center', va='center', fontsize=10)
# 6. 弧形
arc = patches.Arc((8.5, 6), 3, 2, angle=0, theta1=45, theta2=135,
linewidth=3, edgecolor='teal', linestyle='--')
ax.add_patch(arc)
ax.text(8.5, 6, '弧形', ha='center', va='center', fontsize=10)
# 添加连接线
ax.plot([1, 9], [8, 8], 'k--', alpha=0.5, linewidth=1)
ax.text(5, 8.2, '各种自定义图形元素', ha='center', va='center',
fontsize=12, fontweight='bold',
bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.5))
plt.tight_layout()
plt.show()
五、实际应用场景
1. 股票数据可视化分析系统
python
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
class StockVisualizer:
def __init__(self):
self.fig = None
self.axes = None
self.data = None
def create_candlestick_chart(self, stock_data):
"""创建K线图"""
# 创建图形
self.fig, self.axes = plt.subplots(4, 1, figsize=(15, 12),
gridspec_kw={'height_ratios': [3, 1, 1, 1]})
# 设置数据
self.data = stock_data.copy()
self.data['Date'] = pd.to_datetime(self.data['Date'])
# 1. K线图
ax1 = self.axes[0]
# 计算涨跌颜色
colors = ['red' if close >= open_ else 'green'
for open_, close in zip(self.data['Open'], self.data['Close'])]
# 绘制K线
for i, (idx, row) in enumerate(self.data.iterrows()):
# 绘制实体
rect = Rectangle(
(i - 0.3, min(row['Open'], row['Close'])),
0.6,
abs(row['Close'] - row['Open']),
color=colors[i],
alpha=0.7
)
ax1.add_patch(rect)
# 绘制影线
ax1.plot([i, i], [row['Low'], row['High']],
color=colors[i], linewidth=1)
# 计算移动平均线
self.data['MA5'] = self.data['Close'].rolling(window=5).mean()
self.data['MA20'] = self.data['Close'].rolling(window=20).mean()
# 绘制移动平均线
ax1.plot(self.data['MA5'].values, 'b-', label='5日均线', linewidth=1.5)
ax1.plot(self.data['MA20'].values, 'r-', label='20日均线', linewidth=1.5)
# 设置K线图属性
ax1.set_title('股票K线图与技术分析', fontsize=16, fontweight='bold', pad=20)
ax1.set_ylabel('价格 (元)', fontsize=12)
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)
# 设置x轴刻度
n_points = len(self.data)
step = max(1, n_points // 10)
ax1.set_xticks(range(0, n_points, step))
ax1.set_xticklabels(self.data['Date'].iloc[::step].dt.strftime('%m-%d'))
# 2. 成交量图
ax2 = self.axes[1]
# 成交量柱状图(颜色与涨跌一致)
ax2.bar(range(len(self.data)), self.data['Volume'],
color=colors, alpha=0.6)
ax2.set_ylabel('成交量', fontsize=12)
ax2.grid(True, alpha=0.3)
ax2.set_xticks(range(0, n_points, step))
ax2.set_xticklabels(self.data['Date'].iloc[::step].dt.strftime('%m-%d'))
# 3. RSI指标
ax3 = self.axes[2]
# 计算RSI
delta = self.data['Close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
ax3.plot(rsi.values, 'purple', linewidth=1.5)
ax3.axhline(y=70, color='r', linestyle='--', alpha=0.5, label='超买线')
ax3.axhline(y=30, color='g', linestyle='--', alpha=0.5, label='超卖线')
ax3.fill_between(range(len(rsi)), 70, rsi.values,
where=(rsi.values >= 70), color='red', alpha=0.3)
ax3.fill_between(range(len(rsi)), 30, rsi.values,
where=(rsi.values <= 30), color='green', alpha=0.3)
ax3.set_ylabel('RSI', fontsize=12)
ax3.set_ylim([0, 100])
ax3.legend(loc='upper left')
ax3.grid(True, alpha=0.3)
ax3.set_xticks(range(0, n_points, step))
ax3.set_xticklabels(self.data['Date'].iloc[::step].dt.strftime('%m-%d'))
# 4. MACD指标
ax4 = self.axes[3]
# 计算MACD
exp1 = self.data['Close'].ewm(span=12, adjust=False).mean()
exp2 = self.data['Close'].ewm(span=26, adjust=False).mean()
macd = exp1 - exp2
signal = macd.ewm(span=9, adjust=False).mean()
histogram = macd - signal
# 绘制MACD
ax4.plot(macd.values, 'blue', label='MACD', linewidth=1.5)
ax4.plot(signal.values, 'red', label='信号线', linewidth=1.5)
# 绘制柱状图
colors_macd = ['green' if h >= 0 else 'red' for h in histogram]
ax4.bar(range(len(histogram)), histogram.values,
color=colors_macd, alpha=0.5, width=0.8)
ax4.set_xlabel('日期', fontsize=12)
ax4.set_ylabel('MACD', fontsize=12)
ax4.legend(loc='upper left')
ax4.grid(True, alpha=0.3)
ax4.set_xticks(range(0, n_points, step))
ax4.set_xticklabels(self.data['Date'].iloc[::step].dt.strftime('%m-%d'))
plt.tight_layout()
return self.fig
def add_trading_signals(self, buy_signals, sell_signals):
"""添加交易信号标记"""
if self.fig is None:
print("请先创建图表")
return
ax1 = self.axes[0]
# 标记买入信号
for signal in buy_signals:
idx = self.data[self.data['Date'] == signal['date']].index
if len(idx) > 0:
i = idx[0]
price = signal.get('price', self.data.loc[i, 'Low'] * 0.98)
ax1.plot(i, price, '^', markersize=10, color='green',
label='买入' if 'buy' not in locals() else "")
ax1.annotate(f"买入\n{price:.2f}",
xy=(i, price),
xytext=(i, price * 0.95),
ha='center',
fontsize=8,
arrowprops=dict(arrowstyle='->', color='green'))
buy = True
# 标记卖出信号
for signal in sell_signals:
idx = self.data[self.data['Date'] == signal['date']].index
if len(idx) > 0:
i = idx[0]
price = signal.get('price', self.data.loc[i, 'High'] * 1.02)
ax1.plot(i, price, 'v', markersize=10, color='red',
label='卖出' if 'sell' not in locals() else "")
ax1.annotate(f"卖出\n{price:.2f}",
xy=(i, price),
xytext=(i, price * 1.05),
ha='center',
fontsize=8,
arrowprops=dict(arrowstyle='->', color='red'))
sell = True
if 'buy' in locals() or 'sell' in locals():
ax1.legend(loc='upper left')
plt.tight_layout()
def highlight_periods(self, periods, color='yellow', alpha=0.2):
"""高亮特定时间段"""
if self.fig is None:
print("请先创建图表")
return
ax1 = self.axes[0]
for period in periods:
start_date = pd.to_datetime(period['start'])
end_date = pd.to_datetime(period['end'])
start_idx = self.data[self.data['Date'] >= start_date].index[0]
end_idx = self.data[self.data['Date'] <= end_date].index[-1]
# 获取y轴范围
ymin, ymax = ax1.get_ylim()
# 添加矩形高亮
rect = Rectangle(
(start_idx - 0.5, ymin),
end_idx - start_idx + 1,
ymax - ymin,
color=color,
alpha=alpha,
label=period.get('label', '')
)
ax1.add_patch(rect)
# 添加标签
if 'label' in period:
ax1.text((start_idx + end_idx) / 2, ymax * 0.95,
period['label'],
ha='center',
fontsize=10,
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.tight_layout()
# 生成示例数据
np.random.seed(42)
n_days = 100
dates = pd.date_range(start='2024-01-01', periods=n_days, freq='D')
# 生成随机股价(模拟随机游走)
prices = [100]
for i in range(1, n_days):
change = np.random.randn() * 2 # 每日变化
prices.append(max(10, prices[-1] + change)) # 防止价格为负
# 生成OHLC数据(简化版)
stock_data = pd.DataFrame({
'Date': dates,
'Open': [p * (1 + np.random.randn() * 0.01) for p in prices],
'High': [p * (1 + abs(np.random.randn()) * 0.02) for p in prices],
'Low': [p * (1 - abs(np.random.randn()) * 0.02) for p in prices],
'Close': [p * (1 + np.random.randn() * 0.01) for p in prices],
'Volume': np.random.randint(1000000, 5000000, n_days)
})
# 使用可视化器
visualizer = StockVisualizer()
fig = visualizer.create_candlestick_chart(stock_data)
# 添加交易信号
buy_signals = [
{'date': pd.to_datetime('2024-01-15'), 'price': 95},
{'date': pd.to_datetime('2024-02-20'), 'price': 102}
]
sell_signals = [
{'date': pd.to_datetime('2024-02-01'), 'price': 110},
{'date': pd.to_datetime('2024-03-10'), 'price': 105}
]
visualizer.add_trading_signals(buy_signals, sell_signals)
# 高亮重要时期
important_periods = [
{
'start': '2024-01-20',
'end': '2024-01-30',
'label': '财报发布期'
},
{
'start': '2024-02-15',
'end': '2024-02-25',
'label': '产品发布会'
}
]
visualizer.highlight_periods(important_periods)
plt.show()
2. 气象数据可视化
python
class WeatherVisualizer:
def __init__(self):
self.fig = None
self.data = None
def create_weather_dashboard(self, weather_data):
"""创建气象数据仪表板"""
# 创建图形
self.fig = plt.figure(figsize=(16, 12))
self.data = weather_data.copy()
# 创建子图布局
gs = self.fig.add_gridspec(3, 3)
# 1. 温度曲线图(左上,占用一行两列)
ax1 = self.fig.add_subplot(gs[0, :2])
# 绘制温度曲线
days = range(1, len(self.data) + 1)
ax1.plot(days, self.data['max_temp'], 'r-', label='最高温度', linewidth=2, marker='o')
ax1.plot(days, self.data['min_temp'], 'b-', label='最低温度', linewidth=2, marker='s')
# 填充温度区域
ax1.fill_between(days, self.data['min_temp'], self.data['max_temp'],
color='orange', alpha=0.2)
ax1.set_title('温度变化趋势', fontsize=14, fontweight='bold')
ax1.set_xlabel('日期', fontsize=12)
ax1.set_ylabel('温度 (°C)', fontsize=12)
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)
# 添加温度注释
max_temp_idx = self.data['max_temp'].idxmax()
min_temp_idx = self.data['min_temp'].idxmin()
ax1.annotate(f"最高: {self.data.loc[max_temp_idx, 'max_temp']}°C",
xy=(max_temp_idx + 1, self.data.loc[max_temp_idx, 'max_temp']),
xytext=(max_temp_idx + 1, self.data.loc[max_temp_idx, 'max_temp'] + 2),
ha='center',
arrowprops=dict(arrowstyle='->', color='red'))
ax1.annotate(f"最低: {self.data.loc[min_temp_idx, 'min_temp']}°C",
xy=(min_temp_idx + 1, self.data.loc[min_temp_idx, 'min_temp']),
xytext=(min_temp_idx + 1, self.data.loc[min_temp_idx, 'min_temp'] - 3),
ha='center',
arrowprops=dict(arrowstyle='->', color='blue'))
# 2. 降水量柱状图(右上)
ax2 = self.fig.add_subplot(gs[0, 2])
# 降水天数统计
rainy_days = (self.data['precipitation'] > 0).sum()
dry_days = len(self.data) - rainy_days
ax2.bar(['降水日', '干燥日'], [rainy_days, dry_days],
color=['skyblue', 'gold'], alpha=0.7)
ax2.set_title('降水日统计', fontsize=14, fontweight='bold')
ax2.set_ylabel('天数', fontsize=12)
# 在柱子上添加数字
for i, v in enumerate([rainy_days, dry_days]):
ax2.text(i, v + 0.5, str(v), ha='center', fontsize=12)
# 3. 风速玫瑰图(中左)
ax3 = self.fig.add_subplot(gs[1, 0], projection='polar')
# 风向数据(假设有8个方向)
directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
direction_angles = np.linspace(0, 2*np.pi, 8, endpoint=False)
# 模拟各方向风速频率
wind_frequency = np.random.randint(1, 20, 8)
# 绘制玫瑰图
bars = ax3.bar(direction_angles, wind_frequency,
width=2*np.pi/8, alpha=0.7,
color=plt.cm.viridis(np.linspace(0, 1, 8)))
ax3.set_title('风向频率玫瑰图', fontsize=14, fontweight='bold', pad=20)
ax3.set_theta_zero_location('N')
ax3.set_theta_direction(-1)
ax3.set_xticks(direction_angles)
ax3.set_xticklabels(directions)
# 4. 湿度热力图(中中)
ax4 = self.fig.add_subplot(gs[1, 1])
# 创建湿度矩阵(模拟一周内不同时段的湿度)
hours = np.arange(0, 24, 2)
days_week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
# 生成随机湿度数据
humidity_data = np.random.randint(30, 90, (7, 12))
im = ax4.imshow(humidity_data, cmap='Blues', aspect='auto',
vmin=30, vmax=90)
ax4.set_title('周湿度热力图', fontsize=14, fontweight='bold')
ax4.set_xlabel('时间 (小时)', fontsize=12)
ax4.set_ylabel('星期', fontsize=12)
# 设置刻度
ax4.set_xticks(range(len(hours)))
ax4.set_xticklabels([f'{h:02d}:00' for h in hours], rotation=45)
ax4.set_yticks(range(len(days_week)))
ax4.set_yticklabels(days_week)
# 添加颜色条
cbar = plt.colorbar(im, ax=ax4, fraction=0.046, pad=0.04)
cbar.set_label('湿度 (%)', fontsize=12)
# 5. 气压变化图(中右)
ax5 = self.fig.add_subplot(gs[1, 2])
# 绘制气压曲线
ax5.plot(days, self.data['pressure'], 'g-', linewidth=2)
ax5.fill_between(days, self.data['pressure'],
self.data['pressure'].min(),
color='green', alpha=0.2)
ax5.set_title('气压变化', fontsize=14, fontweight='bold')
ax5.set_xlabel('日期', fontsize=12)
ax5.set_ylabel('气压 (hPa)', fontsize=12)
ax5.grid(True, alpha=0.3)
# 标记高低压
max_pressure_idx = self.data['pressure'].idxmax()
min_pressure_idx = self.data['pressure'].idxmin()
ax5.plot(max_pressure_idx + 1, self.data.loc[max_pressure_idx, 'pressure'],
'^', markersize=10, color='red')
ax5.plot(min_pressure_idx + 1, self.data.loc[min_pressure_idx, 'pressure'],
'v', markersize=10, color='blue')
# 6. 天气现象分布(下左)
ax6 = self.fig.add_subplot(gs[2, 0])
# 统计各种天气现象
weather_counts = self.data['weather'].value_counts()
# 创建甜甜圈图
wedges, texts, autotexts = ax6.pie(weather_counts.values,
labels=weather_counts.index,
autopct='%1.1f%%',
startangle=90,
wedgeprops=dict(width=0.3, edgecolor='w'))
# 美化文本
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
ax6.set_title('天气现象分布', fontsize=14, fontweight='bold')
# 7. 综合指标雷达图(下中)
ax7 = self.fig.add_subplot(gs[2, 1], projection='polar')
# 定义指标
categories = ['温度适宜度', '湿度舒适度', '风力影响',
'降水影响', '空气质量', '紫外线强度']
N = len(categories)
# 计算各指标值(0-100)
temp_score = 100 - abs(self.data['avg_temp'].mean() - 22) * 5 # 22度为最舒适
humidity_score = 100 - abs(self.data['humidity'].mean() - 50) * 2 # 50%湿度最舒适
wind_score = max(0, 100 - self.data['wind_speed'].mean() * 10) # 风速越小越好
rain_score = max(0, 100 - self.data['precipitation'].sum()) # 降水越少越好
air_score = np.random.randint(70, 100) # 空气质量指数
uv_score = max(0, 100 - self.data['uv_index'].mean() * 10) # 紫外线越弱越好
values = [temp_score, humidity_score, wind_score,
rain_score, air_score, uv_score]
# 确保雷达图闭合
values += values[:1]
angles = np.linspace(0, 2*np.pi, N, endpoint=False).tolist()
angles += angles[:1]
# 绘制雷达图
ax7.plot(angles, values, 'o-', linewidth=2, color='blue')
ax7.fill(angles, values, alpha=0.25, color='blue')
# 设置刻度标签
ax7.set_xticks(angles[:-1])
ax7.set_xticklabels(categories, fontsize=10)
ax7.set_ylim([0, 100])
ax7.set_title('天气舒适度雷达图', fontsize=14, fontweight='bold', pad=20)
ax7.grid(True)
# 8. 日出日落时间图(下右)
ax8 = self.fig.add_subplot(gs[2, 2])
# 转换时间格式
sunrise_times = pd.to_datetime(self.data['sunrise'])
sunset_times = pd.to_datetime(self.data['sunset'])
# 计算日照时长(小时)
daylight_hours = (sunset_times - sunrise_times).dt.total_seconds() / 3600
# 绘制日照时长
bars = ax8.bar(days, daylight_hours, color='gold', alpha=0.7, edgecolor='orange')
ax8.set_title('日照时长变化', fontsize=14, fontweight='bold')
ax8.set_xlabel('日期', fontsize=12)
ax8.set_ylabel('日照时长 (小时)', fontsize=12)
ax8.grid(True, alpha=0.3, axis='y')
# 在柱子上添加具体时长
for bar, hour in zip(bars, daylight_hours):
height = bar.get_height()
ax8.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{hour:.1f}', ha='center', va='bottom', fontsize=9)
plt.suptitle('气象数据综合仪表板', fontsize=18, fontweight='bold', y=0.98)
plt.tight_layout()
return self.fig
# 生成示例气象数据
np.random.seed(42)
n_days = 30
dates = pd.date_range(start='2024-06-01', periods=n_days, freq='D')
weather_data = pd.DataFrame({
'date': dates,
'max_temp': np.random.randint(25, 35, n_days),
'min_temp': np.random.randint(15, 25, n_days),
'avg_temp': np.random.randint(20, 30, n_days),
'humidity': np.random.randint(40, 90, n_days),
'pressure': np.random.randint(1000, 1020, n_days),
'wind_speed': np.random.uniform(1, 10, n_days).round(1),
'precipitation': np.random.exponential(2, n_days).round(1),
'uv_index': np.random.randint(3, 11, n_days),
'weather': np.random.choice(['晴', '多云', '阴', '小雨', '中雨', '雷阵雨'],
n_days, p=[0.4, 0.3, 0.1, 0.1, 0.05, 0.05]),
'sunrise': [f'05:{np.random.randint(30, 45):02d}' for _ in range(n_days)],
'sunset': [f'19:{np.random.randint(30, 45):02d}' for _ in range(n_days)]
})
# 计算平均温度
weather_data['avg_temp'] = (weather_data['max_temp'] + weather_data['min_temp']) / 2
# 使用气象可视化器
weather_viz = WeatherVisualizer()
fig = weather_viz.create_weather_dashboard(weather_data)
plt.show()
Matplotlib作为Python可视化领域的奠基者,不仅提供了一个功能强大的绘图库,更建立了一套完整的可视化思维框架。从简单的折线图到复杂的3D可视化,从静态图表到交互式动画,Matplotlib展现了其无与伦比的灵活性和扩展性。它的真正价值在于:将抽象的数学概念和复杂的数据转化为直观的视觉语言,让数据分析从"看数字"升级到"看图形",从而更容易发现模式、趋势和异常。
随着数据科学的不断发展,Matplotlib也在持续进化。新的版本提供了更好的默认样式、更强的性能优化,以及与Jupyter、Streamlit等现代工具的深度集成。同时,围绕Matplotlib建立的生态系统(如Seaborn、Plotly、Bokeh等)进一步扩展了Python可视化的边界。
你在使用Matplotlib时有什么特别的技巧或遇到过什么有趣的挑战?或者你有关于数据可视化的独特应用场景想要分享吗?欢迎在评论区交流你的经验和想法!
更多推荐

所有评论(0)