Matplotlib的装饰

一、颜色系统

1. 基本颜色表示

import matplotlib.pyplot as plt
import numpy as np

# 1. 颜色字符串(8种基本颜色)
basic_colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']
# blue, green, red, cyan, magenta, yellow, black, white

# 2. 扩展颜色名(140+种)
named_colors = ['red', 'blue', 'green', 'orange', 'purple', 
                'brown', 'pink', 'gray', 'olive', 'cyan']

# 3. 十六进制颜色
hex_colors = ['#FF5733', '#33FF57', '#3357FF', '#F0E68C', '#8A2BE2']

# 4. RGB/RGBA元组
rgb_colors = [
    (1.0, 0.0, 0.0),        # 纯红
    (0.0, 0.5, 0.0),        # 深绿
    (0.2, 0.4, 0.6),        # 蓝灰
    (0.5, 0.5, 0.5, 0.5),   # 半透明白
]

# 5. 灰度值
gray_colors = ['0.0', '0.25', '0.5', '0.75', '1.0']  # 黑到白

2. 颜色映射(Colormap)

# 查看所有颜色映射
print(plt.colormaps()[:10])  # 显示前10个

# 分类颜色映射(适用于分类数据)
categorical_cmaps = ['tab10', 'tab20', 'Set1', 'Set2', 'Set3', 'Pastel1']

# 连续颜色映射(适用于连续数据)
sequential_cmaps = ['viridis', 'plasma', 'inferno', 'magma', 'cividis',
                    'Greys', 'Blues', 'Greens', 'Oranges', 'Reds']

