Matplotlib的颜色

一、颜色表示方法

1. 基本颜色表示方式

import matplotlib.pyplot as plt
import numpy as np

# 八种基本颜色字符
basic_colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']
# 对应:蓝(blue)、绿(green)、红(red)、青(cyan)、品红(magenta)、黄(yellow)、黑(black)、白(white)

# 使用示例
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 2*np.pi, 100)

for i, color in enumerate(basic_colors):
    y = np.sin(x + i*0.5)
    ax.plot(x, y, color=color, linewidth=2, label=f"'{color}'")

ax.legend()
ax.set_title('Matplotlib 基本颜色字符', fontsize=14)
plt.show()

2. 扩展颜色名称

# 查看所有命名颜色
import matplotlib.colors as mcolors

# 所有命名颜色(140+种)
named_colors = list(mcolors.CSS4_COLORS.keys())
print(f"CSS4 颜色数量: {len(named_colors)}")
print("前20个颜色:", named_colors[:20])

# XKCD 颜色调查颜色(954种)
xkcd_colors = list(mcolors.XKCD_COLORS.keys())
print(f"\nXKCD 颜色数量: {len(xkcd_colors)}")

# 使用示例
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 常用CSS4颜色
common_colors = ['red', 'blue', 'green', 'orange', 'purple', 
                 'brown', 'pink', 'gray', 'olive', 'cyan', 
                 'navy', 'teal', 'maroon', 'lime', 'gold']

for i, ax in enumerate(axes.flat):
    for j in range(5):
        idx = i*5 + j
        if idx < len(common_colors):
            color_name = common_colors[idx]
            ax.bar(j, 1, color=color_name, edgecolor='black')
            ax.text(j, 0.5, color_name, ha='center', va='center', 
                   fontsize=9, rotation=90, color='white')
    ax.set_ylim(0, 1.2)
    ax.axis('off')
    ax.set_title(f'颜色组 {i+1}')

plt.suptitle('CSS4 命名颜色示例', fontsize=16)
plt.tight_layout()
plt.show()

3. 十六进制颜色

# 十六进制颜色表示
hex_colors = [
    '#FF5733',    # 橙红色
    '#33FF57',    # 亮绿色
    '#3357FF',    # 蓝色
    '#F0E68C',    # 卡其色
    '#8A2BE2',    # 蓝紫色
    '#FF1493',    # 深粉色
    '#00FFFF',    # 青色
    '#FFD700',    # 金色
]

# 创建渐变色系
def generate_gradient(start_hex, end_hex, n=10):
    """生成两种颜色之间的渐变"""
    start = mcolors.hex2color(start_hex)
    end = mcolors.hex2color(end_hex)
    return [mcolors.to_hex([(1-i/(n-1))*start[j] + (i/(n-1))*end[j] 
                           for j in range(3)]) 
            for i in range(n)]

# 示例:蓝色到红色的渐变
blue_to_red = generate_gradient('#0000FF', '#FF0000', 8)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

# 显示十六进制颜色
for i, color in enumerate(hex_colors):
    ax1.bar(i, 1, color=color, edgecolor='black', width=0.8)
    ax1.text(i, 0.5, color, ha='center', va='center', 
            fontsize=10, rotation=90, color='white')
ax1.set_title('十六进制颜色示例', fontsize=14)
ax1.set_ylim(0, 1.2)
ax1.axis('off')

# 显示渐变色
for i, color in enumerate(blue_to_red):
    ax2.bar(i, 1, color=color, edgecolor='black', width=0.8)
    ax2.text(i, 0.5, f'{i+1}', ha='center', va='center', 
            fontsize=10, color='white')
ax2.set_title('蓝色到红色渐变', fontsize=14)
ax2.set_ylim(0, 1.2)
ax2.axis('off')

plt.tight_layout()
plt.show()

4. RGB/RGBA 颜色

# RGB: 0-1之间的浮点数或0-255之间的整数
rgb_colors = [
    (1.0, 0.0, 0.0),        # 纯红 (浮点数)
    (0.0, 1.0, 0.0),        # 纯绿
    (0.0, 0.0, 1.0),        # 纯蓝
    (0.5, 0.5, 0.0),        # 橄榄色
    
    # 整数表示 (0-255)
    (255, 0, 0),            # 纯红
    (0, 255, 0),            # 纯绿
    (0, 0, 255),            # 纯蓝
]