# 发散颜色映射(适用于有中心值的数据)
diverging_cmaps = ['RdBu', 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm']

# 使用示例
fig, axes = plt.subplots(3, 3, figsize=(12, 10))
cmaps = ['viridis', 'plasma', 'inferno', 
         'RdBu', 'coolwarm', 'Spectral',
         'Set1', 'tab10', 'Pastel1']

for ax, cmap in zip(axes.flat, cmaps):
    data = np.random.rand(10, 10)
    im = ax.imshow(data, cmap=cmap)
    ax.set_title(cmap)
    ax.axis('off')
    plt.colorbar(im, ax=ax, shrink=0.8)

plt.suptitle('常用颜色映射示例', fontsize=16)
plt.tight_layout()
plt.show()

3. 自定义颜色映射

from matplotlib.colors import LinearSegmentedColormap

# 方法1:从颜色列表创建
colors = ['#FF0000', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF']
custom_cmap = LinearSegmentedColormap.from_list('my_cmap', colors, N=256)

# 方法2:定义颜色节点
cdict = {
    'red':   [(0.0,  0.0, 0.0),   # 在0.0位置,红色为0
              (0.5,  1.0, 1.0),   # 在0.5位置,红色为1
              (1.0,  1.0, 1.0)],  # 在1.0位置,红色为1
    
    'green': [(0.0,  0.0, 0.0),
              (0.25, 0.0, 0.0),
              (0.75, 1.0, 1.0),
              (1.0,  1.0, 1.0)],
    
    'blue':  [(0.0,  0.0, 0.0),
              (0.5,  0.0, 0.0),
              (1.0,  1.0, 1.0)]
}

custom_cmap2 = LinearSegmentedColormap('Custom2', cdict, 256)

# 应用自定义颜色映射
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# 使用第一个自定义颜色映射
im1 = ax1.imshow(np.random.rand(10, 10), cmap=custom_cmap)
ax1.set_title('彩虹色映射')
plt.colorbar(im1, ax=ax1)

# 使用第二个自定义颜色映射
im2 = ax2.imshow(np.random.rand(10, 10), cmap=custom_cmap2)
ax2.set_title('红绿蓝渐变映射')
plt.colorbar(im2, ax=ax2)

plt.tight_layout()
plt.show()

二、线型与标记

1. 线型(Line Styles)

# 基本线型
line_styles = [
    '-',   # 实线
    '--',  # 虚线
    '-.',  # 点划线
    ':',   # 点线
    ' ',   # 无线
    '',    # 无线
    'None' # 无线
]

# 自定义线型
custom_linestyle = {
    'dotted': (0, (1, 1)),           # 点线
    'dashed': (0, (5, 5)),           # 虚线
    'dashdot': (0, (3, 5, 1, 5)),    # 点划线
    'loosely dotted': (0, (1, 10)),  # 稀疏点线
    'densely dotted': (0, (1, 1)),   # 密集点线
    'loosely dashed': (0, (5, 10)),  # 稀疏虚线
    'densely dashed': (0, (5, 1)),   # 密集虚线
}

# 线型演示
fig, ax = plt.subplots(figsize=(12, 6))
x = np.linspace(0, 10, 100)

# 基本线型
for i, ls in enumerate(line_styles[:4]):
    y = np.sin(x) + i * 0.5
    ax.plot(x, y, linestyle=ls, linewidth=2, label=f"linestyle='{ls}'")

# 自定义线型
for i, (name, ls) in enumerate(custom_linestyle.items()):
    y = np.cos(x) + (i + 4) * 0.5
    ax.plot(x, y, linestyle=ls, linewidth=2, label=name)

ax.legend(ncol=2, loc='upper right')
ax.set_title('线型样式示例', fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()

2. 标记(Markers)

# 基本标记类型
markers = [
    '.',   # 点
    ',',   # 像素点
    'o',   # 圆圈
    'v',   # 倒三角
    '^',   # 正三角
    '<',   # 左三角
    '>',   # 右三角
    '1',   # 三脚架下
    '2',   # 三脚架上
    '3',   # 三脚架左
    '4',   # 三脚架右
    '8',   # 八角形
    's',   # 正方形
    'p',   # 五边形
    '*',   # 星号
    'h',   # 六边形1
    'H',   # 六边形2
    '+',   # 加号
    'x',   # 叉号
    'D',   # 菱形
    'd',   # 瘦菱形
    '|',   # 竖线
    '_',   # 横线
    'P',   # 加号填充
    'X',   # 叉号填充
]

# 标记演示
fig, axes = plt.subplots(4, 6, figsize=(15, 10))
axes = axes.flatten()

for i, (ax, marker) in enumerate(zip(axes, markers)):
    x = np.arange(5)
    y = np.ones(5) * i
    ax.scatter(x, y, marker=marker, s=100, edgecolor='k', linewidth=1)
    ax.set_title(f"'{marker}'", fontsize=12)
    ax.set_xlim(-1, 5)
    ax.set_ylim(-1, len(markers))
    ax.axis('off')

plt.suptitle('标记样式大全', fontsize=16)
plt.tight_layout()
plt.show()

3. 线型与标记组合

fig, ax = plt.subplots(figsize=(12, 8))

# 创建组合样式
styles = [
    {'marker': 'o', 'linestyle': '-', 'color': 'blue'},
    {'marker': 's', 'linestyle': '--', 'color': 'red'},
    {'marker': '^', 'linestyle': '-.', 'color': 'green'},
    {'marker': 'D', 'linestyle': ':', 'color': 'orange'},
    {'marker': '*', 'linestyle': ' ', 'color': 'purple'},  # 只有标记
    {'marker': ' ', 'linestyle': '-', 'color': 'brown'},   # 只有线
]

x = np.linspace(0, 10, 20)
for i, style in enumerate(styles):
    y = np.sin(x) + i * 0.8
    ax.plot(x, y, 
            marker=style['marker'],
            linestyle=style['linestyle'],
            color=style['color'],
            markersize=10,
            markerfacecolor='white',
            markeredgecolor=style['color'],
            markeredgewidth=2,
            linewidth=2,
            label=f"样式{i+1}")

ax.legend(loc='upper right', ncol=2)
ax.set_title('线型与标记组合示例', fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()

三、文本装饰

1. 字体与样式

# 系统可用字体
import matplotlib.font_manager as fm
fonts = [f.name for f in fm.fontManager.ttflist]
print(f"可用字体数量: {len(fonts)}")
print("部分字体:", fonts[:10])

# 字体属性设置
font_properties = {
    'family': 'serif',          # 字体族: 'serif', 'sans-serif', 'monospace'
    'style': 'italic',          # 样式: 'normal', 'italic', 'oblique'
    'variant': 'normal',        # 变体: 'normal', 'small-caps'
    'weight': 'bold',           # 粗细: 'light', 'normal', 'medium', 'semibold', 'bold', 'black'
    'stretch': 'normal',        # 拉伸: 'ultra-condensed' 到 'ultra-expanded'
    'size': 12                  # 大小
}

# 应用示例
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 10, 100)
y = np.sin(x)

ax.plot(x, y)

# 添加不同样式的文本
texts = [
    ('正常文本', (1, 0.5), {'fontsize': 12}),
    ('粗体文本', (3, 0), {'fontweight': 'bold', 'fontsize': 14}),
    ('斜体文本', (5, -0.5), {'fontstyle': 'italic', 'fontsize': 12}),
    ('大号文本', (7, 0.5), {'fontsize': 16}),
    ('彩色文本', (2, 0.8), {'color': 'red', 'fontsize': 12}),
    ('带框文本', (4, 0.8), {'bbox': dict(boxstyle='round', facecolor='yellow', alpha=0.5)}),
    ('旋转文本', (8, -0.5), {'rotation': 45, 'fontsize': 12}),
]

for text_str, position, props in texts:
    ax.text(position[0], position[1], text_str, **props)

ax.set_title('文本样式示例', fontsize=16, fontweight='bold')
plt.show()

2. 数学公式

fig, ax = plt.subplots(figsize=(12, 8))

# 行内公式(单$符号)
ax.text(0.1, 0.9, r'行内公式: $E = mc^2$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.8, r'希腊字母: $\alpha, \beta, \gamma$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.7, r'上下标: $x^2 + y_1$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.6, r'分数: $\frac{a}{b}$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.5, r'根号: $\sqrt{x}$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.4, r'积分: $\int_a^b f(x)dx$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.3, r'求和: $\sum_{i=1}^n i$', fontsize=14, transform=ax.transAxes)
ax.text(0.1, 0.2, r'矩阵: $\begin{pmatrix} a & b \\ c & d \end{pmatrix}$', fontsize=14, transform=ax.transAxes)

# 多行公式($$符号)
equation = r'''
多行公式:
$$
\begin{aligned}
f(x) &= \int_{-\infty}^{\infty} \hat{f}(\xi) e^{2\pi i \xi x} d\xi \\
&= \lim_{n \to \infty} \sum_{k=1}^{n} f(x_k) \Delta x
\end{aligned}
$$
'''

ax.text(0.6, 0.5, equation, fontsize=14, transform=ax.transAxes,
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.axis('off')
ax.set_title('LaTeX数学公式示例', fontsize=16, fontweight='bold')
plt.show()

3. 注释与箭头

fig, ax = plt.subplots(figsize=(12, 8))

# 绘制示例曲线
x = np.linspace(0, 10, 100)
y = np.sin(x) * np.exp(-x/10)
ax.plot(x, y, 'b-', linewidth=2)

# 1. 基本注释
ax.annotate('最大值点', 
            xy=(np.pi/2, max(y)),          # 箭头指向的位置
            xytext=(np.pi/2 + 1, max(y) + 0.1),  # 文本位置
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3', color='red'),
            fontsize=12)

# 2. 带不同箭头的注释
arrow_styles = ['->', '-[', '<-', '<->', 'fancy', 'wedge', 'simple']

for i, arrowstyle in enumerate(arrow_styles):
    x_pos = 1 + i * 1.2
    y_pos = -0.2 - i * 0.05
    
    ax.annotate(f"'{arrowstyle}'", 
                xy=(x_pos, np.sin(x_pos) * np.exp(-x_pos/10)),
                xytext=(x_pos, y_pos),
                arrowprops=dict(arrowstyle=arrowstyle, 
                              color='green',
                              lw=1.5,
                              shrinkA=5, shrinkB=5),
                fontsize=10,
                ha='center')

# 3. 文本框注释
ax.text(0.02, 0.98, '曲线说明:\n• 衰减的正弦波\n• 时间常数: 10\n• 初始相位: 0',
        transform=ax.transAxes,
        fontsize=12,
        verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.8))

# 4. 连接线注释
ax.annotate('',
            xy=(8, y[80]), 
            xytext=(8.5, -0.3),
            arrowprops=dict(arrowstyle='->',
                          connectionstyle='angle3,angleA=90,angleB=0',
                          color='purple',
                          lw=2))

ax.text(8.5, -0.35, '连接线示例', ha='center', fontsize=10)

ax.set_title('注释与箭头装饰示例', fontsize=16)
ax.grid(True, alpha=0.3)
plt.show()

四、填充与阴影

1. 区域填充

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 子图1:基本填充
ax = axes[0, 0]
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
ax.plot(x, y1, 'b-', label='sin(x)')
ax.plot(x, y2, 'r--', label='cos(x)')
ax.fill_between(x, y1, y2, where=(y1 > y2), color='green', alpha=0.3, label='sin > cos')
ax.fill_between(x, y1, y2, where=(y1 <= y2), color='red', alpha=0.3, label='sin ≤ cos')
ax.legend()
ax.set_title('fill_between 区域填充')

# 子图2:与基准线填充
ax = axes[0, 1]
x = np.arange(10)
y = np.random.randn(10).cumsum()
ax.plot(x, y, 'o-')
ax.fill_between(x, y, 0, where=(y > 0), color='green', alpha=0.3, interpolate=True)
ax.fill_between(x, y, 0, where=(y <= 0), color='red', alpha=0.3, interpolate=True)
ax.axhline(y=0, color='black', linestyle='--', alpha=0.5)
ax.set_title('与基准线填充')

# 子图3:多边形填充
ax = axes[1, 0]
t = np.linspace(0, 2*np.pi, 100)
x = 2 * np.cos(t) + np.cos(2*t)
y = 2 * np.sin(t) - np.sin(2*t)
ax.fill(x, y, 'orange', alpha=0.7, edgecolor='darkorange', linewidth=2)
ax.set_title('多边形填充')
ax.set_aspect('equal')

# 子图4:渐变填充
ax = axes[1, 1]
x = np.linspace(0, 10, 100)
y = np.exp(-x/5) * np.sin(x)
ax.plot(x, y, 'b-', linewidth=2)

# 创建渐变填充
for i in range(len(x)-1):
    alpha = 1 - i/len(x)  # 渐变透明度
    ax.fill_between(x[i:i+2], 0, y[i:i+2], 
                    color='blue', 
                    alpha=alpha*0.3)

ax.set_title('渐变透明度填充')

plt.suptitle('填充装饰示例', fontsize=16)
plt.tight_layout()
plt.show()

2. 阴影效果

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 子图1:误差带
ax = axes[0, 0]
x = np.linspace(0, 10, 50)
y = np.sin(x)
y_err = 0.2 * np.ones_like(x)

ax.plot(x, y, 'b-', linewidth=2, label='均值')
ax.fill_between(x, y - y_err, y + y_err, 
                alpha=0.3, color='blue', label='±1σ')
ax.fill_between(x, y - 2*y_err, y + 2*y_err, 
                alpha=0.2, color='blue', label='±2σ')
ax.legend()
ax.set_title('误差带阴影')

# 子图2:置信区间
ax = axes[0, 1]
np.random.seed(42)
x = np.linspace(0, 10, 30)
y_true = 2 * x + 1
y_noise = y_true + np.random.randn(30) * 3

# 模拟置信区间
y_pred = 2.1 * x + 0.8  # 预测值
ci_lower = y_pred - 4
ci_upper = y_pred + 4

ax.scatter(x, y_noise, alpha=0.6, label='观测值')
ax.plot(x, y_pred, 'r-', linewidth=2, label='回归线')
ax.fill_between(x, ci_lower, ci_upper, 
                color='red', alpha=0.2, label='95%置信区间')
ax.legend()
ax.set_title('置信区间阴影')

# 子图3:柱状图阴影
ax = axes[1, 0]
categories = ['A', 'B', 'C', 'D', 'E']
values = [25, 40, 30, 35, 20]

bars = ax.bar(categories, values, 
              color='skyblue',
              edgecolor='navy',
              linewidth=2)

# 添加阴影效果
for bar in bars:
    height = bar.get_height()
    ax.text(bar.get_x() + bar.get_width()/2., height + 1,
            f'{height}', ha='center', va='bottom',
            fontsize=10, fontweight='bold')
    
    # 添加阴影
    shadow = plt.Rectangle((bar.get_x() + 0.05, 0), 
                          bar.get_width() - 0.1, 
                          height,
                          color='gray', alpha=0.3, zorder=0)
    ax.add_patch(shadow)

ax.set_title('柱状图阴影效果')

# 子图4:文字阴影
ax = axes[1, 1]
ax.axis([0, 1, 0, 1])
ax.axis('off')

# 创建带阴影的文字
def text_with_shadow(ax, x, y, text, fontsize=24, shadow_offset=0.005):
    # 先画阴影
    ax.text(x + shadow_offset, y - shadow_offset, text,
            fontsize=fontsize, fontweight='bold',
            color='gray', alpha=0.5,
            transform=ax.transAxes)
    # 再画实际文字
    ax.text(x, y, text,
            fontsize=fontsize, fontweight='bold',
            color='darkred',
            transform=ax.transAxes)

text_with_shadow(ax, 0.1, 0.8, '阴影文字效果', 28)
text_with_shadow(ax, 0.2, 0.6, '3D立体感', 32, 0.008)
text_with_shadow(ax, 0.3, 0.4, '装饰性强', 36, 0.01)
text_with_shadow(ax, 0.4, 0.2, '重点突出', 40, 0.012)

ax.set_title('文字阴影效果')

plt.suptitle('阴影装饰示例', fontsize=16)
plt.tight_layout()
plt.show()

五、边框与背景

1. 坐标轴装饰

fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 子图1:基本坐标轴样式
ax = axes[0, 0]
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x))
ax.set_title('默认坐标轴')

# 子图2:隐藏坐标轴
ax = axes[0, 1]
ax.plot(x, np.cos(x))
ax.axis('off')  # 隐藏所有坐标轴
ax.set_title('隐藏坐标轴')

# 子图3:自定义坐标轴样式
ax = axes[0, 2]
ax.plot(x, np.tan(x)/10)  # 缩放tan函数
ax.spines['top'].set_visible(False)    # 隐藏顶部边框
ax.spines['right'].set_visible(False)  # 隐藏右侧边框
ax.spines['left'].set_linewidth(2)     # 加粗左侧边框
ax.spines['left'].set_color('red')     # 左侧边框红色
ax.spines['bottom'].set_linewidth(2)   # 加粗底部边框
ax.spines['bottom'].set_color('blue')  # 底部边框蓝色
ax.set_title('自定义坐标轴边框')

# 子图4:移动坐标轴位置
ax = axes[1, 0]
ax.plot(x, np.exp(-x/5) * np.sin(x))
ax.spines['left'].set_position('center')        # 左侧坐标轴移到中心
ax.spines['bottom'].set_position('center')      # 底部坐标轴移到中心
ax.spines['right'].set_color('none')           # 隐藏右侧坐标轴
ax.spines['top'].set_color('none')             # 隐藏顶部坐标轴
ax.xaxis.set_ticks_position('bottom')          # X轴刻度在底部
ax.yaxis.set_ticks_position('left')            # Y轴刻度在左侧
ax.set_title('坐标轴居中')

# 子图5:双坐标轴
ax = axes[1, 1]
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.exp(-x/5) * 100

color1 = 'tab:blue'
color2 = 'tab:red'

ax.plot(x, y1, color=color1, label='sin(x)')
ax.set_xlabel('x')
ax.set_ylabel('sin(x)', color=color1)
ax.tick_params(axis='y', labelcolor=color1)

# 创建第二个Y轴
ax2 = ax.twinx()
ax2.plot(x, y2, color=color2, linestyle='--', label='exp(-x/5)*100')
ax2.set_ylabel('衰减指数', color=color2)
ax2.tick_params(axis='y', labelcolor=color2)

ax.set_title('双Y轴图表')

# 子图6:极坐标轴
ax = axes[1, 2]
ax = plt.subplot(236, projection='polar')
theta = np.linspace(0, 2*np.pi, 100)
r = 1 + 0.5 * np.sin(5*theta)
ax.plot(theta, r)
ax.set_title('极坐标轴', pad=20)
ax.grid(True)

plt.suptitle('坐标轴装饰示例', fontsize=16)
plt.tight_layout()
plt.show()

2. 背景与网格

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 子图1:网格样式
ax = axes[0, 0]
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), 'b-', linewidth=2)
ax.grid(True, 
        which='both',      # 'major', 'minor', 'both'
        axis='both',       # 'x', 'y', 'both'
        linestyle='--',    # 线型
        linewidth=0.5,     # 线宽
        alpha=0.7,         # 透明度
        color='gray')      # 颜色
ax.set_title('网格样式')

# 子图2:背景颜色
ax = axes[0, 1]
ax.plot(x, np.cos(x), 'r-', linewidth=2)
ax.set_facecolor('lightyellow')  # 设置背景颜色
ax.set_title('黄色背景')

# 添加网格
ax.grid(True, alpha=0.3)

# 子图3:渐变背景
ax = axes[1, 0]
ax.plot(x, np.sin(x) * np.exp(-x/10), 'g-', linewidth=2)

# 创建渐变背景
gradient = np.linspace(0, 1, 256).reshape(1, -1)
gradient = np.vstack((gradient, gradient))
ax.imshow(gradient, aspect='auto', cmap='Blues', 
          extent=[ax.get_xlim()[0], ax.get_xlim()[1], 
                  ax.get_ylim()[0], ax.get_ylim()[1]],
          alpha=0.3, zorder=0)

# 重新绘制曲线(在渐变之上)
ax.plot(x, np.sin(x) * np.exp(-x/10), 'g-', linewidth=2, zorder=1)
ax.set_title('渐变背景')

# 子图4:图片背景
ax = axes[1, 1]
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

# 加载背景图片(示例:使用颜色渐变代替)
from matplotlib.cbook import get_sample_data
import matplotlib.image as mpimg