# RGBA: 包含透明度 (Alpha)
rgba_colors = [
    (1.0, 0.0, 0.0, 0.5),    # 半透明红色
    (0.0, 1.0, 0.0, 0.3),    # 30%透明绿色
    (0.0, 0.0, 1.0, 0.8),    # 80%透明蓝色
]

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

# RGB颜色示例
ax = axes[0, 0]
for i, color in enumerate(rgb_colors[:4]):
    ax.bar(i, 1, color=color, edgecolor='black')
    ax.text(i, 0.5, f'RGB{color}', ha='center', va='center', 
           fontsize=10, color='white')
ax.set_title('RGB 浮点数颜色', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

# RGBA颜色示例
ax = axes[0, 1]
for i, color in enumerate(rgba_colors):
    ax.bar(i, 1, color=color, edgecolor='black')
    alpha = color[3] if len(color) == 4 else 1.0
    ax.text(i, 0.5, f'α={alpha}', ha='center', va='center', 
           fontsize=10, color='black')
ax.set_title('RGBA 颜色(带透明度)', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

# HSV/HSL颜色空间
ax = axes[1, 0]
hues = np.linspace(0, 1, 12)  # 色相
for i, hue in enumerate(hues):
    # HSV 转 RGB
    rgb_color = mcolors.hsv_to_rgb([hue, 0.8, 0.8])
    ax.bar(i, 1, color=rgb_color, edgecolor='black')
ax.set_title('HSV 色相变化', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

# 灰度颜色
ax = axes[1, 1]
grays = np.linspace(0, 1, 10)  # 0=黑, 1=白
for i, gray in enumerate(grays):
    ax.bar(i, 1, color=str(gray), edgecolor='black')
    ax.text(i, 0.5, f'{gray:.1f}', ha='center', va='center', 
           fontsize=10, color='white' if gray < 0.5 else 'black')
ax.set_title('灰度颜色', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

plt.suptitle('RGB/RGBA 颜色表示', fontsize=16)
plt.tight_layout()
plt.show()

二、颜色映射(Colormap)系统

1. 查看所有颜色映射

import matplotlib.pyplot as plt
import numpy as np

# 获取所有颜色映射
cmaps = plt.colormaps()
print(f"Matplotlib 内置颜色映射数量: {len(cmaps)}")

# 分类显示
categories = {
    'Perceptually Uniform Sequential': ['viridis', 'plasma', 'inferno', 'magma', 'cividis'],
    'Sequential': ['Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
                   'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
                   'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn'],
    'Sequential (2)': ['binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
                       'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
                       'hot', 'afmhot', 'gist_heat', 'copper'],
    'Diverging': ['PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
                  'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic'],
    'Cyclic': ['twilight', 'twilight_shifted', 'hsv'],
    'Qualitative': ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                    'Dark2', 'Set1', 'Set2', 'Set3',
                    'tab10', 'tab20', 'tab20b', 'tab20c'],
    'Miscellaneous': ['flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
                      'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
                      'gist_rainbow', 'rainbow', 'jet', 'turbo', 'nipy_spectral',
                      'gist_ncar']
}

# 显示部分重要颜色映射
fig, axes = plt.subplots(6, 6, figsize=(15, 15))
axes = axes.flatten()

important_cmaps = [
    'viridis', 'plasma', 'inferno', 'magma', 'cividis',  # 感知均匀
    'Greys', 'Blues', 'Reds', 'Greens', 'Oranges',      # 顺序色
    'coolwarm', 'RdBu', 'Spectral', 'PiYG', 'seismic',  # 发散色
    'Set1', 'Set2', 'Set3', 'tab10', 'tab20',           # 分类色
    'rainbow', 'hsv', 'jet', 'turbo', 'gist_rainbow',   # 彩虹色
    'bone', 'pink', 'summer', 'winter', 'autumn',       # 杂项
    'terrain', 'ocean', 'gist_earth', 'gist_stern',     # 地理相关
    'twilight', 'prism', 'flag', 'Paired', 'Accent'     # 特殊用途
]

for idx, (ax, cmap_name) in enumerate(zip(axes, important_cmaps)):
    # 创建渐变色条
    gradient = np.linspace(0, 1, 256).reshape(1, -1)
    gradient = np.vstack((gradient, gradient))
    
    ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(cmap_name))
    ax.set_title(cmap_name, fontsize=9)
    ax.axis('off')

# 隐藏多余的子图
for i in range(len(important_cmaps), len(axes)):
    axes[i].axis('off')

plt.suptitle('重要颜色映射预览', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

2. 颜色映射分类详解

(1) 顺序颜色映射(Sequential)
# 适用于表示从低到高的数据
sequential_cmaps = ['viridis', 'plasma', 'summer', 'winter', 'Blues', 'Reds', 'Greens']

fig, axes = plt.subplots(2, 4, figsize=(15, 8))
axes = axes.flatten()

# 创建示例数据
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)

for idx, cmap_name in enumerate(sequential_cmaps[:8]):
    ax = axes[idx]
    im = ax.imshow(Z, cmap=cmap_name, extent=[0, 10, 0, 10])
    ax.set_title(f'{cmap_name}\n(顺序色)', fontsize=11)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    plt.colorbar(im, ax=ax, shrink=0.8)

# 隐藏多余的子图
for i in range(len(sequential_cmaps[:8]), 8):
    axes[i].axis('off')

plt.suptitle('顺序颜色映射示例', fontsize=16, y=0.98)
plt.tight_layout()
plt.show()
(2) 发散颜色映射(Diverging)
# 适用于有中心值的数据(如温度异常、相关性)
diverging_cmaps = ['coolwarm', 'RdBu', 'RdYlBu', 'Spectral', 'PiYG', 'seismic', 'bwr']

fig, axes = plt.subplots(2, 4, figsize=(15, 8))
axes = axes.flatten()

# 创建发散数据(有正负值)
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = X**2 - Y**2  # 有正有负的数据

for idx, cmap_name in enumerate(diverging_cmaps[:8]):
    ax = axes[idx]
    im = ax.imshow(Z, cmap=cmap_name, extent=[-5, 5, -5, 5])
    ax.set_title(f'{cmap_name}\n(发散色)', fontsize=11)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    
    # 添加0值线
    ax.contour(X, Y, Z, levels=[0], colors='black', linewidths=0.5)
    
    plt.colorbar(im, ax=ax, shrink=0.8)

# 隐藏多余的子图
for i in range(len(diverging_cmaps[:8]), 8):
    axes[i].axis('off')

plt.suptitle('发散颜色映射示例', fontsize=16, y=0.98)
plt.tight_layout()
plt.show()
(3) 分类颜色映射(Qualitative/Categorical)
# 适用于分类数据,颜色差异明显
qualitative_cmaps = ['tab10', 'tab20', 'Set1', 'Set2', 'Set3', 'Pastel1', 'Pastel2', 'Dark2']

fig, axes = plt.subplots(2, 4, figsize=(15, 8))
axes = axes.flatten()

# 创建分类数据
categories = 10  # 10个类别
data = np.random.rand(10, 10)
labels = np.random.randint(0, categories, (10, 10))

for idx, cmap_name in enumerate(qualitative_cmaps[:8]):
    ax = axes[idx]
    cmap = plt.get_cmap(cmap_name)
    
    # 显示颜色条
    colors = cmap(np.arange(cmap.N))
    ax.imshow([colors], aspect='auto', extent=[0, cmap.N, 0, 1])
    
    ax.set_title(f'{cmap_name}\n({cmap.N}种颜色)', fontsize=11)
    ax.set_xlabel('颜色索引')
    ax.set_yticks([])
    ax.set_xlim(0, cmap.N)

plt.suptitle('分类颜色映射示例', fontsize=16, y=0.98)
plt.tight_layout()
plt.show()

3. 颜色映射的应用

# 实际应用示例
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 1. 热图(顺序色)
ax = axes[0, 0]
data = np.random.rand(10, 10)
im = ax.imshow(data, cmap='viridis')
ax.set_title('热图 - viridis (顺序色)', fontsize=12)
plt.colorbar(im, ax=ax)

# 2. 相关性矩阵(发散色)
ax = axes[0, 1]
corr_matrix = np.random.randn(5, 5)
corr_matrix = np.corrcoef(corr_matrix)
im = ax.imshow(corr_matrix, cmap='coolwarm', vmin=-1, vmax=1)
ax.set_title('相关性矩阵 - coolwarm (发散色)', fontsize=12)
plt.colorbar(im, ax=ax)

# 3. 分类散点图(分类色)
ax = axes[0, 2]
np.random.seed(42)
n_points = 100
categories = np.random.randint(0, 10, n_points)
x = np.random.randn(n_points)
y = np.random.randn(n_points)
scatter = ax.scatter(x, y, c=categories, cmap='tab10', s=50, alpha=0.7)
ax.set_title('分类散点图 - tab10 (分类色)', fontsize=12)
plt.colorbar(scatter, ax=ax)

# 4. 地形图(地理色)
ax = axes[1, 0]
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X**2 + Y**2)
im = ax.imshow(Z, cmap='terrain', extent=[-3, 3, -3, 3])
ax.set_title('地形图 - terrain (地理色)', fontsize=12)
plt.colorbar(im, ax=ax)

# 5. 相位图(循环色)
ax = axes[1, 1]
x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(0, 2*np.pi, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
im = ax.imshow(Z, cmap='twilight', extent=[0, 2*np.pi, 0, 2*np.pi])
ax.set_title('相位图 - twilight (循环色)', fontsize=12)
plt.colorbar(im, ax=ax)

# 6. 3D曲面(彩虹色)
from mpl_toolkits.mplot3d import Axes3D
ax = axes[1, 2]
ax.remove()
ax = fig.add_subplot(2, 3, 6, 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))

surf = ax.plot_surface(X, Y, Z, cmap='rainbow', alpha=0.8)
ax.set_title('3D曲面 - rainbow (彩虹色)', fontsize=12)
fig.colorbar(surf, ax=ax, shrink=0.6)

plt.suptitle('颜色映射实际应用示例', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

三、自定义颜色映射

1. 从颜色列表创建

from matplotlib.colors import LinearSegmentedColormap, ListedColormap

# 方法1:从颜色列表创建线性颜色映射
colors_list = ['#FF0000', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF']
custom_cmap1 = LinearSegmentedColormap.from_list('rainbow_custom', colors_list, N=256)

# 方法2:创建分类颜色映射
categorical_colors = ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00']
custom_cmap2 = ListedColormap(categorical_colors, name='categorical_custom')

# 方法3:使用颜色字典
cdict = {
    'red':   [(0.0,  0.0, 0.0),
              (0.5,  1.0, 1.0),
              (1.0,  1.0, 1.0)],
    
    '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_cmap3 = LinearSegmentedColormap('Custom3', cdict, 256)

# 显示自定义颜色映射
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

custom_cmaps = [custom_cmap1, custom_cmap2, custom_cmap3]
names = ['彩虹渐变', '分类颜色', '红绿蓝渐变']

# 显示颜色条
for i, (cmap, name) in enumerate(zip(custom_cmaps, names)):
    ax = axes[0, i]
    gradient = np.linspace(0, 1, 256).reshape(1, -1)
    gradient = np.vstack((gradient, gradient))
    ax.imshow(gradient, aspect='auto', cmap=cmap)
    ax.set_title(f'{name}', fontsize=14)
    ax.axis('off')

# 应用示例
data = np.random.rand(10, 10)
for i, (cmap, name) in enumerate(zip(custom_cmaps, names)):
    ax = axes[1, i]
    im = ax.imshow(data, cmap=cmap)
    ax.set_title(f'应用: {name}', fontsize=12)
    ax.axis('off')
    plt.colorbar(im, ax=ax, shrink=0.8)

plt.suptitle('自定义颜色映射', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

2. 高级自定义技巧

from matplotlib.colors import LinearSegmentedColormap, PowerNorm, LogNorm

# 创建发散型自定义颜色映射
def create_diverging_cmap(low_color, high_color, center_color='white'):
    """创建发散颜色映射"""
    colors = [low_color, center_color, high_color]
    return LinearSegmentedColormap.from_list('diverging_custom', colors, N=256)

# 创建多段颜色映射
def create_multi_segment_cmap(colors, positions=None):
    """创建多段颜色映射"""
    if positions is None:
        positions = np.linspace(0, 1, len(colors))
    
    cdict = {'red': [], 'green': [], 'blue': []}
    
    for pos, color in zip(positions, colors):
        r, g, b = mcolors.to_rgb(color)
        cdict['red'].append((pos, r, r))
        cdict['green'].append((pos, g, g))
        cdict['blue'].append((pos, b, b))
    
    return LinearSegmentedColormap('multi_segment', cdict, 256)

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

# 1. 发散颜色映射
cmap_div = create_diverging_cmap('#0000FF', '#FF0000', '#FFFFFF')
ax = axes[0, 0]
gradient = np.linspace(0, 1, 256).reshape(1, -1)
ax.imshow(gradient, aspect='auto', cmap=cmap_div)
ax.set_title('蓝-白-红发散色', fontsize=12)
ax.axis('off')

# 2. 多段颜色映射
colors_multi = ['#0000FF', '#00FFFF', '#00FF00', '#FFFF00', '#FF0000']
cmap_multi = create_multi_segment_cmap(colors_multi)
ax = axes[0, 1]
ax.imshow(gradient, aspect='auto', cmap=cmap_multi)
ax.set_title('多段渐变色', fontsize=12)
ax.axis('off')

# 3. 透明度渐变色映射
colors_with_alpha = [
    (1, 0, 0, 0.0),    # 完全透明红色
    (1, 0, 0, 0.5),    # 半透明红色
    (1, 0, 0, 1.0),    # 不透明红色
]
cmap_alpha = LinearSegmentedColormap.from_list('alpha_cmap', colors_with_alpha)
ax = axes[0, 2]
ax.imshow(gradient, aspect='auto', cmap=cmap_alpha)
ax.set_title('透明度渐变色', fontsize=12)
ax.axis('off')

# 4. 非线性标准化
ax = axes[1, 0]
data = np.random.lognormal(mean=1, sigma=1, size=(10, 10))
im = ax.imshow(data, cmap='viridis', norm=LogNorm())
ax.set_title('LogNorm 标准化', fontsize=12)
plt.colorbar(im, ax=ax)

# 5. 幂律标准化
ax = axes[1, 1]
data = np.random.rand(10, 10) ** 2
im = ax.imshow(data, cmap='plasma', norm=PowerNorm(gamma=0.5))
ax.set_title('PowerNorm (gamma=0.5)', fontsize=12)
plt.colorbar(im, ax=ax)

# 6. 离散化颜色映射
from matplotlib.colors import BoundaryNorm
ax = axes[1, 2]
data = np.random.randn(10, 10)
# 定义边界和颜色
bounds = [-3, -2, -1, 0, 1, 2, 3]
norm = BoundaryNorm(bounds, ncolors=256)
im = ax.imshow(data, cmap='RdBu', norm=norm)
ax.set_title('BoundaryNorm 离散化', fontsize=12)
plt.colorbar(im, ax=ax, boundaries=bounds)

plt.suptitle('高级颜色映射技巧', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

四、颜色盲友好调色板

1. 颜色盲类型与友好颜色

# 颜色盲友好颜色方案
colorblind_friendly = {
    '色盲通用': ['#377EB8', '#FF7F00', '#4DAF4A',  # 蓝、橙、绿
               '#F781BF', '#A65628', '#984EA3',  # 粉、棕、紫
               '#999999', '#E41A1C', '#DEDE00'], # 灰、红、黄绿
    
    '红绿色盲优化': ['#006BA4', '#FF800E', '#ABABAB',  # 深蓝、橙色、浅灰
                  '#595959', '#5F9ED1', '#C85200',  # 深灰、浅蓝、深橙
                  '#898989', '#A2C8EC', '#FFBC79'], # 中灰、淡蓝、淡橙
    
    '蓝黄色盲优化': ['#E69F00', '#56B4E9', '#009E73',  # 橙、浅蓝、绿
                  '#F0E442', '#0072B2', '#D55E00',  # 黄绿、蓝、红棕
                  '#CC79A7', '#000000'],            # 紫、黑
}

# 颜色盲模拟函数
def simulate_colorblindness(color, deficiency_type='protanopia'):
    """
    模拟不同类型的色盲
    deficiency_type: 'protanopia'(红色盲), 
                    'deuteranopia'(绿色盲),
                    'tritanopia'(蓝色盲)
    """
    # 简化的色盲模拟矩阵(实际应用应使用更精确的转换)
    matrices = {
        'protanopia': np.array([[0.567, 0.433, 0],
                                [0.558, 0.442, 0],
                                [0, 0.242, 0.758]]),
        'deuteranopia': np.array([[0.625, 0.375, 0],
                                  [0.7, 0.3, 0],
                                  [0, 0.3, 0.7]]),
        'tritanopia': np.array([[0.95, 0.05, 0],
                                [0, 0.433, 0.567],
                                [0, 0.475, 0.525]])
    }
    
    rgb = np.array(mcolors.to_rgb(color))
    return mcolors.to_hex(matrices[deficiency_type] @ rgb)

fig, axes = plt.subplots(3, 4, figsize=(16, 12))

deficiency_types = ['正常', '红色盲', '绿色盲', '蓝色盲']
color_sets = ['色盲通用', '红绿色盲优化', '蓝黄色盲优化']

for row, (set_name, colors) in enumerate(color_sets.items()):
    for col, deficiency in enumerate(deficiency_types):
        ax = axes[row, col]
        
        for i, color in enumerate(colors):
            if deficiency == '正常':
                display_color = color
            else:
                display_color = simulate_colorblindness(color, deficiency)
            
            ax.bar(i, 1, color=display_color, edgecolor='black')
            ax.text(i, 0.5, str(i+1), ha='center', va='center', 
                   fontsize=10, color='white' if i not in [3, 7] else 'black')
        
        ax.set_ylim(0, 1.2)
        ax.set_title(f'{set_name}\n{deficiency}', fontsize=11)
        ax.axis('off')

plt.suptitle('颜色盲友好调色板', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

2. 实际应用建议

# 创建颜色盲友好的可视化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 1. 分类数据 - 使用Set2(颜色盲友好)
ax = axes[0, 0]
np.random.seed(42)
n_categories = 8
data = np.random.rand(5, n_categories)
x = np.arange(5)

for i in range(n_categories):
    ax.bar(x + i*0.1, data[:, i], width=0.1, 
           color=plt.cm.Set2(i/n_categories),
           label=f'类别 {i+1}')

ax.set_title('分类柱状图 - Set2', fontsize=12)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# 2. 顺序数据 - 使用viridis(颜色盲友好)
ax = axes[0, 1]
data = np.random.rand(10, 10)
im = ax.imshow(data, cmap='viridis')
ax.set_title('热图 - viridis', fontsize=12)
plt.colorbar(im, ax=ax)

# 3. 发散数据 - 使用coolwarm(颜色盲友好)
ax = axes[0, 2]
data = np.random.randn(10, 10)
im = ax.imshow(data, cmap='coolwarm', vmin=-2, vmax=2)
ax.set_title('发散数据 - coolwarm', fontsize=12)
plt.colorbar(im, ax=ax)

# 4. 散点图 - 使用颜色盲友好调色板
ax = axes[1, 0]
np.random.seed(42)
n_points = 50
categories = np.random.randint(0, 6, n_points)
x = np.random.randn(n_points)
y = np.random.randn(n_points)

# 使用颜色盲友好颜色
cb_friendly_colors = ['#377EB8', '#FF7F00', '#4DAF4A', 
                      '#F781BF', '#A65628', '#984EA3']

for i in range(6):
    mask = categories == i
    ax.scatter(x[mask], y[mask], color=cb_friendly_colors[i], 
               s=50, alpha=0.7, label=f'组{i+1}')

ax.set_title('散点图 - 颜色盲友好', fontsize=12)
ax.legend()

# 5. 折线图 - 使用线型和标记区分
ax = axes[1, 1]
x = np.linspace(0, 10, 100)
line_styles = ['-', '--', '-.', ':']
markers = ['o', 's', '^', 'D']

for i in range(4):
    y = np.sin(x + i*0.5)
    ax.plot(x, y, linestyle=line_styles[i], 
            marker=markers[i], markevery=10,
            color='black',  # 统一用黑色,用样式区分
            label=f'序列{i+1}')

ax.set_title('折线图 - 用样式区分', fontsize=12)
ax.legend()

# 6. 饼图 - 使用纹理填充
ax = axes[1, 2]
sizes = [30, 25, 20, 15, 10]
labels = ['A', 'B', 'C', 'D', 'E']

# 定义纹理模式
patterns = ['/', '\\', '|', '-', '+']
colors = ['gray'] * 5  # 统一颜色

wedges, texts = ax.pie(sizes, labels=labels, colors=colors)

# 添加纹理
for wedge, pattern in zip(wedges, patterns):
    wedge.set_hatch(pattern)

ax.set_title('饼图 - 用纹理区分', fontsize=12)

plt.suptitle('颜色盲友好可视化实践', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

五、颜色工具与实用函数

1. 颜色转换工具

import matplotlib.colors as mcolors

def demonstrate_color_conversions():
    """演示颜色转换"""
    
    # 测试颜色
    test_colors = [
        'red',              # 颜色名
        '#FF0000',          # 十六进制
        (1.0, 0.0, 0.0),    # RGB浮点
        (255, 0, 0),        # RGB整数
        (1.0, 0.0, 0.0, 0.5) # RGBA
    ]
    
    print("颜色转换演示:")
    print("="*60)
    
    for color in test_colors:
        print(f"\n输入: {color}")
        
        # 转换为RGB浮点
        try:
            rgb = mcolors.to_rgb(color)
            print(f"  → RGB浮点: {rgb}")
        except Exception as e:
            print(f"  → RGB转换错误: {e}")
        
        # 转换为RGBA浮点
        try:
            rgba = mcolors.to_rgba(color)
            print(f"  → RGBA浮点: {rgba}")
        except Exception as e:
            print(f"  → RGBA转换错误: {e}")
        
        # 转换为十六进制
        try:
            hex_color = mcolors.to_hex(color)
            print(f"  → 十六进制: {hex_color}")
        except Exception as e:
            print(f"  → 十六进制转换错误: {e}")
        
        # 转换为HSV
        try:
            hsv = mcolors.rgb_to_hsv(mcolors.to_rgb(color))
            print(f"  → HSV: {hsv}")
        except Exception as e:
            print(f"  → HSV转换错误: {e}")

# 运行演示
demonstrate_color_conversions()

# 创建颜色转换工具界面
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. 颜色拾取器模拟
ax = axes[0, 0]
# 创建颜色网格
colors_grid = np.random.rand(10, 10, 3)
ax.imshow(colors_grid)
ax.set_title('颜色拾取器', fontsize=12)
ax.axis('off')

# 添加点击事件(模拟)
ax.text(0.5, -0.1, '点击选择颜色 →', transform=ax.transAxes,
        ha='center', fontsize=10, style='italic')

# 2. 颜色梯度生成器
ax = axes[0, 1]
start_color = '#FF0000'
end_color = '#0000FF'
n_steps = 10

gradient_colors = []
for i in range(n_steps):
    t = i / (n_steps - 1)
    # 线性插值
    start_rgb = mcolors.to_rgb(start_color)
    end_rgb = mcolors.to_rgb(end_color)
    interp_color = [(1-t)*start_rgb[j] + t*end_rgb[j] for j in range(3)]
    gradient_colors.append(interp_color)

for i, color in enumerate(gradient_colors):
    ax.bar(i, 1, color=color, edgecolor='black')
    hex_color = mcolors.to_hex(color)
    ax.text(i, 0.5, hex_color, ha='center', va='center', 
           fontsize=8, rotation=90, color='white')

ax.set_title(f'渐变: {start_color} → {end_color}', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

# 3. 颜色调和
ax = axes[1, 0]
color1 = '#FF0000'
color2 = '#00FF00'
color3 = '#0000FF'

# 显示原色
ax.bar(0, 1, color=color1, edgecolor='black', label='红')
ax.bar(1, 1, color=color2, edgecolor='black', label='绿')
ax.bar(2, 1, color=color3, edgecolor='black', label='蓝')

# 显示混合色
mix1 = mcolors.to_hex([0.5, 0.5, 0])  # 红+绿=黄
mix2 = mcolors.to_hex([0.5, 0, 0.5])  # 红+蓝=紫
mix3 = mcolors.to_hex([0, 0.5, 0.5])  # 绿+蓝=青

ax.bar(4, 1, color=mix1, edgecolor='black', label='红+绿')
ax.bar(5, 1, color=mix2, edgecolor='black', label='红+蓝')
ax.bar(6, 1, color=mix3, edgecolor='black', label='绿+蓝')

ax.set_title('颜色调和', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)

# 4. 颜色亮度调整
ax = axes[1, 1]
base_color = '#FF0000'
base_rgb = mcolors.to_rgb(base_color)

# 创建不同亮度的颜色
for i in range(10):
    brightness = i / 9
    # 调整亮度
    adjusted_color = [c * brightness for c in base_rgb]
    ax.bar(i, 1, color=adjusted_color, edgecolor='black')
    ax.text(i, 0.5, f'{brightness:.1f}', ha='center', va='center', 
           fontsize=9, color='white' if brightness < 0.5 else 'black')

ax.set_title(f'亮度调整: {base_color}', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

plt.suptitle('颜色工具与实用函数', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

2. 自动配色方案生成

def generate_color_scheme(base_color, scheme_type='analogous', n_colors=5):
    """
    生成配色方案
    
    scheme_type:
    - 'analogous': 类似色(相邻色相)
    - 'complementary': 互补色
    - 'triadic': 三角色
    - 'tetradic': 矩形色
    - 'monochromatic': 单色调
    """
    
    # 转换为HSV
    base_hsv = mcolors.rgb_to_hsv(mcolors.to_rgb(base_color))
    h, s, v = base_hsv
    
    colors = []
    
    if scheme_type == 'analogous':
        # 类似色:基础色相±30度
        hues = [(h + i*30/360) % 1 for i in range(-(n_colors//2), n_colors//2 + 1)]
        colors = [mcolors.hsv_to_rgb([hue, s, v]) for hue in hues]
    
    elif scheme_type == 'complementary':
        # 互补色:基础色相±180度
        complementary_h = (h + 0.5) % 1
        colors = [mcolors.hsv_to_rgb([h, s, v]), 
                 mcolors.hsv_to_rgb([complementary_h, s, v])]
    
    elif scheme_type == 'triadic':
        # 三角色:基础色相±120度
        colors = [mcolors.hsv_to_rgb([(h + i/3) % 1, s, v]) for i in range(3)]
    
    elif scheme_type == 'tetradic':
        # 矩形色:基础色相±90度, ±180度
        colors = [mcolors.hsv_to_rgb([(h + i*0.25) % 1, s, v]) for i in range(4)]
    
    elif scheme_type == 'monochromatic':
        # 单色调:调整饱和度或亮度
        for i in range(n_colors):
            factor = i / (n_colors - 1)
            # 调整亮度
            new_v = v * (0.5 + 0.5*factor)
            colors.append(mcolors.hsv_to_rgb([h, s, new_v]))
    
    return [mcolors.to_hex(color) for color in colors]

# 演示配色方案生成
base_color = '#3498db'  # 蓝色
scheme_types = ['analogous', 'complementary', 'triadic', 'tetradic', 'monochromatic']

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

for idx, scheme_type in enumerate(scheme_types):
    ax = axes[idx]
    colors = generate_color_scheme(base_color, scheme_type, n_colors=5)
    
    for i, color in enumerate(colors):
        ax.bar(i, 1, color=color, edgecolor='black')
        ax.text(i, 0.5, color, ha='center', va='center', 
               fontsize=9, rotation=90, color='white')
    
    ax.set_title(f'{scheme_type.capitalize()} 配色方案\n基础色: {base_color}', 
                fontsize=12)
    ax.set_ylim(0, 1.2)
    ax.axis('off')

# 最后一个子图显示基础色
ax = axes[-1]
ax.bar(0, 1, color=base_color, edgecolor='black')
ax.text(0.5, 0.5, f'基础色\n{base_color}', 
        ha='center', va='center', fontsize=12, color='white')
ax.set_title('基础颜色', fontsize=12)
ax.set_ylim(0, 1.2)
ax.axis('off')

plt.suptitle('自动配色方案生成', fontsize=16, y=0.95)
plt.tight_layout()
plt.show()

颜色使用最佳实践

  1. 数据语义匹配
    • 顺序数据 → 顺序颜色映射(viridis, plasma等)
    • 分类数据 → 分类颜色映射(Set3, tab20等)
    • 发散数据 → 发散颜色映射(coolwarm, RdBu等)
  2. 可访问性考虑
    • 避免红绿搭配(色盲不友好)
    • 提供纹理/图案作为颜色替代
    • 测试颜色盲模拟效果
  3. 美学原则
    • 限制颜色数量(一般不超过6-8种)
    • 使用调色板而非随机颜色
    • 保持一致性(相同含义使用相同颜色)
  4. 性能优化
    • 对于大量数据,使用轻量级颜色映射
    • 避免不必要的颜色转换
    • 预计算颜色映射
  5. 导出考虑
    • 打印时使用CMYK安全颜色
    • 网页使用sRGB颜色空间
    • 考虑不同设备的颜色显示差异

通过合理使用Matplotlib的颜色系统,可以创建出既美观又实用的数据可视化图表。