# 创建示例背景
bg_data = np.random.rand(10, 10)
ax.imshow(bg_data, extent=[0, 2*np.pi, -1.2, 1.2], 
          aspect='auto', cmap='Greys', alpha=0.2, zorder=0)

# 绘制曲线
ax.plot(x, y, 'purple', linewidth=3, zorder=1)
ax.fill_between(x, 0, y, where=(y>0), color='purple', alpha=0.3, zorder=1)
ax.set_title('图片背景')

plt.suptitle('背景与网格装饰示例', fontsize=16)
plt.tight_layout()
plt.show()

六、图例装饰

1. 图例样式

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 子图1:基本图例
ax = axes[0, 0]
x = np.linspace(0, 10, 100)
for i in range(5):
    ax.plot(x, np.sin(x + i*0.5), label=f'曲线 {i+1}')
ax.legend()
ax.set_title('基本图例')

# 子图2:自定义位置和样式
ax = axes[0, 1]
for i in range(3):
    ax.plot(x, np.cos(x + i*0.5), linewidth=2, label=f'cos {i+1}')

legend = ax.legend(loc='upper left',           # 位置
                   bbox_to_anchor=(1.02, 1),   # 相对于坐标轴的位置
                   borderaxespad=0.,           # 边框内边距
                   frameon=True,               # 显示边框
                   fancybox=True,              # 圆角边框
                   shadow=True,                # 阴影
                   fontsize=12,
                   title='图例标题',
                   title_fontsize=13)

# 设置图例样式
legend.get_frame().set_facecolor('lightblue')
legend.get_frame().set_alpha(0.7)
legend.get_frame().set_edgecolor('navy')
legend.get_frame().set_linewidth(2)

ax.set_title('自定义图例样式')

# 子图3:多列图例
ax = axes[1, 0]
for i in range(8):
    ax.plot(x[:50], np.random.randn(50).cumsum(), label=f'序列 {i+1}')

ax.legend(ncol=2,                # 2列
          loc='upper center',    # 位置
          bbox_to_anchor=(0.5, -0.1),  # 在图外底部
          fontsize=10)
ax.set_title('多列图例')

# 子图4:自定义图例项
ax = axes[1, 1]
# 创建不同类型的图形元素
line1, = ax.plot(x, np.sin(x), 'b-', label='正弦')
line2, = ax.plot(x, np.cos(x), 'r--', label='余弦')
scatter = ax.scatter(x[::10], np.tan(x[::10])/10, 
                     c='green', s=50, label='正切点')

# 创建自定义图例
from matplotlib.lines import Line2D

# 手动创建图例句柄
custom_lines = [Line2D([0], [0], color='blue', lw=2),
                Line2D([0], [0], color='red', lw=2, linestyle='--'),
                Line2D([0], [0], marker='o', color='w', 
                       markerfacecolor='green', markersize=10)]

# 创建图例
ax.legend(custom_lines, ['自定义线1', '自定义线2', '自定义点'],
          loc='center',
          framealpha=0.9,
          edgecolor='black')

ax.set_title('自定义图例句柄')

plt.suptitle('图例装饰示例', fontsize=16)
plt.tight_layout()
plt.show()

2. 高级图例技巧

fig, ax = plt.subplots(figsize=(12, 8))

# 创建多个数据系列
np.random.seed(42)
n_series = 5
n_points = 50

# 绘制多条曲线
lines = []
for i in range(n_series):
    line, = ax.plot(np.arange(n_points), 
                    np.random.randn(n_points).cumsum(),
                    linewidth=2,
                    marker='o' if i < 2 else 's',  # 前2个用圆圈,后3个用方块
                    markersize=6 if i < 2 else 8,
                    markevery=5,
                    alpha=0.7,
                    label=f'系列 {i+1}')
    lines.append(line)

# 1. 交互式图例(点击隐藏/显示系列)
legends = []
for i, line in enumerate(lines):
    leg = ax.legend([line], [line.get_label()], 
                    loc='upper left',
                    bbox_to_anchor=(1.01, 1 - i*0.15),
                    frameon=True,
                    fancybox=True,
                    framealpha=0.8)
    
    # 使图例可点击
    leg.get_lines()[0].set_picker(True)
    leg.get_lines()[0].pickradius = 10
    
    # 存储图例对象
    legends.append(leg)
    
    # 默认添加到ax,但后面会移除
    ax.add_artist(leg)

# 移除临时图例,添加总图例
for leg in legends:
    leg.remove()

# 添加一个总图例
main_legend = ax.legend(loc='upper left', 
                        bbox_to_anchor=(1.01, 0.3),
                        title='数据系列',
                        fontsize=11)

# 2. 带符号的图例
from matplotlib.patches import Patch

# 创建符号图例
symbols_legend_elements = [
    Patch(facecolor='lightblue', edgecolor='blue', label='类别 A'),
    Patch(facecolor='lightgreen', edgecolor='green', label='类别 B'),
    Patch(facecolor='lightcoral', edgecolor='red', label='类别 C'),
]

symbols_legend = ax.legend(handles=symbols_legend_elements,
                           loc='upper left',
                           bbox_to_anchor=(1.01, 0),
                           title='数据类别',
                           fontsize=11)

ax.add_artist(main_legend)  # 重新添加主图例

# 3. 彩色图例文本
color_legend = ax.legend([], [], loc='upper left', 
                         bbox_to_anchor=(1.01, 0.6),
                         title='状态说明',
                         frameon=False,
                         handlelength=0)

# 创建彩色文本
from matplotlib.patches import Rectangle

# 添加彩色方块
colors = ['green', 'yellow', 'red']
labels = ['正常', '警告', '危险']
for i, (color, label) in enumerate(zip(colors, labels)):
    # 添加彩色方块
    rect = Rectangle((0, 0), 1, 1, facecolor=color, edgecolor='black')
    color_legend.add_patch(rect)
    # 添加文本
    color_legend.get_texts()[i].set_text(label)
    color_legend.get_texts()[i].set_color('black')

ax.add_artist(color_legend)

# 设置图形边界
ax.set_xlim(0, n_points)
ax.set_title('高级图例装饰示例', fontsize=14, pad=20)
ax.grid(True, alpha=0.3)

# 调整布局
plt.subplots_adjust(right=0.75)
plt.show()

七、综合装饰实战

# 创建专业风格的图表装饰
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 设置全局样式
plt.style.use('seaborn-v0_8-darkgrid')
fig.patch.set_facecolor('#F5F5F5')  # 画布背景色

# 数据准备
np.random.seed(42)
x = np.linspace(0, 10, 100)
time = pd.date_range('2023-01-01', periods=100, freq='D')

# 1. 商业折线图装饰
ax1.plot(time, 100 + np.cumsum(np.random.randn(100)), 
         color='#2E86AB', linewidth=2.5, marker='o', markevery=10)
ax1.fill_between(time, 
                 100 + np.cumsum(np.random.randn(100)) - 5,
                 100 + np.cumsum(np.random.randn(100)) + 5,
                 color='#2E86AB', alpha=0.2)

# 装饰
ax1.set_title('📈 股价走势分析', fontsize=14, fontweight='bold', pad=15)
ax1.set_xlabel('日期', fontsize=11)
ax1.set_ylabel('价格 (USD)', fontsize=11)
ax1.tick_params(axis='x', rotation=45)
ax1.grid(True, alpha=0.4)

# 添加注释
ax1.annotate('关键突破点', 
             xy=(time[60], 110),
             xytext=(time[40], 120),
             arrowprops=dict(arrowstyle='->', 
                           connectionstyle='arc3,rad=0.2',
                           color='red',
                           lw=1.5),
             fontsize=10,
             bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

# 2. 统计柱状图装饰
categories = ['科技', '金融', '医疗', '能源', '消费']
values = [35, 28, 18, 12, 7]
colors = ['#4ECDC4', '#FF6B6B', '#FFD166', '#06D6A0', '#118AB2']
errors = [2, 3, 1.5, 2.5, 1]

bars = ax2.bar(categories, values, 
               color=colors,
               edgecolor='white',
               linewidth=2,
               yerr=errors,
               capsize=5,
               error_kw={'elinewidth': 2, 'ecolor': 'darkred'})

# 添加数值标签
for bar, value in zip(bars, values):
    height = bar.get_height()
    ax2.text(bar.get_x() + bar.get_width()/2., height + 1,
             f'{value}%', ha='center', va='bottom',
             fontsize=10, fontweight='bold',
             bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))

# 装饰
ax2.set_title('🏢 行业市场份额分布', fontsize=14, fontweight='bold', pad=15)
ax2.set_ylabel('市场份额 (%)', fontsize=11)
ax2.set_ylim(0, 45)
ax2.grid(True, axis='y', alpha=0.3)

# 3. 科学散点图装饰
x_scatter = np.random.randn(200)
y_scatter = 0.5 * x_scatter + np.random.randn(200) * 0.5

# 按密度着色
from scipy.stats import gaussian_kde
xy = np.vstack([x_scatter, y_scatter])
z = gaussian_kde(xy)(xy)

scatter = ax3.scatter(x_scatter, y_scatter, 
                      c=z, s=50, alpha=0.6, 
                      cmap='viridis', edgecolor='white', linewidth=0.5)

# 添加回归线
from scipy import stats
slope, intercept, r_value, p_value, std_err = stats.linregress(x_scatter, y_scatter)
x_fit = np.array([x_scatter.min(), x_scatter.max()])
y_fit = slope * x_fit + intercept
ax3.plot(x_fit, y_fit, 'r--', linewidth=2, 
         label=f'y = {slope:.2f}x + {intercept:.2f}\nR² = {r_value**2:.3f}')

# 装饰
ax3.set_title('🔬 实验数据相关性分析', fontsize=14, fontweight='bold', pad=15)
ax3.set_xlabel('自变量 X', fontsize=11)
ax3.set_ylabel('因变量 Y', fontsize=11)
ax3.legend(loc='upper left', fontsize=10, framealpha=0.9)
ax3.grid(True, alpha=0.3)
plt.colorbar(scatter, ax=ax3, label='点密度')

# 4. 饼图装饰
sizes = [30, 25, 20, 15, 10]
labels = ['Python', 'Java', 'JavaScript', 'C++', '其他']
explode = (0.05, 0, 0, 0, 0)
colors_pie = ['#FF9F1C', '#E71D36', '#2EC4B6', '#011627', '#6C757D']

wedges, texts, autotexts = ax4.pie(sizes, 
                                   explode=explode,
                                   labels=labels,
                                   colors=colors_pie,
                                   autopct='%1.1f%%',
                                   startangle=90,
                                   shadow=True,
                                   wedgeprops=dict(edgecolor='white', linewidth=2))

# 美化百分比文本
for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_fontsize(10)
    autotext.set_fontweight('bold')

# 美化标签文本
for text in texts:
    text.set_fontsize(11)

# 添加中心圆(甜甜圈图效果)
centre_circle = plt.Circle((0,0), 0.6, fc='white')
ax4.add_patch(centre_circle)

# 装饰
ax4.set_title('💻 编程语言使用比例', fontsize=14, fontweight='bold', pad=20)

# 统一调整
for ax in [ax1, ax2, ax3, ax4]:
    # 设置统一的边框样式
    for spine in ax.spines.values():
        spine.set_linewidth(1.5)
        spine.set_color('gray')
    
    # 设置统一的刻度样式
    ax.tick_params(axis='both', which='major', labelsize=10)

# 添加总标题
fig.suptitle('数据可视化装饰综合示例', 
             fontsize=18, fontweight='bold', y=0.98)

# 调整布局
plt.tight_layout(rect=[0, 0, 1, 0.96])

# 添加脚注
fig.text(0.5, 0.01, 
         '数据来源:模拟数据 | 可视化工具:Matplotlib',
         ha='center', fontsize=10, style='italic',
         bbox=dict(boxstyle='round', facecolor='lightgray', alpha=0.3))

plt.show()

八、实用装饰技巧

# 装饰技巧集合
fig, axes = plt.subplots(3, 3, figsize=(15, 12))

# 1. 渐变线
ax = axes[0, 0]
x = np.linspace(0, 10, 100)
y = np.sin(x)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

from matplotlib.collections import LineCollection
lc = LineCollection(segments, cmap='viridis', linewidth=3)
lc.set_array(y)  # 按y值着色
ax.add_collection(lc)
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
ax.set_title('渐变线')

# 2. 填充透明度渐变
ax = axes[0, 1]
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y, 'b-')
for i in range(len(x)-1):
    alpha = 0.3 * (1 - i/len(x))
    ax.fill_between(x[i:i+2], 0, y[i:i+2], color='blue', alpha=alpha)
ax.set_title('渐变透明度填充')

# 3. 阴影文字
ax = axes[0, 2]
ax.axis([0, 1, 0, 1])
ax.axis('off')
for i in range(5):
    size = 20 + i * 4
    offset = 0.003 * size
    ax.text(0.1, 0.9 - i*0.15, f'Size {size}',
            fontsize=size, fontweight='bold',
            color='white',
            path_effects=[patheffects.withSimplePatchShadow(
                offset=(offset, -offset), shadow_color='gray',
                alpha=0.7)])
ax.set_title('阴影文字效果')

# 4. 高亮区域
ax = axes[1, 0]
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y, 'b-')
ax.axvspan(3, 5, alpha=0.3, color='yellow', label='关键区域')
ax.axvspan(7, 9, alpha=0.3, color='red')
ax.set_title('高亮区域')

# 5. 箭头标注
ax = axes[1, 1]
x = np.linspace(0, 10, 100)
y = np.exp(-x/5) * np.sin(x)
ax.plot(x, y, 'g-')
ax.annotate('局部最大值', xy=(x[20], y[20]), 
            xytext=(x[20]+1, y[20]+0.1),
            arrowprops=dict(arrowstyle='fancy',
                          facecolor='red',
                          connectionstyle='arc3,rad=0.3'),
            fontsize=10,
            bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.8))
ax.set_title('箭头标注')

# 6. 自定义刻度
ax = axes[1, 2]
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x))
ax.set_xticks([0, np.pi, 2*np.pi, 3*np.pi])
ax.set_xticklabels(['0', 'π', '2π', '3π'], fontsize=12)
ax.set_yticks([-1, 0, 1])
ax.set_yticklabels(['最小', '零', '最大'], fontsize=12)
ax.set_title('自定义刻度标签')

# 7. 多Y轴
ax = axes[2, 0]
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), 'b-', label='sin(x)')
ax.set_ylabel('sin(x)', color='blue')
ax2 = ax.twinx()
ax2.plot(x, x**2, 'r--', label='x²')
ax2.set_ylabel('x²', color='red')
ax.set_title('多Y轴图表')

# 8. 极坐标装饰
ax = axes[2, 1]
ax = plt.subplot(3, 3, 8, projection='polar')
theta = np.linspace(0, 2*np.pi, 100)
r = 1 + 0.5 * np.cos(5*theta)
ax.plot(theta, r, 'purple', linewidth=2)
ax.fill(theta, r, 'purple', alpha=0.3)
ax.set_title('极坐标装饰', pad=20)

# 9. 3D装饰
from mpl_toolkits.mplot3d import Axes3D
ax = axes[2, 2]
ax.remove()
ax = fig.add_subplot(3, 3, 9, projection='3d')
X = np.linspace(-5, 5, 50)
Y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(X, Y)
Z = np.sin(np.sqrt(X**2 + Y**2))
ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax.set_title('3D曲面装饰')

plt.suptitle('实用装饰技巧大全', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

装饰最佳实践

  1. 保持一致性:同一图表中,相似元素使用相同样式
  2. 突出重点:使用颜色、大小、透明度突出关键信息
  3. 适度装饰:装饰服务于内容,不要过度装饰
  4. 考虑色盲:避免红绿搭配,使用颜色盲友好调色板
  5. 测试输出:在不同设备、不同尺寸下测试显示效果
  6. 性能优化:复杂装饰可能影响渲染性能,适时优化

通过合理使用这些装饰技巧,可以让你的图表更具表现力和专业性。