chore: import zh skill scientific-visualization
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- Skill 名称:`scientific-visualization`
|
||||
- 中文类目:Python科学数据绘图
|
||||
- 上游仓库:`K-Dense-AI__scientific-agent-skills`
|
||||
- 上游路径:`skills/scientific-visualization/SKILL.md`
|
||||
- 上游链接:https://github.com/K-Dense-AI/scientific-agent-skills/blob/HEAD/skills/scientific-visualization/SKILL.md
|
||||
- 本仓库为 WeHub 中文 Skill 汉化包,基于 skill 市场筛选 Top200 清单整理
|
||||
- 原作者、版权和许可证信息以上游仓库为准
|
||||
@@ -0,0 +1,777 @@
|
||||
---
|
||||
name: scientific-visualization
|
||||
description: 用于制作达到发表质量图形的元技能。在需要创建期刊投稿图形时使用,要求包含多面板布局、显著性标注、误差线、色盲友好调色板以及特定期刊(Nature、Science、Cell)格式。使用 matplotlib/seaborn/plotly 配合发表样式进行编排。快速探索请直接使用 seaborn 或 plotly。
|
||||
license: MIT license
|
||||
metadata:
|
||||
version: "1.0"
|
||||
skill-author: K-Dense Inc.
|
||||
---
|
||||
|
||||
# Scientific Visualization
|
||||
|
||||
## 概述
|
||||
|
||||
科学可视化将数据转换为清晰、准确的发表级图形。创建具有多面板布局、误差线、显著性标记和色盲友好调色板的期刊就绪图表。使用 matplotlib、seaborn 和 plotly 导出为 PDF/EPS/TIFF 格式,用于学术手稿。
|
||||
|
||||
## 何时使用本技能
|
||||
|
||||
以下情况应使用本技能:
|
||||
- 为科学手稿创建图形或可视化
|
||||
- 为期刊投稿准备图表(Nature、Science、Cell、PLOS 等)
|
||||
- 确保图形色盲友好且无障碍可读
|
||||
- 制作风格一致的多面板图形
|
||||
- 以正确的分辨率和格式导出图形
|
||||
- 遵循特定的出版指南
|
||||
- 改进现有图形以符合发表标准
|
||||
- 创建需在彩色和灰度下均能清晰显示的图形
|
||||
|
||||
## 快速入门指南
|
||||
|
||||
### 基本发表质量图形
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 应用发表样式(来自 scripts/style_presets.py)
|
||||
from style_presets import apply_publication_style
|
||||
apply_publication_style('default')
|
||||
|
||||
# 创建合适尺寸的图形(单栏 = 3.5 英寸)
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# 绘制数据
|
||||
x = np.linspace(0, 10, 100)
|
||||
ax.plot(x, np.sin(x), label='sin(x)')
|
||||
ax.plot(x, np.cos(x), label='cos(x)')
|
||||
|
||||
# 正确标注单位
|
||||
ax.set_xlabel('Time (seconds)')
|
||||
ax.set_ylabel('Amplitude (mV)')
|
||||
ax.legend(frameon=False)
|
||||
|
||||
# 移除不必要的边框
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# 以发表格式保存(来自 figure_export.py)
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'figure1', formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
### 使用预配置样式
|
||||
|
||||
使用 `assets/` 中的 matplotlib 样式文件应用特定期刊样式:
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 选项 1:直接使用样式文件
|
||||
plt.style.use('assets/nature.mplstyle')
|
||||
|
||||
# 选项 2:使用 style_presets.py 辅助函数
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', figure_width='single')
|
||||
|
||||
# 现在创建图形——它们将自动符合 Nature 规格
|
||||
fig, ax = plt.subplots()
|
||||
# ... 你的绘图代码 ...
|
||||
```
|
||||
|
||||
### 使用 Seaborn 快速入门
|
||||
|
||||
对于统计图形,使用 seaborn 配合发表样式:
|
||||
|
||||
```python
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
from style_presets import apply_publication_style
|
||||
|
||||
# 应用发表样式
|
||||
apply_publication_style('default')
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
sns.set_palette('colorblind')
|
||||
|
||||
# 创建统计比较图
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
sns.boxplot(data=df, x='treatment', y='response',
|
||||
order=['Control', 'Low', 'High'], palette='Set2', ax=ax)
|
||||
sns.stripplot(data=df, x='treatment', y='response',
|
||||
order=['Control', 'Low', 'High'],
|
||||
color='black', alpha=0.3, size=3, ax=ax)
|
||||
ax.set_ylabel('Response (μM)')
|
||||
sns.despine()
|
||||
|
||||
# 保存图形
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'treatment_comparison', formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
## 核心原则与最佳实践
|
||||
|
||||
### 1. 分辨率与文件格式
|
||||
|
||||
**关键要求**(详见 `references/publication_guidelines.md`):
|
||||
- **栅格图像**(照片、显微镜):300-600 DPI
|
||||
- **线条图**(图表、曲线图):600-1200 DPI 或矢量格式
|
||||
- **矢量格式**(首选):PDF、EPS、SVG
|
||||
- **栅格格式**:TIFF、PNG(科学数据切勿使用 JPEG)
|
||||
|
||||
**实现方式:**
|
||||
```python
|
||||
# 使用 figure_export.py 脚本设置正确参数
|
||||
from figure_export import save_publication_figure
|
||||
|
||||
# 以多种格式保存,DPI 正确
|
||||
save_publication_figure(fig, 'myfigure', formats=['pdf', 'png'], dpi=300)
|
||||
|
||||
# 或按特定期刊要求保存
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', journal='nature', figure_type='combination')
|
||||
```
|
||||
|
||||
### 2. 颜色选择——色盲无障碍
|
||||
|
||||
**始终使用色盲友好调色板**(详见 `references/color_palettes.md`):
|
||||
|
||||
**推荐:Okabe-Ito 调色板**(所有类型的色盲均可区分):
|
||||
```python
|
||||
# 选项 1:使用 assets/color_palettes.py
|
||||
from color_palettes import OKABE_ITO_LIST, apply_palette
|
||||
apply_palette('okabe_ito')
|
||||
|
||||
# 选项 2:手动指定
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=okabe_ito)
|
||||
```
|
||||
|
||||
**对于热力图/连续数据:**
|
||||
- 使用感知均匀的颜色映射:`viridis`、`plasma`、`cividis`
|
||||
- 避免红-绿发散型映射(改用 `PuOr`、`RdBu`、`BrBG`)
|
||||
- 切勿使用 `jet` 或 `rainbow` 颜色映射
|
||||
|
||||
**始终在灰度模式下测试图形**,确保可读性。
|
||||
|
||||
### 3. 字体与文本
|
||||
|
||||
**字体指南**(详见 `references/publication_guidelines.md`):
|
||||
- 无衬线字体:Arial、Helvetica、Calibri
|
||||
- **最终印刷尺寸**下的最小字号:
|
||||
- 轴标签:7-9 pt
|
||||
- 刻度标签:6-8 pt
|
||||
- 面板标签:8-12 pt(粗体)
|
||||
- 标签使用句首大写:写 "Time (hours)" 而非 "TIME (HOURS)"
|
||||
- 始终在括号内包含单位
|
||||
|
||||
**实现方式:**
|
||||
```python
|
||||
# 全局设置字体
|
||||
import matplotlib as mpl
|
||||
mpl.rcParams['font.family'] = 'sans-serif'
|
||||
mpl.rcParams['font.sans-serif'] = ['Arial', 'Helvetica']
|
||||
mpl.rcParams['font.size'] = 8
|
||||
mpl.rcParams['axes.labelsize'] = 9
|
||||
mpl.rcParams['xtick.labelsize'] = 7
|
||||
mpl.rcParams['ytick.labelsize'] = 7
|
||||
```
|
||||
|
||||
### 4. 图形尺寸
|
||||
|
||||
**期刊特定宽度**(详见 `references/journal_requirements.md`):
|
||||
- **Nature**:单栏 89 mm,双栏 183 mm
|
||||
- **Science**:单栏 55 mm,双栏 175 mm
|
||||
- **Cell**:单栏 85 mm,双栏 178 mm
|
||||
|
||||
**检查图形尺寸合规性:**
|
||||
```python
|
||||
from figure_export import check_figure_size
|
||||
|
||||
fig = plt.figure(figsize=(3.5, 3)) # Nature 的 89 mm
|
||||
check_figure_size(fig, journal='nature')
|
||||
```
|
||||
|
||||
### 5. 多面板图形
|
||||
|
||||
**最佳实践:**
|
||||
- 使用粗体字母标注面板:**A**、**B**、**C**(大多数期刊用大写,Nature 用小写)
|
||||
- 所有面板保持风格一致
|
||||
- 尽可能沿边缘对齐面板
|
||||
- 面板之间保留适当的空白
|
||||
|
||||
**示例实现**(完整代码见 `references/matplotlib_examples.md`):
|
||||
```python
|
||||
from string import ascii_uppercase
|
||||
|
||||
fig = plt.figure(figsize=(7, 4))
|
||||
gs = fig.add_gridspec(2, 2, hspace=0.4, wspace=0.4)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax2 = fig.add_subplot(gs[0, 1])
|
||||
# ... 创建其他面板 ...
|
||||
|
||||
# 添加面板标签
|
||||
for i, ax in enumerate([ax1, ax2, ...]):
|
||||
ax.text(-0.15, 1.05, ascii_uppercase[i], transform=ax.transAxes,
|
||||
fontsize=10, fontweight='bold', va='top')
|
||||
```
|
||||
|
||||
## 常见任务
|
||||
|
||||
### 任务 1:创建可用于发表的线图
|
||||
|
||||
完整代码见 `references/matplotlib_examples.md` 示例 1。
|
||||
|
||||
**关键步骤:**
|
||||
1. 应用发表样式
|
||||
2. 为目标期刊设置合适的图形尺寸
|
||||
3. 使用色盲友好颜色
|
||||
4. 添加正确表示的误差线(SEM、SD 或 CI)
|
||||
5. 标注轴及单位
|
||||
6. 移除不必要的边框
|
||||
7. 以矢量格式保存
|
||||
|
||||
**使用 seaborn 自动计算置信区间:**
|
||||
```python
|
||||
import seaborn as sns
|
||||
fig, ax = plt.subplots(figsize=(5, 3))
|
||||
sns.lineplot(data=timeseries, x='time', y='measurement',
|
||||
hue='treatment', errorbar=('ci', 95),
|
||||
markers=True, ax=ax)
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Measurement (AU)')
|
||||
sns.despine()
|
||||
```
|
||||
|
||||
### 任务 2:创建多面板图形
|
||||
|
||||
完整代码见 `references/matplotlib_examples.md` 示例 2。
|
||||
|
||||
**关键步骤:**
|
||||
1. 使用 `GridSpec` 实现灵活布局
|
||||
2. 确保所有面板风格一致
|
||||
3. 添加粗体面板标签(A、B、C 等)
|
||||
4. 对齐相关面板
|
||||
5. 验证所有文本在最终尺寸下可读
|
||||
|
||||
### 任务 3:创建使用正确颜色映射的热力图
|
||||
|
||||
完整代码见 `references/matplotlib_examples.md` 示例 4。
|
||||
|
||||
**关键步骤:**
|
||||
1. 使用感知均匀的颜色映射(`viridis`、`plasma`、`cividis`)
|
||||
2. 包含带标注的颜色条
|
||||
3. 对于发散型数据,使用色盲安全的发散映射(`RdBu_r`、`PuOr`)
|
||||
4. 为发散映射设置合适的中心值
|
||||
5. 测试灰度显示效果
|
||||
|
||||
**使用 seaborn 绘制相关矩阵:**
|
||||
```python
|
||||
import seaborn as sns
|
||||
fig, ax = plt.subplots(figsize=(5, 4))
|
||||
corr = df.corr()
|
||||
mask = np.triu(np.ones_like(corr, dtype=bool))
|
||||
sns.heatmap(corr, mask=mask, annot=True, fmt='.2f',
|
||||
cmap='RdBu_r', center=0, square=True,
|
||||
linewidths=1, cbar_kws={'shrink': 0.8}, ax=ax)
|
||||
```
|
||||
|
||||
### 任务 4:为特定期刊准备图形
|
||||
|
||||
**工作流程:**
|
||||
1. 查看期刊要求:`references/journal_requirements.md`
|
||||
2. 为期刊配置 matplotlib:
|
||||
```python
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', figure_width='single')
|
||||
```
|
||||
3. 创建图形(将自动调整尺寸)
|
||||
4. 按期刊规格导出:
|
||||
```python
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', journal='nature', figure_type='line_art')
|
||||
```
|
||||
|
||||
### 任务 5:修复现有图形使其符合发表标准
|
||||
|
||||
**清单式方法**(完整清单见 `references/publication_guidelines.md`):
|
||||
|
||||
1. **检查分辨率**:确认 DPI 符合期刊要求
|
||||
2. **检查文件格式**:图形用矢量格式,图像用 TIFF/PNG
|
||||
3. **检查颜色**:确保色盲友好
|
||||
4. **检查字体**:最终尺寸下最小 6-7 pt,无衬线字体
|
||||
5. **检查标签**:所有轴标注单位
|
||||
6. **检查尺寸**:匹配期刊栏宽
|
||||
7. **测试灰度**:无颜色也能解读图形
|
||||
8. **去除图表垃圾**:不要不必要的网格、3D 效果、阴影
|
||||
|
||||
### 任务 6:创建色盲友好的可视化
|
||||
|
||||
**策略:**
|
||||
1. 使用 `assets/color_palettes.py` 中的认可调色板
|
||||
2. 添加冗余编码(线型、标记、图案)
|
||||
3. 使用色盲模拟器测试
|
||||
4. 确保灰度兼容
|
||||
|
||||
**示例:**
|
||||
```python
|
||||
from color_palettes import apply_palette
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
apply_palette('okabe_ito')
|
||||
|
||||
# 在颜色之外添加冗余编码
|
||||
line_styles = ['-', '--', '-.', ':']
|
||||
markers = ['o', 's', '^', 'v']
|
||||
|
||||
for i, (data, label) in enumerate(datasets):
|
||||
plt.plot(x, data, linestyle=line_styles[i % 4],
|
||||
marker=markers[i % 4], label=label)
|
||||
```
|
||||
|
||||
## 统计严谨性
|
||||
|
||||
**始终包含:**
|
||||
- 误差线(SD、SEM 或 CI——在说明中注明具体类型)
|
||||
- 样本量(n)在图中或说明中
|
||||
- 统计显著性标记(*、**、***)
|
||||
- 尽可能显示个体数据点(而不仅仅是汇总统计量)
|
||||
|
||||
**含统计信息的示例:**
|
||||
```python
|
||||
# 显示个体数据点及汇总统计量
|
||||
ax.scatter(x_jittered, individual_points, alpha=0.4, s=8)
|
||||
ax.errorbar(x, means, yerr=sems, fmt='o', capsize=3)
|
||||
|
||||
# 标记显著性
|
||||
ax.text(1.5, max_y * 1.1, '***', ha='center', fontsize=8)
|
||||
```
|
||||
|
||||
## 使用不同的绘图库
|
||||
|
||||
### Matplotlib
|
||||
- 对发表细节的控制最全面
|
||||
- 最适合复杂的多面板图形
|
||||
- 使用提供的样式文件实现统一格式
|
||||
- 更多示例见 `references/matplotlib_examples.md`
|
||||
|
||||
### Seaborn
|
||||
|
||||
Seaborn 提供了一种高级的、面向数据集的统计图形接口,构建于 matplotlib 之上。它在使用最少代码创建发表质量的统计可视化方面表现出色,同时与 matplotlib 自定义功能完全兼容。
|
||||
|
||||
**科学可视化的主要优势:**
|
||||
- 自动统计估计和置信区间
|
||||
- 内置多面板图形支持(分面)
|
||||
- 默认使用色盲友好调色板
|
||||
- 基于 pandas DataFrame 的面向数据集 API
|
||||
- 变量到视觉属性的语义映射
|
||||
|
||||
#### 发表样式快速入门
|
||||
|
||||
始终先应用 matplotlib 发表样式,然后配置 seaborn:
|
||||
|
||||
```python
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
from style_presets import apply_publication_style
|
||||
|
||||
# 应用发表样式
|
||||
apply_publication_style('default')
|
||||
|
||||
# 为发表配置 seaborn
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
sns.set_palette('colorblind') # 使用色盲安全调色板
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
sns.scatterplot(data=df, x='time', y='response',
|
||||
hue='treatment', style='condition', ax=ax)
|
||||
sns.despine() # 移除顶部和右侧边框
|
||||
```
|
||||
|
||||
#### 用于发表的常见图形类型
|
||||
|
||||
**统计比较:**
|
||||
```python
|
||||
# 带个体数据点的箱线图,增加透明度
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
sns.boxplot(data=df, x='treatment', y='response',
|
||||
order=['Control', 'Low', 'High'], palette='Set2', ax=ax)
|
||||
sns.stripplot(data=df, x='treatment', y='response',
|
||||
order=['Control', 'Low', 'High'],
|
||||
color='black', alpha=0.3, size=3, ax=ax)
|
||||
ax.set_ylabel('Response (μM)')
|
||||
sns.despine()
|
||||
```
|
||||
|
||||
**分布分析:**
|
||||
```python
|
||||
# 带分组比较的提琴图
|
||||
fig, ax = plt.subplots(figsize=(4, 3))
|
||||
sns.violinplot(data=df, x='timepoint', y='expression',
|
||||
hue='treatment', split=True, inner='quartile', ax=ax)
|
||||
ax.set_ylabel('Gene Expression (AU)')
|
||||
sns.despine()
|
||||
```
|
||||
|
||||
**相关矩阵:**
|
||||
```python
|
||||
# 使用正确颜色映射和标注的热力图
|
||||
fig, ax = plt.subplots(figsize=(5, 4))
|
||||
corr = df.corr()
|
||||
mask = np.triu(np.ones_like(corr, dtype=bool)) # 仅显示下三角
|
||||
sns.heatmap(corr, mask=mask, annot=True, fmt='.2f',
|
||||
cmap='RdBu_r', center=0, square=True,
|
||||
linewidths=1, cbar_kws={'shrink': 0.8}, ax=ax)
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||
**带置信带的时序图:**
|
||||
```python
|
||||
# 自动计算置信区间的线图
|
||||
fig, ax = plt.subplots(figsize=(5, 3))
|
||||
sns.lineplot(data=timeseries, x='time', y='measurement',
|
||||
hue='treatment', style='replicate',
|
||||
errorbar=('ci', 95), markers=True, dashes=False, ax=ax)
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Measurement (AU)')
|
||||
sns.despine()
|
||||
```
|
||||
|
||||
#### 使用 Seaborn 创建多面板图形
|
||||
|
||||
**使用 FacetGrid 自动分面:**
|
||||
```python
|
||||
# 创建分面图
|
||||
g = sns.relplot(data=df, x='dose', y='response',
|
||||
hue='treatment', col='cell_line', row='timepoint',
|
||||
kind='line', height=2.5, aspect=1.2,
|
||||
errorbar=('ci', 95), markers=True)
|
||||
g.set_axis_labels('Dose (μM)', 'Response (AU)')
|
||||
g.set_titles('{row_name} | {col_name}')
|
||||
sns.despine()
|
||||
|
||||
# 以正确 DPI 保存
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(g.figure, 'figure_facets',
|
||||
formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
**将 seaborn 与 matplotlib 子图结合:**
|
||||
```python
|
||||
# 创建自定义多面板布局
|
||||
fig, axes = plt.subplots(2, 2, figsize=(7, 6))
|
||||
|
||||
# 面板 A:带回归的散点图
|
||||
sns.regplot(data=df, x='predictor', y='response', ax=axes[0, 0])
|
||||
axes[0, 0].text(-0.15, 1.05, 'A', transform=axes[0, 0].transAxes,
|
||||
fontsize=10, fontweight='bold')
|
||||
|
||||
# 面板 B:分布比较
|
||||
sns.violinplot(data=df, x='group', y='value', ax=axes[0, 1])
|
||||
axes[0, 1].text(-0.15, 1.05, 'B', transform=axes[0, 1].transAxes,
|
||||
fontsize=10, fontweight='bold')
|
||||
|
||||
# 面板 C:热力图
|
||||
sns.heatmap(correlation_data, cmap='viridis', ax=axes[1, 0])
|
||||
axes[1, 0].text(-0.15, 1.05, 'C', transform=axes[1, 0].transAxes,
|
||||
fontsize=10, fontweight='bold')
|
||||
|
||||
# 面板 D:时序图
|
||||
sns.lineplot(data=timeseries, x='time', y='signal',
|
||||
hue='condition', ax=axes[1, 1])
|
||||
axes[1, 1].text(-0.15, 1.05, 'D', transform=axes[1, 1].transAxes,
|
||||
fontsize=10, fontweight='bold')
|
||||
|
||||
plt.tight_layout()
|
||||
sns.despine()
|
||||
```
|
||||
|
||||
#### 发表用配色方案
|
||||
|
||||
Seaborn 包含多个色盲安全调色板:
|
||||
|
||||
```python
|
||||
# 使用内置色盲调色板(推荐)
|
||||
sns.set_palette('colorblind')
|
||||
|
||||
# 或指定自定义色盲安全颜色(Okabe-Ito)
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
sns.set_palette(okabe_ito)
|
||||
|
||||
# 用于热力图和连续数据
|
||||
sns.heatmap(data, cmap='viridis') # 感知均匀
|
||||
sns.heatmap(corr, cmap='RdBu_r', center=0) # 发散型,居中
|
||||
```
|
||||
|
||||
#### 在轴级与图级函数之间选择
|
||||
|
||||
**轴级函数**(如 `scatterplot`、`boxplot`、`heatmap`):
|
||||
- 在构建自定义多面板布局时使用
|
||||
- 接受 `ax=` 参数进行精确定位
|
||||
- 与 matplotlib 子图集成更好
|
||||
- 对图形组成有更多控制
|
||||
|
||||
```python
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
sns.scatterplot(data=df, x='x', y='y', hue='group', ax=ax)
|
||||
```
|
||||
|
||||
**图级函数**(如 `relplot`、`catplot`、`displot`):
|
||||
- 用于按分类变量自动分面
|
||||
- 创建风格一致的完整图形
|
||||
- 非常适合探索性分析
|
||||
- 使用 `height` 和 `aspect` 控制尺寸
|
||||
|
||||
```python
|
||||
g = sns.relplot(data=df, x='x', y='y', col='category', kind='scatter')
|
||||
```
|
||||
|
||||
#### 使用 Seaborn 实现统计严谨性
|
||||
|
||||
Seaborn 自动计算并显示不确定性:
|
||||
|
||||
```python
|
||||
# 线图:默认显示均值 ± 95% CI
|
||||
sns.lineplot(data=df, x='time', y='value', hue='treatment',
|
||||
errorbar=('ci', 95)) # 可改为 'sd'、'se' 等
|
||||
|
||||
# 条形图:显示均值及自助法 CI
|
||||
sns.barplot(data=df, x='treatment', y='response',
|
||||
errorbar=('ci', 95), capsize=0.1)
|
||||
|
||||
# 始终在图注中注明误差类型:
|
||||
# "误差线表示 95% 置信区间"
|
||||
```
|
||||
|
||||
#### 发表级 Seaborn 图形的最佳实践
|
||||
|
||||
1. **始终先设置发表主题:**
|
||||
```python
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
```
|
||||
|
||||
2. **使用色盲安全调色板:**
|
||||
```python
|
||||
sns.set_palette('colorblind')
|
||||
```
|
||||
|
||||
3. **移除不必要的元素:**
|
||||
```python
|
||||
sns.despine() # 移除顶部和右侧边框
|
||||
```
|
||||
|
||||
4. **适当控制图形尺寸:**
|
||||
```python
|
||||
# 轴级:使用 matplotlib figsize
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# 图级:使用 height 和 aspect
|
||||
g = sns.relplot(..., height=3, aspect=1.2)
|
||||
```
|
||||
|
||||
5. **尽可能显示个体数据点:**
|
||||
```python
|
||||
sns.boxplot(...) # 汇总统计
|
||||
sns.stripplot(..., alpha=0.3) # 个体数据点
|
||||
```
|
||||
|
||||
6. **包含带单位的正确标签:**
|
||||
```python
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Expression (AU)')
|
||||
```
|
||||
|
||||
7. **以正确分辨率导出:**
|
||||
```python
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'figure_name',
|
||||
formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
#### 高级 Seaborn 技巧
|
||||
|
||||
**探索性分析的成对关系图:**
|
||||
```python
|
||||
# 快速查看所有关系
|
||||
g = sns.pairplot(data=df, hue='condition',
|
||||
vars=['gene1', 'gene2', 'gene3'],
|
||||
corner=True, diag_kind='kde', height=2)
|
||||
```
|
||||
|
||||
**层次聚类热力图:**
|
||||
```python
|
||||
# 对样本和特征进行聚类
|
||||
g = sns.clustermap(expression_data, method='ward',
|
||||
metric='euclidean', z_score=0,
|
||||
cmap='RdBu_r', center=0,
|
||||
figsize=(10, 8),
|
||||
row_colors=condition_colors,
|
||||
cbar_kws={'label': 'Z-score'})
|
||||
```
|
||||
|
||||
**带边缘分布的联合图:**
|
||||
```python
|
||||
# 双变量分布及上下文
|
||||
g = sns.jointplot(data=df, x='gene1', y='gene2',
|
||||
hue='treatment', kind='scatter',
|
||||
height=6, ratio=4, marginal_kws={'kde': True})
|
||||
```
|
||||
|
||||
#### 常见 Seaborn 问题及解决方案
|
||||
|
||||
**问题:图例超出绘图区域**
|
||||
```python
|
||||
g = sns.relplot(...)
|
||||
g._legend.set_bbox_to_anchor((0.9, 0.5))
|
||||
```
|
||||
|
||||
**问题:标签重叠**
|
||||
```python
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||
**问题:最终尺寸下文字过小**
|
||||
```python
|
||||
sns.set_context('paper', font_scale=1.2) # 根据需要增大
|
||||
```
|
||||
|
||||
#### 其他资源
|
||||
|
||||
更多 seaborn 详细信息,请参见:
|
||||
- `skills/seaborn/SKILL.md` —— 全面的 seaborn 文档
|
||||
- `skills/seaborn/references/examples.md` —— 实际用例
|
||||
- `skills/seaborn/references/function_reference.md` —— 完整 API 参考
|
||||
- `skills/seaborn/references/objects_interface.md` —— 现代声明式 API
|
||||
|
||||
### Plotly
|
||||
- 交互式图形用于探索
|
||||
- 导出静态图像用于发表
|
||||
- 配置为发表质量:
|
||||
```python
|
||||
fig.update_layout(
|
||||
font=dict(family='Arial, sans-serif', size=10),
|
||||
plot_bgcolor='white',
|
||||
# ... 详见 matplotlib_examples.md 示例 8
|
||||
)
|
||||
fig.write_image('figure.png', scale=3) # scale=3 约等于 300 DPI
|
||||
```
|
||||
|
||||
## 资源
|
||||
|
||||
### 参考文档目录
|
||||
|
||||
**根据需要加载以下文件获取详细信息:**
|
||||
|
||||
- **`publication_guidelines.md`**:综合最佳实践
|
||||
- 分辨率和文件格式要求
|
||||
- 排版指南
|
||||
- 布局与构图规则
|
||||
- 统计严谨性要求
|
||||
- 完整发表检查清单
|
||||
|
||||
- **`color_palettes.md`**:颜色使用指南
|
||||
- 色盲友好调色板规格及 RGB 值
|
||||
- 顺序型和发散型颜色映射推荐
|
||||
- 无障碍测试流程
|
||||
- 领域特定调色板(基因组学、显微镜)
|
||||
|
||||
- **`journal_requirements.md`**:期刊特定规格
|
||||
- 各出版社的技术要求
|
||||
- 文件格式和 DPI 规格
|
||||
- 图形尺寸要求
|
||||
- 快速参考表
|
||||
|
||||
- **`matplotlib_examples.md`**:实用代码示例
|
||||
- 10 个完整的工作示例
|
||||
- 线图、条形图、热力图、多面板图形
|
||||
- 期刊特定图形示例
|
||||
- 各库(matplotlib、seaborn、plotly)的使用技巧
|
||||
|
||||
### 脚本目录
|
||||
|
||||
**使用以下辅助脚本实现自动化:**
|
||||
|
||||
- **`figure_export.py`**:导出工具
|
||||
- `save_publication_figure()`:以多种格式保存,DPI 正确
|
||||
- `save_for_journal()`:自动应用期刊特定要求
|
||||
- `check_figure_size()`:验证尺寸是否符合期刊规格
|
||||
- 直接运行:`python scripts/figure_export.py` 查看示例
|
||||
|
||||
- **`style_presets.py`**:预配置样式
|
||||
- `apply_publication_style()`:应用预设样式(default、nature、science、cell)
|
||||
- `set_color_palette()`:快速切换调色板
|
||||
- `configure_for_journal()`:一键期刊配置
|
||||
- 直接运行:`python scripts/style_presets.py` 查看示例
|
||||
|
||||
### 资源文件目录
|
||||
|
||||
**在图形中使用以下文件:**
|
||||
|
||||
- **`color_palettes.py`**:可导入的颜色定义
|
||||
- 所有推荐调色板作为 Python 常量
|
||||
- `apply_palette()` 辅助函数
|
||||
- 可直接导入到笔记本/脚本中
|
||||
|
||||
- **Matplotlib 样式文件**:使用 `plt.style.use()` 加载
|
||||
- `publication.mplstyle`:通用发表质量
|
||||
- `nature.mplstyle`:Nature 期刊规格
|
||||
- `presentation.mplstyle`:海报/幻灯片用大字体
|
||||
|
||||
## 工作流程总结
|
||||
|
||||
**创建发表级图形的推荐工作流程:**
|
||||
|
||||
1. **规划**:确定目标期刊、图形类型和内容
|
||||
2. **配置**:为期刊应用合适的样式
|
||||
```python
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', 'single')
|
||||
```
|
||||
3. **创建**:使用正确的标签、颜色和统计信息构建图形
|
||||
4. **验证**:检查尺寸、字体、颜色和无障碍性
|
||||
```python
|
||||
from figure_export import check_figure_size
|
||||
check_figure_size(fig, journal='nature')
|
||||
```
|
||||
5. **导出**:以所需格式保存
|
||||
```python
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', 'nature', 'combination')
|
||||
```
|
||||
6. **审阅**:在手稿上下文中以最终尺寸查看
|
||||
|
||||
## 应避免的常见陷阱
|
||||
|
||||
1. **字体过小**:以最终尺寸打印时文字不可读
|
||||
2. **JPEG 格式**:切勿在图表中使用 JPEG(会产生伪影)
|
||||
3. **红-绿配色**:约 8% 的男性无法区分
|
||||
4. **低分辨率**:发表时出现像素化图形
|
||||
5. **缺少单位**:始终为轴标注单位
|
||||
6. **3D 效果**:扭曲感知,应完全避免
|
||||
7. **图表垃圾**:移除不必要的网格线和装饰
|
||||
8. **截断坐标轴**:条形图从零开始,除非有科学依据
|
||||
9. **风格不一致**:同一手稿中不同图形使用不同字体/颜色
|
||||
10. **没有误差线**:始终显示不确定性
|
||||
|
||||
## 最终检查清单
|
||||
|
||||
在提交图形之前,请确认:
|
||||
|
||||
- [ ] 分辨率符合期刊要求(300+ DPI)
|
||||
- [ ] 文件格式正确(图形用矢量,图像用 TIFF)
|
||||
- [ ] 图形尺寸符合期刊规格
|
||||
- [ ] 所有文字在最终尺寸下可读(≥6 pt)
|
||||
- [ ] 颜色色盲友好
|
||||
- [ ] 图形在灰度下可用
|
||||
- [ ] 所有轴已标注单位
|
||||
- [ ] 误差线已标注且说明中已定义
|
||||
- [ ] 面板标签存在且一致
|
||||
- [ ] 无图表垃圾或 3D 效果
|
||||
- [ ] 所有图形字体一致
|
||||
- [ ] 统计显著性已清晰标记
|
||||
- [ ] 图例清晰完整
|
||||
|
||||
使用本技能,确保科学图形达到最高发表标准,同时对所有读者保持无障碍可读。
|
||||
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
Colorblind-Friendly Color Palettes for Scientific Visualization
|
||||
|
||||
This module provides carefully curated color palettes optimized for
|
||||
scientific publications and accessibility.
|
||||
|
||||
Usage:
|
||||
from color_palettes import OKABE_ITO, apply_palette
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
apply_palette('okabe_ito')
|
||||
plt.plot([1, 2, 3], [1, 4, 9])
|
||||
"""
|
||||
|
||||
# Okabe-Ito Palette (2008)
|
||||
# The most widely recommended colorblind-friendly palette
|
||||
OKABE_ITO = {
|
||||
'orange': '#E69F00',
|
||||
'sky_blue': '#56B4E9',
|
||||
'bluish_green': '#009E73',
|
||||
'yellow': '#F0E442',
|
||||
'blue': '#0072B2',
|
||||
'vermillion': '#D55E00',
|
||||
'reddish_purple': '#CC79A7',
|
||||
'black': '#000000'
|
||||
}
|
||||
|
||||
OKABE_ITO_LIST = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
|
||||
# Wong Palette (Nature Methods)
|
||||
WONG = ['#000000', '#E69F00', '#56B4E9', '#009E73',
|
||||
'#F0E442', '#0072B2', '#D55E00', '#CC79A7']
|
||||
|
||||
# Paul Tol Palettes (https://personal.sron.nl/~pault/)
|
||||
TOL_BRIGHT = ['#4477AA', '#EE6677', '#228833', '#CCBB44',
|
||||
'#66CCEE', '#AA3377', '#BBBBBB']
|
||||
|
||||
TOL_MUTED = ['#332288', '#88CCEE', '#44AA99', '#117733',
|
||||
'#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
|
||||
TOL_LIGHT = ['#77AADD', '#EE8866', '#EEDD88', '#FFAABB',
|
||||
'#99DDFF', '#44BB99', '#BBCC33', '#AAAA00', '#DDDDDD']
|
||||
|
||||
TOL_HIGH_CONTRAST = ['#004488', '#DDAA33', '#BB5566']
|
||||
|
||||
# Sequential colormaps (for continuous data)
|
||||
SEQUENTIAL_COLORMAPS = [
|
||||
'viridis', # Default, perceptually uniform
|
||||
'plasma', # Perceptually uniform
|
||||
'inferno', # Perceptually uniform
|
||||
'magma', # Perceptually uniform
|
||||
'cividis', # Optimized for colorblind viewers
|
||||
'YlOrRd', # Yellow-Orange-Red
|
||||
'YlGnBu', # Yellow-Green-Blue
|
||||
'Blues', # Single hue
|
||||
'Greens', # Single hue
|
||||
'Purples', # Single hue
|
||||
]
|
||||
|
||||
# Diverging colormaps (for data with meaningful center)
|
||||
DIVERGING_COLORMAPS_SAFE = [
|
||||
'RdYlBu', # Red-Yellow-Blue (reversed is common)
|
||||
'RdBu', # Red-Blue
|
||||
'PuOr', # Purple-Orange (excellent for colorblind)
|
||||
'BrBG', # Brown-Blue-Green (good for colorblind)
|
||||
'PRGn', # Purple-Green (use with caution)
|
||||
'PiYG', # Pink-Yellow-Green (use with caution)
|
||||
]
|
||||
|
||||
# Diverging colormaps to AVOID (red-green combinations)
|
||||
DIVERGING_COLORMAPS_AVOID = [
|
||||
'RdGn', # Red-Green (problematic!)
|
||||
'RdYlGn', # Red-Yellow-Green (problematic!)
|
||||
]
|
||||
|
||||
# Fluorophore colors (traditional - use with caution)
|
||||
FLUOROPHORES_TRADITIONAL = {
|
||||
'DAPI': '#0000FF', # Blue
|
||||
'GFP': '#00FF00', # Green (problematic for colorblind)
|
||||
'RFP': '#FF0000', # Red
|
||||
'Cy5': '#FF00FF', # Magenta
|
||||
'YFP': '#FFFF00', # Yellow
|
||||
}
|
||||
|
||||
# Fluorophore colors (colorblind-friendly alternatives)
|
||||
FLUOROPHORES_ACCESSIBLE = {
|
||||
'Channel1': '#0072B2', # Blue
|
||||
'Channel2': '#E69F00', # Orange (instead of green)
|
||||
'Channel3': '#D55E00', # Vermillion (instead of red)
|
||||
'Channel4': '#CC79A7', # Magenta
|
||||
'Channel5': '#F0E442', # Yellow
|
||||
}
|
||||
|
||||
# Genomics/Bioinformatics
|
||||
DNA_BASES = {
|
||||
'A': '#00CC00', # Green
|
||||
'C': '#0000CC', # Blue
|
||||
'G': '#FFB300', # Orange
|
||||
'T': '#CC0000', # Red
|
||||
}
|
||||
|
||||
DNA_BASES_ACCESSIBLE = {
|
||||
'A': '#009E73', # Bluish Green
|
||||
'C': '#0072B2', # Blue
|
||||
'G': '#E69F00', # Orange
|
||||
'T': '#D55E00', # Vermillion
|
||||
}
|
||||
|
||||
|
||||
def apply_palette(palette_name='okabe_ito'):
|
||||
"""
|
||||
Apply a color palette to matplotlib's default color cycle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
palette_name : str
|
||||
Name of the palette to apply. Options:
|
||||
'okabe_ito', 'wong', 'tol_bright', 'tol_muted',
|
||||
'tol_light', 'tol_high_contrast'
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
List of colors in the palette
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> apply_palette('okabe_ito')
|
||||
>>> plt.plot([1, 2, 3], [1, 4, 9]) # Uses Okabe-Ito colors
|
||||
"""
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
except ImportError:
|
||||
print("matplotlib not installed")
|
||||
return None
|
||||
|
||||
palettes = {
|
||||
'okabe_ito': OKABE_ITO_LIST,
|
||||
'wong': WONG,
|
||||
'tol_bright': TOL_BRIGHT,
|
||||
'tol_muted': TOL_MUTED,
|
||||
'tol_light': TOL_LIGHT,
|
||||
'tol_high_contrast': TOL_HIGH_CONTRAST,
|
||||
}
|
||||
|
||||
if palette_name not in palettes:
|
||||
available = ', '.join(palettes.keys())
|
||||
raise ValueError(f"Palette '{palette_name}' not found. Available: {available}")
|
||||
|
||||
colors = palettes[palette_name]
|
||||
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=colors)
|
||||
return colors
|
||||
|
||||
|
||||
def get_palette(palette_name='okabe_ito'):
|
||||
"""
|
||||
Get a color palette as a list.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
palette_name : str
|
||||
Name of the palette
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
List of color hex codes
|
||||
"""
|
||||
palettes = {
|
||||
'okabe_ito': OKABE_ITO_LIST,
|
||||
'wong': WONG,
|
||||
'tol_bright': TOL_BRIGHT,
|
||||
'tol_muted': TOL_MUTED,
|
||||
'tol_light': TOL_LIGHT,
|
||||
'tol_high_contrast': TOL_HIGH_CONTRAST,
|
||||
}
|
||||
|
||||
if palette_name not in palettes:
|
||||
available = ', '.join(palettes.keys())
|
||||
raise ValueError(f"Palette '{palette_name}' not found. Available: {available}")
|
||||
|
||||
return palettes[palette_name]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Available colorblind-friendly palettes:")
|
||||
print(f" - Okabe-Ito: {len(OKABE_ITO_LIST)} colors")
|
||||
print(f" - Wong: {len(WONG)} colors")
|
||||
print(f" - Tol Bright: {len(TOL_BRIGHT)} colors")
|
||||
print(f" - Tol Muted: {len(TOL_MUTED)} colors")
|
||||
print(f" - Tol Light: {len(TOL_LIGHT)} colors")
|
||||
print(f" - Tol High Contrast: {len(TOL_HIGH_CONTRAST)} colors")
|
||||
|
||||
print("\nOkabe-Ito palette (most recommended):")
|
||||
for name, color in OKABE_ITO.items():
|
||||
print(f" {name:15s}: {color}")
|
||||
@@ -0,0 +1,63 @@
|
||||
# Nature journal style
|
||||
# Usage: plt.style.use('nature.mplstyle')
|
||||
#
|
||||
# Optimized for Nature journal specifications:
|
||||
# - Single column: 89 mm
|
||||
# - Double column: 183 mm
|
||||
# - High resolution requirements
|
||||
|
||||
# Figure properties
|
||||
figure.dpi: 100
|
||||
figure.facecolor: white
|
||||
figure.constrained_layout.use: True
|
||||
figure.figsize: 3.5, 2.625 # 89 mm single column, 3:4 aspect
|
||||
|
||||
# Font properties (Nature prefers smaller fonts)
|
||||
font.size: 7
|
||||
font.family: sans-serif
|
||||
font.sans-serif: Arial, Helvetica
|
||||
|
||||
# Axes properties
|
||||
axes.linewidth: 0.5
|
||||
axes.labelsize: 8
|
||||
axes.titlesize: 8
|
||||
axes.labelweight: normal
|
||||
axes.spines.top: False
|
||||
axes.spines.right: False
|
||||
axes.edgecolor: black
|
||||
axes.axisbelow: True
|
||||
axes.grid: False
|
||||
axes.prop_cycle: cycler('color', ['E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7'])
|
||||
|
||||
# Tick properties
|
||||
xtick.major.size: 2.5
|
||||
xtick.minor.size: 1.5
|
||||
xtick.major.width: 0.5
|
||||
xtick.minor.width: 0.4
|
||||
xtick.labelsize: 6
|
||||
xtick.direction: out
|
||||
ytick.major.size: 2.5
|
||||
ytick.minor.size: 1.5
|
||||
ytick.major.width: 0.5
|
||||
ytick.minor.width: 0.4
|
||||
ytick.labelsize: 6
|
||||
ytick.direction: out
|
||||
|
||||
# Line properties
|
||||
lines.linewidth: 1.2
|
||||
lines.markersize: 3
|
||||
lines.markeredgewidth: 0.4
|
||||
|
||||
# Legend properties
|
||||
legend.fontsize: 6
|
||||
legend.frameon: False
|
||||
|
||||
# Save properties (Nature requirements)
|
||||
savefig.dpi: 600 # 1000 for line art, 600 for combination
|
||||
savefig.format: pdf
|
||||
savefig.bbox: tight
|
||||
savefig.pad_inches: 0.05
|
||||
savefig.facecolor: white
|
||||
|
||||
# Image properties
|
||||
image.cmap: viridis
|
||||
@@ -0,0 +1,61 @@
|
||||
# Presentation/Poster style
|
||||
# Usage: plt.style.use('presentation.mplstyle')
|
||||
#
|
||||
# Larger fonts and thicker lines for presentations,
|
||||
# posters, and projected displays
|
||||
|
||||
# Figure properties
|
||||
figure.dpi: 100
|
||||
figure.facecolor: white
|
||||
figure.constrained_layout.use: True
|
||||
figure.figsize: 8, 6
|
||||
|
||||
# Font properties (larger for visibility)
|
||||
font.size: 14
|
||||
font.family: sans-serif
|
||||
font.sans-serif: Arial, Helvetica, Calibri
|
||||
|
||||
# Axes properties
|
||||
axes.linewidth: 1.5
|
||||
axes.labelsize: 16
|
||||
axes.titlesize: 18
|
||||
axes.labelweight: normal
|
||||
axes.spines.top: False
|
||||
axes.spines.right: False
|
||||
axes.edgecolor: black
|
||||
axes.axisbelow: True
|
||||
axes.grid: False
|
||||
axes.prop_cycle: cycler('color', ['E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7'])
|
||||
|
||||
# Tick properties
|
||||
xtick.major.size: 6
|
||||
xtick.minor.size: 4
|
||||
xtick.major.width: 1.5
|
||||
xtick.minor.width: 1.0
|
||||
xtick.labelsize: 12
|
||||
xtick.direction: out
|
||||
ytick.major.size: 6
|
||||
ytick.minor.size: 4
|
||||
ytick.major.width: 1.5
|
||||
ytick.minor.width: 1.0
|
||||
ytick.labelsize: 12
|
||||
ytick.direction: out
|
||||
|
||||
# Line properties
|
||||
lines.linewidth: 2.5
|
||||
lines.markersize: 8
|
||||
lines.markeredgewidth: 1.0
|
||||
|
||||
# Legend properties
|
||||
legend.fontsize: 12
|
||||
legend.frameon: False
|
||||
|
||||
# Save properties
|
||||
savefig.dpi: 300
|
||||
savefig.format: png
|
||||
savefig.bbox: tight
|
||||
savefig.pad_inches: 0.1
|
||||
savefig.facecolor: white
|
||||
|
||||
# Image properties
|
||||
image.cmap: viridis
|
||||
@@ -0,0 +1,68 @@
|
||||
# Publication-quality matplotlib style
|
||||
# Usage: plt.style.use('publication.mplstyle')
|
||||
#
|
||||
# This style provides clean, professional formatting suitable
|
||||
# for most scientific journals
|
||||
|
||||
# Figure properties
|
||||
figure.dpi: 100
|
||||
figure.facecolor: white
|
||||
figure.autolayout: False
|
||||
figure.constrained_layout.use: True
|
||||
figure.figsize: 3.5, 2.5
|
||||
|
||||
# Font properties
|
||||
font.size: 8
|
||||
font.family: sans-serif
|
||||
font.sans-serif: Arial, Helvetica, DejaVu Sans
|
||||
|
||||
# Axes properties
|
||||
axes.linewidth: 0.5
|
||||
axes.labelsize: 9
|
||||
axes.titlesize: 9
|
||||
axes.labelweight: normal
|
||||
axes.spines.top: False
|
||||
axes.spines.right: False
|
||||
axes.spines.left: True
|
||||
axes.spines.bottom: True
|
||||
axes.edgecolor: black
|
||||
axes.labelcolor: black
|
||||
axes.axisbelow: True
|
||||
axes.grid: False
|
||||
axes.prop_cycle: cycler('color', ['E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7', '000000'])
|
||||
|
||||
# Tick properties
|
||||
xtick.major.size: 3
|
||||
xtick.minor.size: 2
|
||||
xtick.major.width: 0.5
|
||||
xtick.minor.width: 0.5
|
||||
xtick.labelsize: 7
|
||||
xtick.direction: out
|
||||
ytick.major.size: 3
|
||||
ytick.minor.size: 2
|
||||
ytick.major.width: 0.5
|
||||
ytick.minor.width: 0.5
|
||||
ytick.labelsize: 7
|
||||
ytick.direction: out
|
||||
|
||||
# Line properties
|
||||
lines.linewidth: 1.5
|
||||
lines.markersize: 4
|
||||
lines.markeredgewidth: 0.5
|
||||
|
||||
# Legend properties
|
||||
legend.fontsize: 7
|
||||
legend.frameon: False
|
||||
legend.loc: best
|
||||
|
||||
# Save properties
|
||||
savefig.dpi: 300
|
||||
savefig.format: pdf
|
||||
savefig.bbox: tight
|
||||
savefig.pad_inches: 0.05
|
||||
savefig.transparent: False
|
||||
savefig.facecolor: white
|
||||
|
||||
# Image properties
|
||||
image.cmap: viridis
|
||||
image.aspect: auto
|
||||
@@ -0,0 +1,348 @@
|
||||
# 科学配色方案与使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
科学可视化中的颜色选择对于可访问性、清晰度以及准确的数据表示至关重要。本参考资料提供了色盲友好的配色方案和颜色使用的最佳实践。
|
||||
|
||||
## 色盲友好配色方案
|
||||
|
||||
### Okabe-Ito 配色方案(推荐用于分类数据)
|
||||
|
||||
Okabe-Ito 配色方案专门设计为让所有形式的色盲人群都能区分。
|
||||
|
||||
```python
|
||||
# Okabe-Ito 颜色(RGB 值)
|
||||
okabe_ito = {
|
||||
'orange': '#E69F00', # RGB: (230, 159, 0)
|
||||
'sky_blue': '#56B4E9', # RGB: (86, 180, 233)
|
||||
'bluish_green': '#009E73', # RGB: (0, 158, 115)
|
||||
'yellow': '#F0E442', # RGB: (240, 228, 66)
|
||||
'blue': '#0072B2', # RGB: (0, 114, 178)
|
||||
'vermillion': '#D55E00', # RGB: (213, 94, 0)
|
||||
'reddish_purple': '#CC79A7', # RGB: (204, 121, 167)
|
||||
'black': '#000000' # RGB: (0, 0, 0)
|
||||
}
|
||||
```
|
||||
|
||||
**在 Matplotlib 中使用:**
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
colors = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=colors)
|
||||
```
|
||||
|
||||
**在 Seaborn 中使用:**
|
||||
```python
|
||||
import seaborn as sns
|
||||
|
||||
okabe_ito_palette = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
sns.set_palette(okabe_ito_palette)
|
||||
```
|
||||
|
||||
**在 Plotly 中使用:**
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
|
||||
okabe_ito_plotly = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
fig = go.Figure()
|
||||
# 应用于离散色阶
|
||||
```
|
||||
|
||||
### Wong 配色方案(分类数据的替代方案)
|
||||
|
||||
另一个优秀的色盲友好配色方案,由 Bang Wong 设计(Nature Methods 期刊)。
|
||||
|
||||
```python
|
||||
wong_palette = {
|
||||
'black': '#000000',
|
||||
'orange': '#E69F00',
|
||||
'sky_blue': '#56B4E9',
|
||||
'green': '#009E73',
|
||||
'yellow': '#F0E442',
|
||||
'blue': '#0072B2',
|
||||
'vermillion': '#D55E00',
|
||||
'purple': '#CC79A7'
|
||||
}
|
||||
```
|
||||
|
||||
### Paul Tol 配色方案
|
||||
|
||||
Paul Tol 设计了多个针对不同使用场景优化过的科学配色方案。
|
||||
|
||||
**明亮色板(最多 7 个类别):**
|
||||
```python
|
||||
tol_bright = ['#4477AA', '#EE6677', '#228833', '#CCBB44',
|
||||
'#66CCEE', '#AA3377', '#BBBBBB']
|
||||
```
|
||||
|
||||
**柔和色板(最多 9 个类别):**
|
||||
```python
|
||||
tol_muted = ['#332288', '#88CCEE', '#44AA99', '#117733',
|
||||
'#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
```
|
||||
|
||||
**高对比度(仅 3 个类别):**
|
||||
```python
|
||||
tol_high_contrast = ['#004488', '#DDAA33', '#BB5566']
|
||||
```
|
||||
|
||||
## 顺序色图(连续数据)
|
||||
|
||||
顺序色图使用单一色相表示从低到高的数据值。
|
||||
|
||||
### 感知均匀色图
|
||||
|
||||
这些色图在整个色阶上具有均匀的感知变化。
|
||||
|
||||
**Viridis(Matplotlib 默认色图):**
|
||||
- 色盲友好
|
||||
- 灰度打印效果好
|
||||
- 感知均匀
|
||||
```python
|
||||
plt.imshow(data, cmap='viridis')
|
||||
```
|
||||
|
||||
**Cividis:**
|
||||
- 针对色盲观看者优化
|
||||
- 专为绿色盲/红色盲设计
|
||||
```python
|
||||
plt.imshow(data, cmap='cividis')
|
||||
```
|
||||
|
||||
**Plasma、Inferno、Magma:**
|
||||
- viridis 的感知均匀替代方案
|
||||
- 适合不同的审美偏好
|
||||
```python
|
||||
plt.imshow(data, cmap='plasma')
|
||||
```
|
||||
|
||||
### 何时使用顺序色图
|
||||
- 显示强度的热力图
|
||||
- 地理高程数据
|
||||
- 概率分布
|
||||
- 任何单变量连续数据(低 → 高)
|
||||
|
||||
## 发散色图(负值到正值)
|
||||
|
||||
发散色图使用中性的中间色,两端使用两种对比色。
|
||||
|
||||
### 色盲安全的发散色图
|
||||
|
||||
**RdYlBu(红-黄-蓝):**
|
||||
```python
|
||||
plt.imshow(data, cmap='RdYlBu_r') # _r 反转:蓝色(低)到红色(高)
|
||||
```
|
||||
|
||||
**PuOr(紫-橙):**
|
||||
- 非常适合色盲观看者
|
||||
```python
|
||||
plt.imshow(data, cmap='PuOr')
|
||||
```
|
||||
|
||||
**BrBG(棕-蓝-绿):**
|
||||
- 色盲可访问性好
|
||||
```python
|
||||
plt.imshow(data, cmap='BrBG')
|
||||
```
|
||||
|
||||
### 应避免的发散色图
|
||||
- **RdGn(红-绿)**:对红绿色盲人群有问题
|
||||
- **RdYlGn(红-黄-绿)**:同样的问题
|
||||
|
||||
### 何时使用发散色图
|
||||
- 相关矩阵
|
||||
- 变化/差异数据(正值 vs. 负值)
|
||||
- 偏离中心值的数据
|
||||
- 温度异常
|
||||
|
||||
## 专用配色方案
|
||||
|
||||
### 基因组学/生物信息学
|
||||
|
||||
**序列类型标识:**
|
||||
```python
|
||||
# DNA/RNA 碱基
|
||||
nucleotide_colors = {
|
||||
'A': '#00CC00', # 绿色
|
||||
'C': '#0000CC', # 蓝色
|
||||
'G': '#FFB300', # 橙色
|
||||
'T': '#CC0000', # 红色
|
||||
'U': '#CC0000' # 红色(RNA)
|
||||
}
|
||||
```
|
||||
|
||||
**基因表达:**
|
||||
- 使用顺序色图(viridis、YlOrRd)表示表达水平
|
||||
- 使用发散色图(RdBu)表示 log2 倍数变化
|
||||
|
||||
### 显微镜成像
|
||||
|
||||
**荧光通道:**
|
||||
```python
|
||||
# 传统荧光染料颜色(谨慎使用)
|
||||
fluorophore_colors = {
|
||||
'DAPI': '#0000FF', # 蓝色 — DNA
|
||||
'GFP': '#00FF00', # 绿色(对色盲人群有问题)
|
||||
'RFP': '#FF0000', # 红色
|
||||
'Cy5': '#FF00FF' # 品红色
|
||||
}
|
||||
|
||||
# 色盲友好替代方案
|
||||
fluorophore_alt = {
|
||||
'Channel1': '#0072B2', # 蓝色
|
||||
'Channel2': '#E69F00', # 橙色(替代绿色)
|
||||
'Channel3': '#D55E00', # 朱红色
|
||||
'Channel4': '#CC79A7' # 品红色
|
||||
}
|
||||
```
|
||||
|
||||
## 颜色使用最佳实践
|
||||
|
||||
### 分类数据(定性配色方案)
|
||||
|
||||
**应遵循:**
|
||||
- 使用 Okabe-Ito 或 Wong 配色方案中鲜明、可区分的颜色
|
||||
- 单个图表中最多限制在 7–8 个类别
|
||||
- 跨图表对同一类别使用一致的颜色
|
||||
- 当仅凭颜色可能不足以区分时,添加图案/标记
|
||||
|
||||
**应避免:**
|
||||
- 使用红/绿组合
|
||||
- 使用彩虹(jet)色图表示分类数据
|
||||
- 使用难以区分的相近色相
|
||||
|
||||
### 连续数据(顺序/发散方案)
|
||||
|
||||
**应遵循:**
|
||||
- 使用感知均匀色图(viridis、plasma、cividis)
|
||||
- 当数据具有有意义的中心点时选择发散色图
|
||||
- 包含带标注刻度的色标
|
||||
- 测试灰度下的显示效果
|
||||
|
||||
**应避免:**
|
||||
- 使用彩虹(jet)色图 — 感知不均匀
|
||||
- 使用红-绿发散色图
|
||||
- 在热力图中省略色标
|
||||
|
||||
## 色盲可访问性测试
|
||||
|
||||
### 在线模拟器
|
||||
- **Coblis**:https://www.color-blindness.com/coblis-color-blindness-simulator/
|
||||
- **Color Oracle**:免费可下载工具,支持 Windows/Mac/Linux
|
||||
- **Sim Daltonism**:Mac 应用程序
|
||||
|
||||
### 色觉缺陷类型
|
||||
- **绿色盲**(约 5% 的男性):无法区分绿色
|
||||
- **红色盲**(约 2% 的男性):无法区分红色
|
||||
- **蓝色盲**(低于 1%):无法区分蓝色(罕见)
|
||||
|
||||
### Python 工具
|
||||
```python
|
||||
# 使用 colorspacious 模拟色盲视觉
|
||||
from colorspacious import cspace_convert
|
||||
|
||||
def simulate_deuteranopia(image_rgb):
|
||||
from colorspacious import cspace_convert
|
||||
# 转换为色盲模拟
|
||||
# (实现需要 colorspacious 库)
|
||||
pass
|
||||
```
|
||||
|
||||
## 实现示例
|
||||
|
||||
### 设置全局 Matplotlib 样式
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
# 将 Okabe-Ito 设置为默认颜色循环
|
||||
okabe_ito_colors = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=okabe_ito_colors)
|
||||
|
||||
# 将默认色图设置为 viridis
|
||||
mpl.rcParams['image.cmap'] = 'viridis'
|
||||
```
|
||||
|
||||
### 使用自定义色板的 Seaborn
|
||||
```python
|
||||
import seaborn as sns
|
||||
|
||||
# 设置 Paul Tol 柔和色板
|
||||
tol_muted = ['#332288', '#88CCEE', '#44AA99', '#117733',
|
||||
'#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
sns.set_palette(tol_muted)
|
||||
|
||||
# 用于热力图
|
||||
sns.heatmap(data, cmap='viridis', annot=True)
|
||||
```
|
||||
|
||||
### 带离散颜色的 Plotly
|
||||
```python
|
||||
import plotly.express as px
|
||||
|
||||
# 使用 Okabe-Ito 表示分类数据
|
||||
okabe_ito_plotly = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
|
||||
fig = px.scatter(df, x='x', y='y', color='category',
|
||||
color_discrete_sequence=okabe_ito_plotly)
|
||||
```
|
||||
|
||||
## 灰度兼容性
|
||||
|
||||
所有图表应保持灰度下的可读性。通过转换为灰度进行测试:
|
||||
|
||||
```python
|
||||
# 将图表转换为灰度以便测试
|
||||
fig.savefig('figure_gray.png', dpi=300, colormap='gray')
|
||||
```
|
||||
|
||||
**灰度兼容性策略:**
|
||||
1. 使用不同的线型(实线、虚线、点线)
|
||||
2. 使用不同的标记形状(圆形、方形、三角形)
|
||||
3. 为条形添加填充图案
|
||||
4. 确保颜色之间有足够的亮度对比度
|
||||
|
||||
## 色彩空间
|
||||
|
||||
### RGB 与 CMYK
|
||||
- **RGB(红、绿、蓝)**:用于数字/屏幕显示
|
||||
- **CMYK(青、品红、黄、黑)**:用于印刷
|
||||
|
||||
**重要提示:** 印刷品与屏幕上的颜色呈现不同。准备印刷材料时:
|
||||
1. 转换为 CMYK 色彩空间
|
||||
2. 在 CMYK 预览中检查颜色表现
|
||||
3. 确保保留足够的对比度
|
||||
|
||||
### Matplotlib 色彩空间
|
||||
```python
|
||||
# 保存用于印刷(CMYK)
|
||||
# 注意:直接 CMYK 支持有限;使用 PDF 并让出版方转换
|
||||
fig.savefig('figure.pdf', dpi=300)
|
||||
|
||||
# 用于 RGB(数字显示)
|
||||
fig.savefig('figure.png', dpi=300)
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
1. **使用 jet/rainbow 色图**:感知不均匀,应避免
|
||||
2. **红-绿组合**:约 8% 的男性无法区分
|
||||
3. **颜色过多**:超过 7–8 种颜色后难以区分
|
||||
4. **颜色含义不一致**:同一颜色在不同图表中应表示相同含义
|
||||
5. **缺少色标**:连续数据始终应包含色标
|
||||
6. **对比度过低**:确保颜色之间有足够的差异
|
||||
7. **仅依赖颜色区分**:应添加纹理、图案或标记
|
||||
|
||||
## 资源
|
||||
|
||||
- **ColorBrewer**:http://colorbrewer2.org/ — 通过色盲安全选项选择配色方案
|
||||
- **Paul Tol 配色方案**:https://personal.sron.nl/~pault/
|
||||
- **Okabe-Ito 配色方案来源**:《Color Universal Design》(Okabe & Ito, 2008)
|
||||
- **Matplotlib 色图**:https://matplotlib.org/stable/tutorials/colors/colormaps.html
|
||||
- **Seaborn 调色板**:https://seaborn.pydata.org/tutorial/color_palettes.html
|
||||
@@ -0,0 +1,320 @@
|
||||
# 期刊专属图片要求
|
||||
|
||||
## 概述
|
||||
|
||||
不同期刊对图片有特定的技术要求。本参考文档汇总了主要学术出版商的常见要求。**请务必查阅具体期刊的作者指南以获取最新要求。**
|
||||
|
||||
## Nature 系列(Nature、Nature Methods 等)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:PDF、EPS、AI(图表首选)
|
||||
- 位图:TIFF、PNG(用于图像)
|
||||
- 禁止使用:PowerPoint、Word、JPEG
|
||||
|
||||
- **分辨率**:
|
||||
- 线条图:1000–1200 DPI
|
||||
- 组合图(线条图 + 图像):600 DPI
|
||||
- 照片/显微图像:最低 300 DPI
|
||||
|
||||
- **色彩模式**:RGB(Nature 以数字版优先)
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:89 mm(3.5 英寸)
|
||||
- 1.5 栏:120 mm(4.7 英寸)
|
||||
- 双栏:183 mm(7.2 英寸)
|
||||
- 最大高度:247 mm(9.7 英寸)
|
||||
|
||||
- **字体**:
|
||||
- Arial 或 Helvetica(或类似无衬线字体)
|
||||
- 最终尺寸下最小 5–7 pt
|
||||
- 所有 PDF/EPS 文件需嵌入字体
|
||||
|
||||
### Nature 特定指南
|
||||
- 面板标签:a、b、c(小写加粗),位于左上角
|
||||
- 显微图像必须带有比例尺
|
||||
- 凝胶图像:需包含分子量标记
|
||||
- 裁剪:需用分隔线标示
|
||||
- 统计:标明显著性;在图例中定义符号
|
||||
- 源数据:所有图表均需提供
|
||||
|
||||
### 文件命名
|
||||
格式:`FirstAuthorLastName_FigureNumber.ext`
|
||||
示例:`Smith_Fig1.pdf`
|
||||
|
||||
## Science(AAAS)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:EPS、PDF(首选)
|
||||
- 位图:TIFF
|
||||
- 可接受:AI、PSD(Photoshop)
|
||||
|
||||
- **分辨率**:
|
||||
- 线条图:最低 1000 DPI
|
||||
- 照片:最低 300 DPI
|
||||
- 组合图:最低 600 DPI
|
||||
|
||||
- **色彩模式**:RGB
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:5.5 cm(2.17 英寸)
|
||||
- 1.5 栏:12 cm(4.72 英寸)
|
||||
- 全宽:17.5 cm(6.89 英寸)
|
||||
- 最大高度:23.3 cm(9.17 英寸)
|
||||
|
||||
- **字体**:
|
||||
- Helvetica(或 Arial)
|
||||
- 最终尺寸下最小 6–8 pt
|
||||
- 所有图片字体须一致
|
||||
|
||||
### Science 特定指南
|
||||
- 面板标签:(A)、(B)、(C) 加括号
|
||||
- 图中文字尽量精简(详细信息放图注)
|
||||
- 兼顾网页与印刷的高对比度
|
||||
- 必须标注误差线;在图注中说明
|
||||
- 避免过多空白
|
||||
|
||||
### 文件命名
|
||||
格式:`Manuscript#_Fig#.ext`
|
||||
示例:`abn1234_Fig1.eps`
|
||||
|
||||
## Cell Press(Cell、Neuron、Molecular Cell 等)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:PDF、EPS(图表/示意图首选)
|
||||
- 位图:TIFF(用于照片)
|
||||
|
||||
- **分辨率**:
|
||||
- 线条图:1000 DPI
|
||||
- 照片:300 DPI
|
||||
- 组合图:600 DPI
|
||||
|
||||
- **色彩模式**:RGB
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:85 mm(3.35 英寸)
|
||||
- 双栏:178 mm(7.01 英寸)
|
||||
- 最大高度:230 mm(9.06 英寸)
|
||||
|
||||
- **字体**:
|
||||
- 仅限 Arial 或 Helvetica
|
||||
- 坐标轴标签:8–12 pt
|
||||
- 刻度标签:6–8 pt
|
||||
|
||||
### Cell Press 特定指南
|
||||
- 面板标签:(A)、(B)、(C) 或 A、B、C,位于左上角
|
||||
- 相关面板尺寸应一致
|
||||
- 显微图片必须带比例尺
|
||||
- Western blot:需包含分子量标记
|
||||
- 箭头/箭头符号:最小线宽 2 pt
|
||||
- 线条宽度:数据线条 1–2 pt
|
||||
|
||||
## PLOS(公共科学图书馆)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:EPS、PDF(首选)
|
||||
- 位图:TIFF、PNG
|
||||
- TIFF 可使用 LZW 压缩
|
||||
|
||||
- **分辨率**:
|
||||
- 最终尺寸下所有图片类型最低 300 DPI
|
||||
- 线条图建议 600 DPI
|
||||
|
||||
- **色彩模式**:RGB
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:8.3 cm(3.27 英寸)
|
||||
- 1.5 栏:11.4 cm(4.49 英寸)
|
||||
- 双栏:17.3 cm(6.81 英寸)
|
||||
- 最大高度:23.3 cm(9.17 英寸)
|
||||
|
||||
- **字体**:
|
||||
- 推荐无衬线字体(Arial、Helvetica)
|
||||
- 最终尺寸下标签 8–12 pt
|
||||
|
||||
### PLOS 特定指南
|
||||
- 图片应在不依赖图注的情况下可理解
|
||||
- 仅在能增加信息量时使用彩色
|
||||
- 所有图片应可转换为灰度
|
||||
- 面板标签可选但建议使用
|
||||
- 开放获取:图片必须采用 CC-BY 许可
|
||||
- 鼓励提供源数据文件
|
||||
|
||||
## ACS(美国化学会)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 首选:TIFF、PDF、EPS
|
||||
- 应用程序文件:AI、CDX(ChemDraw)、CDL
|
||||
- 可接受:PNG(不可用于正式出版)
|
||||
|
||||
- **分辨率**:
|
||||
- 最终尺寸下最低 300 DPI
|
||||
- 线条图与化学结构式:600 DPI
|
||||
- 精细结构式:1200 DPI
|
||||
|
||||
- **色彩模式**:RGB 或 CMYK(请查阅具体期刊要求)
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:3.25 英寸(8.25 cm)
|
||||
- 双栏:7 英寸(17.78 cm)
|
||||
|
||||
- **字体**:
|
||||
- 必须嵌入字体
|
||||
- 所有图片字体大小保持一致
|
||||
|
||||
### ACS 特定指南
|
||||
- 化学结构式:使用 ChemDraw 或同等软件
|
||||
- 原子标签:10–12 pt
|
||||
- 键线粗细:2 pt
|
||||
- 面板标签:小写加粗(a、b、c)
|
||||
- 需要高对比度(许多 ACS 期刊为灰度印刷)
|
||||
|
||||
## Elsevier 期刊(因期刊而异)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:EPS、PDF
|
||||
- 位图:TIFF、JPEG(仅用于照片)
|
||||
|
||||
- **分辨率**:
|
||||
- 线条图:最低 1000 DPI
|
||||
- 照片:最低 300 DPI
|
||||
- 组合图:最低 600 DPI
|
||||
|
||||
- **色彩模式**:RGB(在线版);CMYK(印刷版期刊)
|
||||
|
||||
- **尺寸**:因期刊而异
|
||||
- 常见单栏宽度:90 mm
|
||||
- 常见双栏宽度:190 mm
|
||||
|
||||
- **字体**:
|
||||
- 推荐:Arial、Times、Symbol
|
||||
- 最终尺寸下最小 6 pt
|
||||
|
||||
### Elsevier 特定指南
|
||||
- 请查阅各期刊的具体指南(差异较大)
|
||||
- 部分期刊对彩色印刷收费
|
||||
- 面板标签通常为 (A)、(B)、(C) 或 A、B、C
|
||||
- 通常需要图文摘要(与正文图片分开提交)
|
||||
|
||||
## IEEE(工程/计算机科学)
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 矢量图:PDF、EPS(首选)
|
||||
- 位图:TIFF、PNG
|
||||
|
||||
- **分辨率**:
|
||||
- 照片/图形:最终尺寸下最低 300 DPI
|
||||
- 线条图:最低 600 DPI
|
||||
|
||||
- **色彩模式**:RGB(在线版);CMYK(印刷版)
|
||||
|
||||
- **尺寸**:
|
||||
- 单栏:3.5 英寸(8.9 cm)
|
||||
- 双栏:7.16 英寸(18.2 cm)
|
||||
|
||||
- **字体**:
|
||||
- 推荐无衬线字体
|
||||
- 最终尺寸下最小 8–10 pt
|
||||
|
||||
### IEEE 特定指南
|
||||
- 图片应在黑白模式下可读
|
||||
- 彩色图片不另行收费(在线出版)
|
||||
- 面板标签:(a)、(b)、(c) 小写
|
||||
- 图注位于图片下方(不另起一页)
|
||||
- 投稿前请使用 IEEE 图片检查工具
|
||||
|
||||
## BMC(BioMed Central)——开放获取
|
||||
|
||||
### 技术规格
|
||||
- **文件格式**:
|
||||
- 接受所有标准格式
|
||||
- 首选:TIFF、PDF、EPS、PNG
|
||||
|
||||
- **分辨率**:
|
||||
- 线条图最低 600 DPI
|
||||
- 照片最低 300 DPI
|
||||
|
||||
- **色彩模式**:RGB
|
||||
|
||||
- **尺寸**:
|
||||
- 较灵活,但需考虑可读性
|
||||
- 最大宽度通常为 140 mm
|
||||
|
||||
- **字体**:
|
||||
- 需嵌入且清晰可读
|
||||
|
||||
### BMC 特定指南
|
||||
- 开放获取:必须采用 CC-BY 许可
|
||||
- 图片文件单独上传
|
||||
- 面板标签根据学科惯例标注
|
||||
- 鼓励提供源数据
|
||||
- 注重无障碍性(色盲友好)
|
||||
|
||||
## 期刊通用要求
|
||||
|
||||
### 通用最佳实践
|
||||
1. **切勿对图表使用 JPEG**:压缩会产生伪影
|
||||
2. **嵌入所有字体**:PDF/EPS 文件中
|
||||
3. **图层结构**:展平图像(在 Photoshop 中合并图层)
|
||||
4. **RGB 与 CMYK**:大多数期刊现已采用 RGB(以数字版优先)
|
||||
5. **高分辨率**:起步时始终设高分辨率,必要时再降低
|
||||
6. **一致性**:稿件中所有图片风格统一
|
||||
7. **文件大小**:在品质与合理文件大小之间取得平衡(通常每张图片不超过 10 MB)
|
||||
|
||||
### 图片提交
|
||||
- **初稿提交**:通常可接受较低分辨率(供审稿用)
|
||||
- **修改/接收阶段**:需提交高分辨率版本
|
||||
- **单独文件**:每张图片作为单独文件提交
|
||||
- **文件命名**:清晰、系统化的命名方式
|
||||
- **补充材料**:可能有不同要求
|
||||
|
||||
## 快速参考表
|
||||
|
||||
| 出版商 | 单栏 | 双栏 | 照片最低 DPI | 线条图最低 DPI | 首选格式 |
|
||||
|--------|------|------|-------------|--------------|---------|
|
||||
| Nature | 89 mm | 183 mm | 300 | 1000 | EPS、PDF |
|
||||
| Science | 5.5 cm | 17.5 cm | 300 | 1000 | EPS、PDF |
|
||||
| Cell Press | 85 mm | 178 mm | 300 | 1000 | EPS、PDF |
|
||||
| PLOS | 8.3 cm | 17.3 cm | 300 | 600 | EPS、TIFF |
|
||||
| ACS | 3.25 英寸 | 7 英寸 | 300 | 600 | TIFF、EPS |
|
||||
|
||||
## 检查要求
|
||||
|
||||
### 投稿前检查清单
|
||||
1. 阅读期刊作者指南中的图片部分
|
||||
2. 检查文件格式要求
|
||||
3. 确认分辨率要求
|
||||
4. 核对尺寸规格(宽 × 高)
|
||||
5. 检查字体要求
|
||||
6. 确认色彩模式(RGB 与 CMYK)
|
||||
7. 检查面板标签样式
|
||||
8. 审阅补充材料要求
|
||||
9. 确认文件命名规范
|
||||
10. 检查文件大小限制
|
||||
|
||||
### 实用工具
|
||||
- **ImageJ/Fiji**:检查/调整 DPI
|
||||
- **Adobe Acrobat**:验证嵌入字体,检查 PDF 属性
|
||||
- **GIMP**:免费的位图编辑替代软件(Photoshop 替代品)
|
||||
- **Inkscape**:免费的矢量图形编辑器
|
||||
|
||||
## 资源
|
||||
|
||||
- **期刊网站**:始终查阅"作者指南"或"投稿须知"
|
||||
- **出版商资源**:许多出版商提供模板和工具
|
||||
- **格式转换**:使用可靠的转换工具;检查输出质量
|
||||
- **帮助台**:如有疑问请联系期刊工作人员
|
||||
|
||||
## 备注
|
||||
|
||||
- 要求会定期变更——请始终核实当前指南
|
||||
- 预印本服务器(bioRxiv、arXiv)通常有不同的要求
|
||||
- 会议论文集可能有单独的要求
|
||||
- 部分期刊提供图片制作服务(通常为收费项目)
|
||||
- 补充图片的要求可能比正文图片宽松
|
||||
@@ -0,0 +1,620 @@
|
||||
# 可发表级别的 Matplotlib 示例
|
||||
|
||||
## 概述
|
||||
|
||||
本参考文档提供了使用 Matplotlib、Seaborn 和 Plotly 创建可发表级别的科学示意图的实用代码示例。所有示例均遵循 `publication_guidelines.md` 中的最佳实践,并使用 `color_palettes.md` 中的色盲友好调色板。
|
||||
|
||||
## 设置与配置
|
||||
|
||||
### 发表级别 Matplotlib 配置
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
import numpy as np
|
||||
|
||||
# 设置发表质量参数
|
||||
mpl.rcParams['figure.dpi'] = 300
|
||||
mpl.rcParams['savefig.dpi'] = 300
|
||||
mpl.rcParams['font.size'] = 8
|
||||
mpl.rcParams['font.family'] = 'sans-serif'
|
||||
mpl.rcParams['font.sans-serif'] = ['Arial', 'Helvetica']
|
||||
mpl.rcParams['axes.labelsize'] = 9
|
||||
mpl.rcParams['axes.titlesize'] = 9
|
||||
mpl.rcParams['xtick.labelsize'] = 7
|
||||
mpl.rcParams['ytick.labelsize'] = 7
|
||||
mpl.rcParams['legend.fontsize'] = 7
|
||||
mpl.rcParams['axes.linewidth'] = 0.5
|
||||
mpl.rcParams['xtick.major.width'] = 0.5
|
||||
mpl.rcParams['ytick.major.width'] = 0.5
|
||||
mpl.rcParams['lines.linewidth'] = 1.5
|
||||
|
||||
# 使用色盲友好颜色(Okabe-Ito 调色板)
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=okabe_ito)
|
||||
|
||||
# 使用感知均匀的色图
|
||||
mpl.rcParams['image.cmap'] = 'viridis'
|
||||
```
|
||||
|
||||
### 保存辅助函数
|
||||
|
||||
```python
|
||||
def save_publication_figure(fig, filename, formats=['pdf', 'png'], dpi=300):
|
||||
"""
|
||||
以多种格式保存图形以供发表。
|
||||
|
||||
参数
|
||||
-----------
|
||||
fig : matplotlib.figure.Figure
|
||||
要保存的图形
|
||||
filename : str
|
||||
基础文件名(不含扩展名)
|
||||
formats : list
|
||||
要保存的文件格式列表 ['pdf', 'png', 'eps', 'svg']
|
||||
dpi : int
|
||||
光栅格式的分辨率
|
||||
"""
|
||||
for fmt in formats:
|
||||
output_file = f"{filename}.{fmt}"
|
||||
fig.savefig(output_file, dpi=dpi, bbox_inches='tight',
|
||||
facecolor='white', edgecolor='none',
|
||||
transparent=False, format=fmt)
|
||||
print(f"已保存: {output_file}")
|
||||
```
|
||||
|
||||
## 示例 1:带误差线的折线图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 生成示例数据
|
||||
x = np.linspace(0, 10, 50)
|
||||
y1 = 2 * x + 1 + np.random.normal(0, 1, 50)
|
||||
y2 = 1.5 * x + 2 + np.random.normal(0, 1.2, 50)
|
||||
|
||||
# 计算分箱数据的均值和标准误差
|
||||
bins = np.linspace(0, 10, 11)
|
||||
y1_mean = [y1[(x >= bins[i]) & (x < bins[i+1])].mean() for i in range(len(bins)-1)]
|
||||
y1_sem = [y1[(x >= bins[i]) & (x < bins[i+1])].std() /
|
||||
np.sqrt(len(y1[(x >= bins[i]) & (x < bins[i+1])]))
|
||||
for i in range(len(bins)-1)]
|
||||
x_binned = (bins[:-1] + bins[1:]) / 2
|
||||
|
||||
# 创建合适大小的图形(单栏宽度 = 3.5 英寸)
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# 绘制带误差线的图
|
||||
ax.errorbar(x_binned, y1_mean, yerr=y1_sem,
|
||||
marker='o', markersize=4, capsize=3, capthick=0.5,
|
||||
label='条件 A', linewidth=1.5)
|
||||
|
||||
# 添加带单位的标签
|
||||
ax.set_xlabel('时间(小时)')
|
||||
ax.set_ylabel('荧光强度(a.u.)')
|
||||
|
||||
# 添加图例
|
||||
ax.legend(frameon=False, loc='upper left')
|
||||
|
||||
# 移除顶部和右侧的轴脊线
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# 紧凑布局
|
||||
fig.tight_layout()
|
||||
|
||||
# 保存
|
||||
save_publication_figure(fig, 'line_plot_with_errors')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 2:多面板图形
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from string import ascii_uppercase
|
||||
|
||||
# 创建多面板图形(双栏宽度 = 7 英寸)
|
||||
fig = plt.figure(figsize=(7, 4))
|
||||
|
||||
# 定义面板网格
|
||||
gs = fig.add_gridspec(2, 3, hspace=0.4, wspace=0.4,
|
||||
left=0.08, right=0.98, top=0.95, bottom=0.08)
|
||||
|
||||
# 面板 A:折线图
|
||||
ax_a = fig.add_subplot(gs[0, :2])
|
||||
x = np.linspace(0, 10, 100)
|
||||
for i, offset in enumerate([0, 0.5, 1.0]):
|
||||
ax_a.plot(x, np.sin(x) + offset, label=f'数据集 {i+1}')
|
||||
ax_a.set_xlabel('时间(秒)')
|
||||
ax_a.set_ylabel('振幅(V)')
|
||||
ax_a.legend(frameon=False, fontsize=6)
|
||||
ax_a.spines['top'].set_visible(False)
|
||||
ax_a.spines['right'].set_visible(False)
|
||||
|
||||
# 面板 B:柱状图
|
||||
ax_b = fig.add_subplot(gs[0, 2])
|
||||
categories = ['对照', '处理\nA', '处理\nB']
|
||||
values = [100, 125, 140]
|
||||
errors = [5, 8, 6]
|
||||
ax_b.bar(categories, values, yerr=errors, capsize=3,
|
||||
color=['#0072B2', '#E69F00', '#009E73'], alpha=0.8)
|
||||
ax_b.set_ylabel('响应(%)')
|
||||
ax_b.spines['top'].set_visible(False)
|
||||
ax_b.spines['right'].set_visible(False)
|
||||
ax_b.set_ylim(0, 160)
|
||||
|
||||
# 面板 C:散点图
|
||||
ax_c = fig.add_subplot(gs[1, 0])
|
||||
x = np.random.randn(100)
|
||||
y = 2*x + np.random.randn(100)
|
||||
ax_c.scatter(x, y, s=10, alpha=0.6, color='#0072B2')
|
||||
ax_c.set_xlabel('变量 X')
|
||||
ax_c.set_ylabel('变量 Y')
|
||||
ax_c.spines['top'].set_visible(False)
|
||||
ax_c.spines['right'].set_visible(False)
|
||||
|
||||
# 面板 D:热图
|
||||
ax_d = fig.add_subplot(gs[1, 1:])
|
||||
data = np.random.randn(10, 20)
|
||||
im = ax_d.imshow(data, cmap='viridis', aspect='auto')
|
||||
ax_d.set_xlabel('样本编号')
|
||||
ax_d.set_ylabel('特征')
|
||||
cbar = plt.colorbar(im, ax=ax_d, fraction=0.046, pad=0.04)
|
||||
cbar.set_label('强度(a.u.)', rotation=270, labelpad=12)
|
||||
|
||||
# 添加面板标签
|
||||
panels = [ax_a, ax_b, ax_c, ax_d]
|
||||
for i, ax in enumerate(panels):
|
||||
ax.text(-0.15, 1.05, ascii_uppercase[i], transform=ax.transAxes,
|
||||
fontsize=10, fontweight='bold', va='top')
|
||||
|
||||
save_publication_figure(fig, 'multi_panel_figure')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 3:带散点的箱线图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 生成示例数据
|
||||
np.random.seed(42)
|
||||
data = [np.random.normal(100, 15, 30),
|
||||
np.random.normal(120, 20, 30),
|
||||
np.random.normal(140, 18, 30),
|
||||
np.random.normal(110, 22, 30)]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
|
||||
# 创建箱线图
|
||||
bp = ax.boxplot(data, widths=0.5, patch_artist=True,
|
||||
showfliers=False, # 我们将手动添加散点
|
||||
boxprops=dict(facecolor='lightgray', edgecolor='black', linewidth=0.8),
|
||||
medianprops=dict(color='black', linewidth=1.5),
|
||||
whiskerprops=dict(linewidth=0.8),
|
||||
capprops=dict(linewidth=0.8))
|
||||
|
||||
# 叠加个体散点
|
||||
colors = ['#0072B2', '#E69F00', '#009E73', '#D55E00']
|
||||
for i, (d, color) in enumerate(zip(data, colors)):
|
||||
# 为 x 位置添加抖动
|
||||
x = np.random.normal(i+1, 0.04, size=len(d))
|
||||
ax.scatter(x, d, alpha=0.4, s=8, color=color)
|
||||
|
||||
# 自定义
|
||||
ax.set_xticklabels(['对照', '处理 A', '处理 B', '处理 C'])
|
||||
ax.set_ylabel('细胞计数')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.set_ylim(50, 200)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'boxplot_with_points')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 4:带颜色条的热图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 生成相关矩阵
|
||||
np.random.seed(42)
|
||||
n = 10
|
||||
A = np.random.randn(n, n)
|
||||
corr_matrix = np.corrcoef(A)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(4, 3.5))
|
||||
|
||||
# 绘制热图
|
||||
im = ax.imshow(corr_matrix, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto')
|
||||
|
||||
# 添加颜色条
|
||||
cbar = plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
||||
cbar.set_label('相关系数', rotation=270, labelpad=15)
|
||||
|
||||
# 设置刻度和标签
|
||||
gene_names = [f'基因{i+1}' for i in range(n)]
|
||||
ax.set_xticks(np.arange(n))
|
||||
ax.set_yticks(np.arange(n))
|
||||
ax.set_xticklabels(gene_names, rotation=45, ha='right')
|
||||
ax.set_yticklabels(gene_names)
|
||||
|
||||
# 添加网格
|
||||
ax.set_xticks(np.arange(n)-.5, minor=True)
|
||||
ax.set_yticks(np.arange(n)-.5, minor=True)
|
||||
ax.grid(which='minor', color='white', linestyle='-', linewidth=0.5)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'correlation_heatmap')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 5:Seaborn 小提琴图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
# 生成示例数据
|
||||
np.random.seed(42)
|
||||
data = pd.DataFrame({
|
||||
'condition': np.repeat(['对照', '药物 A', '药物 B'], 50),
|
||||
'value': np.concatenate([
|
||||
np.random.normal(100, 15, 50),
|
||||
np.random.normal(120, 20, 50),
|
||||
np.random.normal(140, 18, 50)
|
||||
])
|
||||
})
|
||||
|
||||
# 设置样式
|
||||
sns.set_style('ticks')
|
||||
sns.set_palette(['#0072B2', '#E69F00', '#009E73'])
|
||||
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
|
||||
# 创建小提琴图
|
||||
sns.violinplot(data=data, x='condition', y='value', ax=ax,
|
||||
inner='box', linewidth=0.8)
|
||||
|
||||
# 添加带状图
|
||||
sns.stripplot(data=data, x='condition', y='value', ax=ax,
|
||||
size=2, alpha=0.3, color='black')
|
||||
|
||||
# 自定义
|
||||
ax.set_xlabel('')
|
||||
ax.set_ylabel('表达水平(AU)')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'violin_plot')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 6:带回归线的科学散点图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
|
||||
# 生成具有相关性的数据
|
||||
np.random.seed(42)
|
||||
x = np.random.randn(100)
|
||||
y = 2.5 * x + np.random.randn(100) * 0.8
|
||||
|
||||
# 计算回归
|
||||
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3.5))
|
||||
|
||||
# 散点图
|
||||
ax.scatter(x, y, s=15, alpha=0.6, color='#0072B2', edgecolors='none')
|
||||
|
||||
# 回归线
|
||||
x_line = np.array([x.min(), x.max()])
|
||||
y_line = slope * x_line + intercept
|
||||
ax.plot(x_line, y_line, 'r-', linewidth=1.5, label=f'y = {slope:.2f}x + {intercept:.2f}')
|
||||
|
||||
# 添加统计文本
|
||||
stats_text = f'$R^2$ = {r_value**2:.3f}\n$p$ < 0.001' if p_value < 0.001 else f'$R^2$ = {r_value**2:.3f}\n$p$ = {p_value:.3f}'
|
||||
ax.text(0.05, 0.95, stats_text, transform=ax.transAxes,
|
||||
verticalalignment='top', fontsize=7,
|
||||
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8, edgecolor='gray', linewidth=0.5))
|
||||
|
||||
# 自定义
|
||||
ax.set_xlabel('预测变量')
|
||||
ax.set_ylabel('响应变量')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'scatter_regression')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 7:带阴影误差的时间序列图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 生成时间序列数据
|
||||
np.random.seed(42)
|
||||
time = np.linspace(0, 24, 100)
|
||||
n_replicates = 5
|
||||
|
||||
# 模拟多个重复
|
||||
data = np.array([10 * np.exp(-time/10) + np.random.normal(0, 0.5, 100)
|
||||
for _ in range(n_replicates)])
|
||||
|
||||
# 计算均值和标准误差
|
||||
mean = data.mean(axis=0)
|
||||
sem = data.std(axis=0) / np.sqrt(n_replicates)
|
||||
|
||||
# 创建图形
|
||||
fig, ax = plt.subplots(figsize=(4, 2.5))
|
||||
|
||||
# 绘制均值线
|
||||
ax.plot(time, mean, linewidth=1.5, color='#0072B2', label='均值 ± SEM')
|
||||
|
||||
# 添加阴影误差区域
|
||||
ax.fill_between(time, mean - sem, mean + sem,
|
||||
alpha=0.3, color='#0072B2', linewidth=0)
|
||||
|
||||
# 自定义
|
||||
ax.set_xlabel('时间(小时)')
|
||||
ax.set_ylabel('浓度(μM)')
|
||||
ax.legend(frameon=False, loc='upper right')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.set_xlim(0, 24)
|
||||
ax.set_ylim(0, 12)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'timeseries_shaded')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 8:Plotly 交互式图形
|
||||
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
import numpy as np
|
||||
|
||||
# 生成数据
|
||||
np.random.seed(42)
|
||||
x = np.random.randn(100)
|
||||
y = 2*x + np.random.randn(100)
|
||||
colors = np.random.choice(['组 A', '组 B'], 100)
|
||||
|
||||
# Plotly 的 Okabe-Ito 颜色
|
||||
okabe_ito_plotly = ['#E69F00', '#56B4E9']
|
||||
|
||||
# 创建图形
|
||||
fig = go.Figure()
|
||||
|
||||
for group, color in zip(['组 A', '组 B'], okabe_ito_plotly):
|
||||
mask = colors == group
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x[mask], y=y[mask],
|
||||
mode='markers',
|
||||
name=group,
|
||||
marker=dict(size=6, color=color, opacity=0.6)
|
||||
))
|
||||
|
||||
# 更新布局以达到发表质量
|
||||
fig.update_layout(
|
||||
width=500,
|
||||
height=400,
|
||||
font=dict(family='Arial, sans-serif', size=10),
|
||||
plot_bgcolor='white',
|
||||
xaxis=dict(
|
||||
title='变量 X',
|
||||
showgrid=False,
|
||||
showline=True,
|
||||
linewidth=1,
|
||||
linecolor='black',
|
||||
mirror=False
|
||||
),
|
||||
yaxis=dict(
|
||||
title='变量 Y',
|
||||
showgrid=False,
|
||||
showline=True,
|
||||
linewidth=1,
|
||||
linecolor='black',
|
||||
mirror=False
|
||||
),
|
||||
legend=dict(
|
||||
x=0.02,
|
||||
y=0.98,
|
||||
bgcolor='rgba(255,255,255,0.8)',
|
||||
bordercolor='gray',
|
||||
borderwidth=0.5
|
||||
)
|
||||
)
|
||||
|
||||
# 保存为静态图像(需要 kaleido)
|
||||
fig.write_image('plotly_scatter.png', width=500, height=400, scale=3) # scale=3 约等于 300 DPI
|
||||
fig.write_html('plotly_scatter.html') # 交互式版本
|
||||
|
||||
fig.show()
|
||||
```
|
||||
|
||||
## 示例 9:带显著性标记的分组柱状图
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# 数据
|
||||
categories = ['WT', '突变体 A', '突变体 B']
|
||||
control_means = [100, 85, 70]
|
||||
control_sem = [5, 6, 5]
|
||||
treatment_means = [100, 120, 140]
|
||||
treatment_sem = [6, 8, 9]
|
||||
|
||||
x = np.arange(len(categories))
|
||||
width = 0.35
|
||||
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
|
||||
# 创建柱状条
|
||||
bars1 = ax.bar(x - width/2, control_means, width, yerr=control_sem,
|
||||
capsize=3, label='对照', color='#0072B2', alpha=0.8)
|
||||
bars2 = ax.bar(x + width/2, treatment_means, width, yerr=treatment_sem,
|
||||
capsize=3, label='处理', color='#E69F00', alpha=0.8)
|
||||
|
||||
# 添加显著性标记
|
||||
def add_significance_bar(ax, x1, x2, y, h, text):
|
||||
"""在两个柱状条之间添加显著性标记线"""
|
||||
ax.plot([x1, x1, x2, x2], [y, y+h, y+h, y], linewidth=0.8, c='black')
|
||||
ax.text((x1+x2)/2, y+h, text, ha='center', va='bottom', fontsize=7)
|
||||
|
||||
# 标记显著差异
|
||||
add_significance_bar(ax, x[1]-width/2, x[1]+width/2, 135, 3, '***')
|
||||
add_significance_bar(ax, x[2]-width/2, x[2]+width/2, 155, 3, '***')
|
||||
|
||||
# 自定义
|
||||
ax.set_ylabel('活性(占 WT 对照的百分比)')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(categories)
|
||||
ax.legend(frameon=False, loc='upper left')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.set_ylim(0, 180)
|
||||
|
||||
# 添加显著性注释
|
||||
ax.text(0.98, 0.02, '*** p < 0.001', transform=ax.transAxes,
|
||||
ha='right', va='bottom', fontsize=6)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'grouped_bar_significance')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 示例 10:Nature 级别可发表图形
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from string import ascii_lowercase
|
||||
|
||||
# Nature 规格:89mm 单栏
|
||||
inch_per_mm = 0.0393701
|
||||
width_mm = 89
|
||||
height_mm = 110
|
||||
figsize = (width_mm * inch_per_mm, height_mm * inch_per_mm)
|
||||
|
||||
fig = plt.figure(figsize=figsize)
|
||||
gs = fig.add_gridspec(3, 2, hspace=0.5, wspace=0.4,
|
||||
left=0.12, right=0.95, top=0.96, bottom=0.08)
|
||||
|
||||
# 面板 a:时间过程
|
||||
ax_a = fig.add_subplot(gs[0, :])
|
||||
time = np.linspace(0, 48, 100)
|
||||
for i, label in enumerate(['对照', '处理']):
|
||||
y = (1 + i*0.5) * np.exp(-time/20) * (1 + 0.3*np.sin(time/5))
|
||||
ax_a.plot(time, y, linewidth=1.2, label=label)
|
||||
ax_a.set_xlabel('时间(小时)', fontsize=7)
|
||||
ax_a.set_ylabel('生长(OD$_{600}$)', fontsize=7)
|
||||
ax_a.legend(frameon=False, fontsize=6)
|
||||
ax_a.tick_params(labelsize=6)
|
||||
ax_a.spines['top'].set_visible(False)
|
||||
ax_a.spines['right'].set_visible(False)
|
||||
|
||||
# 面板 b:柱状图
|
||||
ax_b = fig.add_subplot(gs[1, 0])
|
||||
categories = ['A', 'B', 'C']
|
||||
values = [1.0, 1.5, 2.2]
|
||||
errors = [0.1, 0.15, 0.2]
|
||||
ax_b.bar(categories, values, yerr=errors, capsize=2, width=0.6,
|
||||
color='#0072B2', alpha=0.8)
|
||||
ax_b.set_ylabel('倍数变化', fontsize=7)
|
||||
ax_b.tick_params(labelsize=6)
|
||||
ax_b.spines['top'].set_visible(False)
|
||||
ax_b.spines['right'].set_visible(False)
|
||||
|
||||
# 面板 c:热图
|
||||
ax_c = fig.add_subplot(gs[1, 1])
|
||||
data = np.random.randn(8, 6)
|
||||
im = ax_c.imshow(data, cmap='viridis', aspect='auto')
|
||||
ax_c.set_xlabel('样本', fontsize=7)
|
||||
ax_c.set_ylabel('基因', fontsize=7)
|
||||
ax_c.tick_params(labelsize=6)
|
||||
|
||||
# 面板 d:散点图
|
||||
ax_d = fig.add_subplot(gs[2, :])
|
||||
x = np.random.randn(50)
|
||||
y = 2*x + np.random.randn(50)*0.5
|
||||
ax_d.scatter(x, y, s=8, alpha=0.6, color='#E69F00')
|
||||
ax_d.set_xlabel('基因 X 表达量', fontsize=7)
|
||||
ax_d.set_ylabel('基因 Y 表达量', fontsize=7)
|
||||
ax_d.tick_params(labelsize=6)
|
||||
ax_d.spines['top'].set_visible(False)
|
||||
ax_d.spines['right'].set_visible(False)
|
||||
|
||||
# 添加小写面板标签(Nature 风格)
|
||||
for i, ax in enumerate([ax_a, ax_b, ax_c, ax_d]):
|
||||
ax.text(-0.2, 1.1, f'{ascii_lowercase[i]}', transform=ax.transAxes,
|
||||
fontsize=9, fontweight='bold', va='top')
|
||||
|
||||
# 以 Nature 偏好的格式保存
|
||||
fig.savefig('nature_figure.pdf', dpi=1000, bbox_inches='tight',
|
||||
facecolor='white', edgecolor='none')
|
||||
fig.savefig('nature_figure.png', dpi=300, bbox_inches='tight',
|
||||
facecolor='white', edgecolor='none')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## 各库的使用技巧
|
||||
|
||||
### Matplotlib
|
||||
- 使用 `fig.tight_layout()` 或 `constrained_layout=True` 防止重叠
|
||||
- 将 DPI 设置为 300–600 以用于发表
|
||||
- 折线图使用矢量格式(PDF、EPS)
|
||||
- 在 PDF/EPS 文件中嵌入字体
|
||||
|
||||
### Seaborn
|
||||
- 基于 matplotlib 构建,因此所有 matplotlib 的自定义功能均适用
|
||||
- 使用 `sns.set_style('ticks')` 或 `'whitegrid'` 以获得简洁外观
|
||||
- `sns.despine()` 移除顶部和右侧的轴脊线
|
||||
- 使用 `sns.set_palette()` 设置自定义调色板
|
||||
|
||||
### Plotly
|
||||
- 非常适合交互式探索性分析
|
||||
- 使用 `fig.write_image()` 导出静态图像(需要 kaleido 包)
|
||||
- 使用 `scale` 参数控制 DPI(scale=3 ≈ 300 DPI)
|
||||
- 大量调整布局以达到发表质量
|
||||
|
||||
## 通用工作流程
|
||||
|
||||
1. **使用默认设置进行探索**
|
||||
2. **应用发表配置**(参见「设置」部分)
|
||||
3. **创建合适大小的图形**(查看期刊要求)
|
||||
4. **自定义颜色**(使用色盲友好调色板)
|
||||
5. **调整字体和线宽**(在最终尺寸下可读)
|
||||
6. **去除图表垃圾元素**(顶部/右侧轴脊线、过多网格线)
|
||||
7. **添加清晰的标签及单位**
|
||||
8. **用灰度测试**
|
||||
9. **以多种格式保存**(矢量用 PDF,光栅用 PNG)
|
||||
10. **在最终上下文中验证**(导入稿件中检查尺寸)
|
||||
|
||||
## 参考资料
|
||||
|
||||
- Matplotlib 文档:https://matplotlib.org/
|
||||
- Seaborn 画廊:https://seaborn.pydata.org/examples/index.html
|
||||
- Plotly 文档:https://plotly.com/python/
|
||||
- Nature Methods Points of View:数据可视化专栏存档
|
||||
@@ -0,0 +1,205 @@
|
||||
# 可投稿级别的图片制作指南
|
||||
|
||||
## 核心原则
|
||||
|
||||
科学图片必须清晰、准确且易于理解。可投稿级别的图片遵循以下基本原则:
|
||||
|
||||
1. **清晰性**:信息应当能够被立即理解
|
||||
2. **准确性**:数据呈现必须真实且未经篡改
|
||||
3. **可访问性**:图片应能被所有读者解读,包括有视觉障碍的人士
|
||||
4. **专业性**:外观整洁精致,适合同行评审期刊
|
||||
|
||||
## 分辨率与文件格式
|
||||
|
||||
### 分辨率要求
|
||||
- **光栅图像(照片、显微图像)**:最终印刷尺寸下 300-600 DPI
|
||||
- **线条图与图表**:600-1200 DPI(或矢量格式)
|
||||
- **组合图片**:300-600 DPI
|
||||
|
||||
### 文件格式
|
||||
- **矢量格式(图表/绘图首选)**:PDF、EPS、SVG
|
||||
- 无限缩放,不损失质量
|
||||
- 线条图文件体积更小
|
||||
- 最佳用途:图表、示意图、结构图
|
||||
|
||||
- **光栅格式**:TIFF、PNG(科学数据绝不使用 JPEG)
|
||||
- 用于:照片、显微图像、连续色调图像
|
||||
- TIFF:无损,被广泛接受
|
||||
- PNG:无损,适合网络和补充材料
|
||||
- **绝不使用 JPEG**:有损压缩会引入伪影
|
||||
|
||||
### 尺寸规范
|
||||
- **单栏**:宽 85-90 mm(3.35-3.54 英寸)
|
||||
- **1.5 栏**:宽 114-120 mm(4.49-4.72 英寸)
|
||||
- **双栏**:宽 174-180 mm(6.85-7.08 英寸)
|
||||
- **最大高度**:通常为 230-240 mm(9-9.5 英寸)
|
||||
|
||||
## 排版
|
||||
|
||||
### 字体指南
|
||||
- **字体系列**:大部分期刊使用无衬线字体(Arial、Helvetica、Calibri)
|
||||
- 部分期刊要求特定字体(请查阅其投稿指南)
|
||||
- 稿件中所有图片的字体应保持一致
|
||||
|
||||
- **最终印刷尺寸下的字号**:
|
||||
- 坐标轴标签:最低 7-9 pt
|
||||
- 刻度标签:最低 6-8 pt
|
||||
- 图例:6-8 pt
|
||||
- 面板标签(A、B、C):8-12 pt,加粗
|
||||
- 标题:多面板图片中通常不推荐使用
|
||||
|
||||
- **字重**:大部分文本使用常规字重;仅面板标签使用加粗
|
||||
|
||||
### 文本最佳实践
|
||||
- 坐标轴标签使用句首大写("Time (hours)",而非"TIME (HOURS)")
|
||||
- 单位放在括号内
|
||||
- 除非空间受限,否则避免缩写(在图注中说明其全称)
|
||||
- 最终尺寸下文本不得小于 5-6 pt
|
||||
|
||||
## 颜色使用
|
||||
|
||||
### 颜色选择原则
|
||||
1. **色盲友好**:约 8% 的男性存在色觉障碍
|
||||
- 避免使用红/绿搭配
|
||||
- 使用蓝/橙、蓝/黄,或增加纹理/图案
|
||||
- 使用色盲模拟器进行测试
|
||||
|
||||
2. **有目的性的颜色**:颜色应传达含义,而非仅为美观
|
||||
- 使用颜色区分类别或突出关键数据
|
||||
- 跨图片保持颜色一致性(相同的处理 = 相同的颜色)
|
||||
|
||||
3. **印刷考量**:
|
||||
- 颜色在印刷品与屏幕上的显示效果可能不同
|
||||
- 印刷品使用 CMYK 色彩空间,数字版使用 RGB
|
||||
- 确保足够的对比度(尤其要考虑灰度转换)
|
||||
|
||||
### 推荐色板
|
||||
- **定性(分类)**:ColorBrewer、Okabe-Ito 色板
|
||||
- **顺序(从低到高)**:Viridis、Cividis、Blues、Oranges
|
||||
- **发散(从负到正)**:RdBu、PuOr、BrBG(确保色盲安全)
|
||||
|
||||
### 灰度兼容性
|
||||
- 所有图片应在灰度模式下仍可解读
|
||||
- 使用不同的线型(实线、虚线、点线)和标记
|
||||
- 为条形和区域添加填充纹理/斜线
|
||||
|
||||
## 布局与构图
|
||||
|
||||
### 多面板图片
|
||||
- **面板标签**:在左上角使用加粗大写字母(A、B、C)
|
||||
- **间距**:面板之间留足空白
|
||||
- **对齐**:尽可能沿面板边缘或坐标轴对齐
|
||||
- **尺寸**:相关面板的尺寸应保持一致
|
||||
- **排列**:逻辑顺序(从左到右、从上到下)
|
||||
|
||||
### 图表元素
|
||||
|
||||
#### 坐标轴
|
||||
- **轴线**:粗细 0.5-1 pt
|
||||
- **刻度线**:一致地指向内侧或外侧
|
||||
- **刻度密度**:足以读取数值,不宜过密(通常 4-7 个主刻度)
|
||||
- **坐标轴标签**:所有图表必须标注,且标明单位
|
||||
- **坐标轴范围**:条形图应从零开始(除非在科学上不合理)
|
||||
|
||||
#### 线条与标记
|
||||
- **线宽**:数据线 1-2 pt;参考线 0.5-1 pt
|
||||
- **标记大小**:3-6 pt,大于线宽
|
||||
- **标记类型**:多个数据系列时使用不同标记加以区分(圆形、方形、三角形)
|
||||
- **误差棒**:宽 0.5-1 pt;若合适则加上端帽
|
||||
|
||||
#### 图例
|
||||
- **位置**:空间允许时放在绘图区域内,否则放在外部
|
||||
- **边框**:可选;如使用,则用细线(0.5 pt)
|
||||
- **顺序**:与数据出现的顺序一致(从上到下或从左到右)
|
||||
- **内容**:简洁描述;详细信息放在图注中
|
||||
|
||||
### 空白与边距
|
||||
- 去除图表周围不必要的空白
|
||||
- 保持一致的边距
|
||||
- 在 matplotlib 中使用 `tight_layout()` 或 `constrained_layout=True`
|
||||
|
||||
## 数据呈现最佳实践
|
||||
|
||||
### 统计严谨性
|
||||
- **误差棒**:始终显示不确定性(SD、SEM、CI),并在图注中说明使用的是哪种
|
||||
- **样本量**:在图片或图注中注明 n
|
||||
- **显著性**:清晰标注统计显著性(*、**、***)
|
||||
- **重复实验**:尽可能展示单个数据点,而非仅显示汇总统计量
|
||||
|
||||
### 合适的图表类型
|
||||
- **条形图**:比较离散类别;纵轴始终从零开始
|
||||
- **折线图**:时间序列或连续关系
|
||||
- **散点图**:变量之间的相关性;若合适则添加回归线
|
||||
- **箱线图**:分布比较;显示异常值
|
||||
- **热力图**:矩阵数据、相关性、表达模式
|
||||
- **小提琴图**:分布形状比较(对于双峰数据优于箱线图)
|
||||
|
||||
### 避免失真
|
||||
- **不使用 3D 效果**:会扭曲数值感知
|
||||
- **不使用不必要的装饰**:不使用渐变、阴影或图表垃圾
|
||||
- **一致的尺度**:可比对的面板使用相同的尺度
|
||||
- **不截断坐标轴**:除非有清晰标注且具备科学依据
|
||||
- **线性 vs 对数尺度**:选择合适的尺度;始终清晰标注
|
||||
|
||||
## 可访问性
|
||||
|
||||
### 色盲考量
|
||||
- 使用在线模拟器测试(如 Coblis、Color Oracle)
|
||||
- 除颜色外,同时使用图案/纹理
|
||||
- 如有需要,在补充材料中提供替代呈现方式
|
||||
|
||||
### 视觉障碍
|
||||
- 元素之间高对比度
|
||||
- 线条足够粗(最低 0.5 pt)
|
||||
- 布局清晰简洁
|
||||
|
||||
### 数据可用性
|
||||
- 在补充材料中包含数据表
|
||||
- 提供图表对应的源数据文件
|
||||
- 对于在线补充材料,可考虑交互式图片
|
||||
|
||||
## 需避免的常见错误
|
||||
|
||||
1. **字号太小**:文本在最终印刷尺寸下难以辨认
|
||||
2. **分辨率过低**:图像出现像素化或模糊
|
||||
3. **图表垃圾**:不必要的网格线、3D 效果、装饰
|
||||
4. **颜色选择不当**:红/绿搭配、对比度低
|
||||
5. **元素缺失**:缺少坐标轴标签、单位、误差棒
|
||||
6. **样式不一致**:同一图片内或跨图片使用不同的字体/字号
|
||||
7. **数据失真**:截断坐标轴、尺度不当、3D 效果
|
||||
8. **JPEG 压缩**:文字和线条周围出现伪影
|
||||
9. **信息过密**:在一张图表中塞入过多数据系列
|
||||
10. **图例不可访问**:导出后图例超出图片边界
|
||||
|
||||
## 图片检查清单
|
||||
|
||||
投稿前,请确认:
|
||||
|
||||
- [ ] 分辨率满足期刊要求(光栅图像 300+ DPI)
|
||||
- [ ] 文件格式可被接受(图表用矢量格式,图像用 TIFF/PNG)
|
||||
- [ ] 图片尺寸符合期刊规范
|
||||
- [ ] 所有文本在最终尺寸下可读(最低 6-7 pt)
|
||||
- [ ] 字体保持一致且已嵌入(针对 PDF/EPS)
|
||||
- [ ] 颜色为色盲友好
|
||||
- [ ] 图片在灰度模式下仍可解读
|
||||
- [ ] 所有坐标轴均已标注且带有单位
|
||||
- [ ] 误差棒或不确定性指标已呈现
|
||||
- [ ] 统计显著性已标注(如适用)
|
||||
- [ ] 面板标签已标注且保持一致(A、B、C)
|
||||
- [ ] 图例清晰完整
|
||||
- [ ] 无图表垃圾或不必要元素
|
||||
- [ ] 文件命名遵循期刊惯例
|
||||
- [ ] 图片图注内容完整
|
||||
- [ ] 源数据已提供
|
||||
|
||||
## 各期刊特有注意事项
|
||||
|
||||
务必查阅具体期刊的作者投稿指南。常见差异包括:
|
||||
|
||||
- **Nature 系列期刊**:RGB、最低 300 DPI、特定的尺寸要求
|
||||
- **Science**:EPS 或高分辨率 TIFF、特定的字体要求
|
||||
- **Cell Press**:首选 PDF 或 EPS、使用 Arial 或 Helvetica 字体
|
||||
- **PLOS**:TIFF 或 EPS、特定的色彩空间要求
|
||||
- **ACS 期刊**:应用程序文件(AI、EPS)或高分辨率 TIFF
|
||||
|
||||
详细规范请参阅 `journal_requirements.md`。
|
||||
@@ -0,0 +1,343 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Figure Export Utilities for Publication-Ready Scientific Figures
|
||||
|
||||
This module provides utilities to export matplotlib figures in publication-ready
|
||||
formats with appropriate settings for various journals.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
|
||||
def save_publication_figure(
|
||||
fig: plt.Figure,
|
||||
filename: Union[str, Path],
|
||||
formats: List[str] = ['pdf', 'png'],
|
||||
dpi: int = 300,
|
||||
transparent: bool = False,
|
||||
bbox_inches: str = 'tight',
|
||||
pad_inches: float = 0.1,
|
||||
facecolor: str = 'white',
|
||||
**kwargs
|
||||
) -> List[Path]:
|
||||
"""
|
||||
Save a matplotlib figure in multiple formats with publication-quality settings.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fig : matplotlib.figure.Figure
|
||||
The figure to save
|
||||
filename : str or Path
|
||||
Base filename (without extension)
|
||||
formats : list of str, default ['pdf', 'png']
|
||||
List of file formats to save. Options: 'pdf', 'png', 'eps', 'svg', 'tiff'
|
||||
dpi : int, default 300
|
||||
Resolution for raster formats (png, tiff). 300 DPI is minimum for most journals
|
||||
transparent : bool, default False
|
||||
If True, save with transparent background
|
||||
bbox_inches : str, default 'tight'
|
||||
Bounding box specification. 'tight' removes excess whitespace
|
||||
pad_inches : float, default 0.1
|
||||
Padding around the figure when bbox_inches='tight'
|
||||
facecolor : str, default 'white'
|
||||
Background color (ignored if transparent=True)
|
||||
**kwargs
|
||||
Additional keyword arguments passed to fig.savefig()
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of Path
|
||||
List of paths to saved files
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.plot([1, 2, 3], [1, 4, 9])
|
||||
>>> save_publication_figure(fig, 'my_plot', formats=['pdf', 'png'], dpi=600)
|
||||
['my_plot.pdf', 'my_plot.png']
|
||||
"""
|
||||
filename = Path(filename)
|
||||
base_name = filename.stem
|
||||
output_dir = filename.parent if filename.parent.exists() else Path.cwd()
|
||||
|
||||
saved_files = []
|
||||
|
||||
for fmt in formats:
|
||||
output_file = output_dir / f"{base_name}.{fmt}"
|
||||
|
||||
# Set format-specific parameters
|
||||
save_kwargs = {
|
||||
'dpi': dpi,
|
||||
'bbox_inches': bbox_inches,
|
||||
'pad_inches': pad_inches,
|
||||
'facecolor': facecolor if not transparent else 'none',
|
||||
'edgecolor': 'none',
|
||||
'transparent': transparent,
|
||||
'format': fmt,
|
||||
}
|
||||
|
||||
# Update with user-provided kwargs
|
||||
save_kwargs.update(kwargs)
|
||||
|
||||
# Adjust DPI for vector formats (DPI less relevant)
|
||||
if fmt in ['pdf', 'eps', 'svg']:
|
||||
save_kwargs['dpi'] = min(dpi, 300) # Lower DPI for embedded rasters in vector
|
||||
|
||||
try:
|
||||
fig.savefig(output_file, **save_kwargs)
|
||||
saved_files.append(output_file)
|
||||
print(f"✓ Saved: {output_file}")
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to save {output_file}: {e}")
|
||||
|
||||
return saved_files
|
||||
|
||||
|
||||
def save_for_journal(
|
||||
fig: plt.Figure,
|
||||
filename: Union[str, Path],
|
||||
journal: str,
|
||||
figure_type: str = 'combination'
|
||||
) -> List[Path]:
|
||||
"""
|
||||
Save figure with journal-specific requirements.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fig : matplotlib.figure.Figure
|
||||
The figure to save
|
||||
filename : str or Path
|
||||
Base filename (without extension)
|
||||
journal : str
|
||||
Journal name. Options: 'nature', 'science', 'cell', 'plos', 'acs', 'ieee'
|
||||
figure_type : str, default 'combination'
|
||||
Type of figure. Options: 'line_art', 'photo', 'combination'
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of Path
|
||||
List of paths to saved files
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.plot([1, 2, 3], [1, 4, 9])
|
||||
>>> save_for_journal(fig, 'figure1', journal='nature', figure_type='line_art')
|
||||
"""
|
||||
journal = journal.lower()
|
||||
|
||||
# Define journal-specific requirements
|
||||
journal_specs = {
|
||||
'nature': {
|
||||
'line_art': {'formats': ['pdf', 'eps'], 'dpi': 1000},
|
||||
'photo': {'formats': ['tiff'], 'dpi': 300},
|
||||
'combination': {'formats': ['pdf'], 'dpi': 600},
|
||||
},
|
||||
'science': {
|
||||
'line_art': {'formats': ['eps', 'pdf'], 'dpi': 1000},
|
||||
'photo': {'formats': ['tiff'], 'dpi': 300},
|
||||
'combination': {'formats': ['eps'], 'dpi': 600},
|
||||
},
|
||||
'cell': {
|
||||
'line_art': {'formats': ['pdf', 'eps'], 'dpi': 1000},
|
||||
'photo': {'formats': ['tiff'], 'dpi': 300},
|
||||
'combination': {'formats': ['pdf'], 'dpi': 600},
|
||||
},
|
||||
'plos': {
|
||||
'line_art': {'formats': ['pdf', 'eps'], 'dpi': 600},
|
||||
'photo': {'formats': ['tiff', 'png'], 'dpi': 300},
|
||||
'combination': {'formats': ['tiff'], 'dpi': 300},
|
||||
},
|
||||
'acs': {
|
||||
'line_art': {'formats': ['tiff', 'pdf'], 'dpi': 600},
|
||||
'photo': {'formats': ['tiff'], 'dpi': 300},
|
||||
'combination': {'formats': ['tiff'], 'dpi': 600},
|
||||
},
|
||||
'ieee': {
|
||||
'line_art': {'formats': ['pdf', 'eps'], 'dpi': 600},
|
||||
'photo': {'formats': ['tiff'], 'dpi': 300},
|
||||
'combination': {'formats': ['pdf'], 'dpi': 300},
|
||||
},
|
||||
}
|
||||
|
||||
if journal not in journal_specs:
|
||||
available = ', '.join(journal_specs.keys())
|
||||
raise ValueError(f"Journal '{journal}' not recognized. Available: {available}")
|
||||
|
||||
if figure_type not in journal_specs[journal]:
|
||||
available = ', '.join(journal_specs[journal].keys())
|
||||
raise ValueError(f"Figure type '{figure_type}' not valid. Available: {available}")
|
||||
|
||||
specs = journal_specs[journal][figure_type]
|
||||
|
||||
print(f"Saving for {journal.upper()} ({figure_type}):")
|
||||
print(f" Formats: {', '.join(specs['formats'])}")
|
||||
print(f" DPI: {specs['dpi']}")
|
||||
|
||||
return save_publication_figure(
|
||||
fig=fig,
|
||||
filename=filename,
|
||||
formats=specs['formats'],
|
||||
dpi=specs['dpi']
|
||||
)
|
||||
|
||||
|
||||
def check_figure_size(fig: plt.Figure, journal: str = 'nature') -> dict:
|
||||
"""
|
||||
Check if figure dimensions are appropriate for journal requirements.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fig : matplotlib.figure.Figure
|
||||
The figure to check
|
||||
journal : str, default 'nature'
|
||||
Journal name
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Dictionary with figure dimensions and compliance status
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> fig = plt.figure(figsize=(3.5, 3))
|
||||
>>> info = check_figure_size(fig, journal='nature')
|
||||
>>> print(info)
|
||||
"""
|
||||
journal = journal.lower()
|
||||
|
||||
# Get figure dimensions in inches
|
||||
width_inches, height_inches = fig.get_size_inches()
|
||||
width_mm = width_inches * 25.4
|
||||
height_mm = height_inches * 25.4
|
||||
|
||||
# Journal specifications (widths in mm)
|
||||
specs = {
|
||||
'nature': {'single': 89, 'double': 183, 'max_height': 247},
|
||||
'science': {'single': 55, 'double': 175, 'max_height': 233},
|
||||
'cell': {'single': 85, 'double': 178, 'max_height': 230},
|
||||
'plos': {'single': 83, 'double': 173, 'max_height': 233},
|
||||
'acs': {'single': 82.5, 'double': 178, 'max_height': 247},
|
||||
}
|
||||
|
||||
if journal not in specs:
|
||||
journal_spec = specs['nature']
|
||||
print(f"Warning: Journal '{journal}' not found, using Nature specifications")
|
||||
else:
|
||||
journal_spec = specs[journal]
|
||||
|
||||
# Determine column type
|
||||
column_type = None
|
||||
width_ok = False
|
||||
|
||||
tolerance = 5 # mm tolerance
|
||||
if abs(width_mm - journal_spec['single']) < tolerance:
|
||||
column_type = 'single'
|
||||
width_ok = True
|
||||
elif abs(width_mm - journal_spec['double']) < tolerance:
|
||||
column_type = 'double'
|
||||
width_ok = True
|
||||
|
||||
height_ok = height_mm <= journal_spec['max_height']
|
||||
|
||||
result = {
|
||||
'width_inches': width_inches,
|
||||
'height_inches': height_inches,
|
||||
'width_mm': width_mm,
|
||||
'height_mm': height_mm,
|
||||
'journal': journal,
|
||||
'column_type': column_type,
|
||||
'width_ok': width_ok,
|
||||
'height_ok': height_ok,
|
||||
'compliant': width_ok and height_ok,
|
||||
'recommendations': {
|
||||
'single_column_mm': journal_spec['single'],
|
||||
'double_column_mm': journal_spec['double'],
|
||||
'max_height_mm': journal_spec['max_height'],
|
||||
}
|
||||
}
|
||||
|
||||
# Print report
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Figure Size Check for {journal.upper()}")
|
||||
print(f"{'='*60}")
|
||||
print(f"Current size: {width_mm:.1f} × {height_mm:.1f} mm")
|
||||
print(f" ({width_inches:.2f} × {height_inches:.2f} inches)")
|
||||
print(f"\n{journal.upper()} specifications:")
|
||||
print(f" Single column: {journal_spec['single']} mm")
|
||||
print(f" Double column: {journal_spec['double']} mm")
|
||||
print(f" Max height: {journal_spec['max_height']} mm")
|
||||
print(f"\nCompliance:")
|
||||
print(f" Width: {'✓ OK' if width_ok else '✗ Non-standard'} ({column_type or 'custom'})")
|
||||
print(f" Height: {'✓ OK' if height_ok else '✗ Too tall'}")
|
||||
print(f" Overall: {'✓ COMPLIANT' if result['compliant'] else '✗ NEEDS ADJUSTMENT'}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def verify_font_embedding(pdf_path: Union[str, Path]) -> bool:
|
||||
"""
|
||||
Check if fonts are embedded in a PDF file.
|
||||
|
||||
Note: This requires PyPDF2 or a similar library to be installed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pdf_path : str or Path
|
||||
Path to PDF file
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if fonts are embedded, False otherwise
|
||||
"""
|
||||
try:
|
||||
from PyPDF2 import PdfReader
|
||||
except ImportError:
|
||||
print("Warning: PyPDF2 not installed. Cannot verify font embedding.")
|
||||
print("Install with: pip install PyPDF2")
|
||||
return None
|
||||
|
||||
pdf_path = Path(pdf_path)
|
||||
|
||||
try:
|
||||
reader = PdfReader(pdf_path)
|
||||
# This is a simplified check; full verification is complex
|
||||
print(f"PDF has {len(reader.pages)} page(s)")
|
||||
print("Note: Full font embedding verification requires detailed PDF inspection.")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error reading PDF: {e}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
import numpy as np
|
||||
|
||||
# Create example figure
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
x = np.linspace(0, 10, 100)
|
||||
ax.plot(x, np.sin(x), label='sin(x)')
|
||||
ax.plot(x, np.cos(x), label='cos(x)')
|
||||
ax.set_xlabel('x')
|
||||
ax.set_ylabel('y')
|
||||
ax.legend()
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# Check size
|
||||
check_figure_size(fig, journal='nature')
|
||||
|
||||
# Save in multiple formats
|
||||
print("\nSaving figure...")
|
||||
save_publication_figure(fig, 'example_figure', formats=['pdf', 'png'], dpi=300)
|
||||
|
||||
# Save with journal-specific requirements
|
||||
print("\nSaving for Nature...")
|
||||
save_for_journal(fig, 'example_figure_nature', journal='nature', figure_type='line_art')
|
||||
|
||||
plt.close(fig)
|
||||
@@ -0,0 +1,416 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Matplotlib Style Presets for Publication-Ready Scientific Figures
|
||||
|
||||
This module provides pre-configured matplotlib styles optimized for
|
||||
different journals and use cases.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
# Okabe-Ito colorblind-friendly palette
|
||||
OKABE_ITO_COLORS = [
|
||||
'#E69F00', # Orange
|
||||
'#56B4E9', # Sky Blue
|
||||
'#009E73', # Bluish Green
|
||||
'#F0E442', # Yellow
|
||||
'#0072B2', # Blue
|
||||
'#D55E00', # Vermillion
|
||||
'#CC79A7', # Reddish Purple
|
||||
'#000000' # Black
|
||||
]
|
||||
|
||||
# Paul Tol palettes
|
||||
TOL_BRIGHT = ['#4477AA', '#EE6677', '#228833', '#CCBB44', '#66CCEE', '#AA3377', '#BBBBBB']
|
||||
TOL_MUTED = ['#332288', '#88CCEE', '#44AA99', '#117733', '#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
TOL_HIGH_CONTRAST = ['#004488', '#DDAA33', '#BB5566']
|
||||
|
||||
# Wong palette
|
||||
WONG_COLORS = ['#000000', '#E69F00', '#56B4E9', '#009E73', '#F0E442', '#0072B2', '#D55E00', '#CC79A7']
|
||||
|
||||
|
||||
def get_base_style() -> Dict[str, Any]:
|
||||
"""
|
||||
Get base publication-quality style settings.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Dictionary of matplotlib rcParams
|
||||
"""
|
||||
return {
|
||||
# Figure
|
||||
'figure.dpi': 100, # Display DPI (changed on save)
|
||||
'figure.facecolor': 'white',
|
||||
'figure.autolayout': False,
|
||||
'figure.constrained_layout.use': True,
|
||||
|
||||
# Font
|
||||
'font.size': 8,
|
||||
'font.family': 'sans-serif',
|
||||
'font.sans-serif': ['Arial', 'Helvetica', 'DejaVu Sans'],
|
||||
|
||||
# Axes
|
||||
'axes.linewidth': 0.5,
|
||||
'axes.labelsize': 9,
|
||||
'axes.titlesize': 9,
|
||||
'axes.labelweight': 'normal',
|
||||
'axes.spines.top': False,
|
||||
'axes.spines.right': False,
|
||||
'axes.spines.left': True,
|
||||
'axes.spines.bottom': True,
|
||||
'axes.edgecolor': 'black',
|
||||
'axes.labelcolor': 'black',
|
||||
'axes.axisbelow': True,
|
||||
'axes.prop_cycle': mpl.cycler(color=OKABE_ITO_COLORS),
|
||||
|
||||
# Grid
|
||||
'axes.grid': False,
|
||||
|
||||
# Ticks
|
||||
'xtick.major.size': 3,
|
||||
'xtick.minor.size': 2,
|
||||
'xtick.major.width': 0.5,
|
||||
'xtick.minor.width': 0.5,
|
||||
'xtick.labelsize': 7,
|
||||
'xtick.direction': 'out',
|
||||
'ytick.major.size': 3,
|
||||
'ytick.minor.size': 2,
|
||||
'ytick.major.width': 0.5,
|
||||
'ytick.minor.width': 0.5,
|
||||
'ytick.labelsize': 7,
|
||||
'ytick.direction': 'out',
|
||||
|
||||
# Lines
|
||||
'lines.linewidth': 1.5,
|
||||
'lines.markersize': 4,
|
||||
'lines.markeredgewidth': 0.5,
|
||||
|
||||
# Legend
|
||||
'legend.fontsize': 7,
|
||||
'legend.frameon': False,
|
||||
'legend.loc': 'best',
|
||||
|
||||
# Savefig
|
||||
'savefig.dpi': 300,
|
||||
'savefig.format': 'pdf',
|
||||
'savefig.bbox': 'tight',
|
||||
'savefig.pad_inches': 0.05,
|
||||
'savefig.transparent': False,
|
||||
'savefig.facecolor': 'white',
|
||||
|
||||
# Image
|
||||
'image.cmap': 'viridis',
|
||||
'image.aspect': 'auto',
|
||||
}
|
||||
|
||||
|
||||
def apply_publication_style(style_name: str = 'default') -> None:
|
||||
"""
|
||||
Apply a pre-configured publication style.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
style_name : str, default 'default'
|
||||
Name of the style to apply. Options:
|
||||
- 'default': General publication style
|
||||
- 'nature': Nature journal style
|
||||
- 'science': Science journal style
|
||||
- 'cell': Cell Press style
|
||||
- 'minimal': Minimal clean style
|
||||
- 'presentation': Larger fonts for presentations
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> apply_publication_style('nature')
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.plot([1, 2, 3], [1, 4, 9])
|
||||
"""
|
||||
base_style = get_base_style()
|
||||
|
||||
# Style-specific modifications
|
||||
if style_name == 'nature':
|
||||
base_style.update({
|
||||
'font.size': 7,
|
||||
'axes.labelsize': 8,
|
||||
'axes.titlesize': 8,
|
||||
'xtick.labelsize': 6,
|
||||
'ytick.labelsize': 6,
|
||||
'legend.fontsize': 6,
|
||||
'savefig.dpi': 600,
|
||||
})
|
||||
|
||||
elif style_name == 'science':
|
||||
base_style.update({
|
||||
'font.size': 7,
|
||||
'axes.labelsize': 8,
|
||||
'xtick.labelsize': 6,
|
||||
'ytick.labelsize': 6,
|
||||
'legend.fontsize': 6,
|
||||
'savefig.dpi': 600,
|
||||
})
|
||||
|
||||
elif style_name == 'cell':
|
||||
base_style.update({
|
||||
'font.size': 8,
|
||||
'axes.labelsize': 9,
|
||||
'xtick.labelsize': 7,
|
||||
'ytick.labelsize': 7,
|
||||
'legend.fontsize': 7,
|
||||
'savefig.dpi': 600,
|
||||
})
|
||||
|
||||
elif style_name == 'minimal':
|
||||
base_style.update({
|
||||
'axes.linewidth': 0.8,
|
||||
'xtick.major.width': 0.8,
|
||||
'ytick.major.width': 0.8,
|
||||
'lines.linewidth': 2,
|
||||
})
|
||||
|
||||
elif style_name == 'presentation':
|
||||
base_style.update({
|
||||
'font.size': 14,
|
||||
'axes.labelsize': 16,
|
||||
'axes.titlesize': 18,
|
||||
'xtick.labelsize': 12,
|
||||
'ytick.labelsize': 12,
|
||||
'legend.fontsize': 12,
|
||||
'axes.linewidth': 1.5,
|
||||
'lines.linewidth': 2.5,
|
||||
'lines.markersize': 8,
|
||||
})
|
||||
|
||||
elif style_name != 'default':
|
||||
print(f"Warning: Style '{style_name}' not recognized. Using 'default'.")
|
||||
|
||||
# Apply the style
|
||||
plt.rcParams.update(base_style)
|
||||
print(f"✓ Applied '{style_name}' publication style")
|
||||
|
||||
|
||||
def set_color_palette(palette_name: str = 'okabe_ito') -> None:
|
||||
"""
|
||||
Set a colorblind-friendly color palette.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
palette_name : str, default 'okabe_ito'
|
||||
Name of the palette. Options:
|
||||
- 'okabe_ito': Okabe-Ito palette (8 colors)
|
||||
- 'wong': Wong palette (8 colors)
|
||||
- 'tol_bright': Paul Tol bright palette (7 colors)
|
||||
- 'tol_muted': Paul Tol muted palette (9 colors)
|
||||
- 'tol_high_contrast': Paul Tol high contrast (3 colors)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> set_color_palette('tol_muted')
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> for i in range(5):
|
||||
... ax.plot([1, 2, 3], [i, i+1, i+2])
|
||||
"""
|
||||
palettes = {
|
||||
'okabe_ito': OKABE_ITO_COLORS,
|
||||
'wong': WONG_COLORS,
|
||||
'tol_bright': TOL_BRIGHT,
|
||||
'tol_muted': TOL_MUTED,
|
||||
'tol_high_contrast': TOL_HIGH_CONTRAST,
|
||||
}
|
||||
|
||||
if palette_name not in palettes:
|
||||
available = ', '.join(palettes.keys())
|
||||
print(f"Warning: Palette '{palette_name}' not found. Available: {available}")
|
||||
palette_name = 'okabe_ito'
|
||||
|
||||
colors = palettes[palette_name]
|
||||
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=colors)
|
||||
print(f"✓ Applied '{palette_name}' color palette ({len(colors)} colors)")
|
||||
|
||||
|
||||
def configure_for_journal(journal: str, figure_width: str = 'single') -> None:
|
||||
"""
|
||||
Configure matplotlib for a specific journal.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
journal : str
|
||||
Journal name: 'nature', 'science', 'cell', 'plos', 'acs', 'ieee'
|
||||
figure_width : str, default 'single'
|
||||
Figure width: 'single' or 'double' column
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> configure_for_journal('nature', figure_width='single')
|
||||
>>> fig, ax = plt.subplots() # Will have correct size for Nature
|
||||
"""
|
||||
journal = journal.lower()
|
||||
|
||||
# Journal specifications
|
||||
journal_configs = {
|
||||
'nature': {
|
||||
'single_width': 89, # mm
|
||||
'double_width': 183,
|
||||
'style': 'nature',
|
||||
},
|
||||
'science': {
|
||||
'single_width': 55,
|
||||
'double_width': 175,
|
||||
'style': 'science',
|
||||
},
|
||||
'cell': {
|
||||
'single_width': 85,
|
||||
'double_width': 178,
|
||||
'style': 'cell',
|
||||
},
|
||||
'plos': {
|
||||
'single_width': 83,
|
||||
'double_width': 173,
|
||||
'style': 'default',
|
||||
},
|
||||
'acs': {
|
||||
'single_width': 82.5,
|
||||
'double_width': 178,
|
||||
'style': 'default',
|
||||
},
|
||||
'ieee': {
|
||||
'single_width': 89,
|
||||
'double_width': 182,
|
||||
'style': 'default',
|
||||
},
|
||||
}
|
||||
|
||||
if journal not in journal_configs:
|
||||
available = ', '.join(journal_configs.keys())
|
||||
raise ValueError(f"Journal '{journal}' not recognized. Available: {available}")
|
||||
|
||||
config = journal_configs[journal]
|
||||
|
||||
# Apply style
|
||||
apply_publication_style(config['style'])
|
||||
|
||||
# Set default figure size
|
||||
width_mm = config['single_width'] if figure_width == 'single' else config['double_width']
|
||||
width_inches = width_mm / 25.4
|
||||
plt.rcParams['figure.figsize'] = (width_inches, width_inches * 0.75) # 4:3 aspect ratio
|
||||
|
||||
print(f"✓ Configured for {journal.upper()} ({figure_width} column: {width_mm} mm)")
|
||||
|
||||
|
||||
def create_style_template(output_file: str = 'publication.mplstyle') -> None:
|
||||
"""
|
||||
Create a matplotlib style file that can be used with plt.style.use().
|
||||
|
||||
Parameters
|
||||
----------
|
||||
output_file : str, default 'publication.mplstyle'
|
||||
Output filename for the style file
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> create_style_template('my_style.mplstyle')
|
||||
>>> plt.style.use('my_style.mplstyle')
|
||||
"""
|
||||
style = get_base_style()
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("# Publication-quality matplotlib style\n")
|
||||
f.write("# Usage: plt.style.use('publication.mplstyle')\n\n")
|
||||
|
||||
for key, value in style.items():
|
||||
if isinstance(value, mpl.cycler):
|
||||
# Handle cycler specially
|
||||
colors = [c['color'] for c in value]
|
||||
f.write(f"axes.prop_cycle : cycler('color', {colors})\n")
|
||||
else:
|
||||
f.write(f"{key} : {value}\n")
|
||||
|
||||
print(f"✓ Created style template: {output_file}")
|
||||
print(f" Use with: plt.style.use('{output_file}')")
|
||||
|
||||
|
||||
def show_color_palettes() -> None:
|
||||
"""
|
||||
Display available color palettes for visual inspection.
|
||||
"""
|
||||
palettes = {
|
||||
'Okabe-Ito': OKABE_ITO_COLORS,
|
||||
'Wong': WONG_COLORS,
|
||||
'Tol Bright': TOL_BRIGHT,
|
||||
'Tol Muted': TOL_MUTED,
|
||||
'Tol High Contrast': TOL_HIGH_CONTRAST,
|
||||
}
|
||||
|
||||
fig, axes = plt.subplots(len(palettes), 1, figsize=(8, len(palettes) * 0.5))
|
||||
|
||||
for ax, (name, colors) in zip(axes, palettes.items()):
|
||||
ax.set_xlim(0, len(colors))
|
||||
ax.set_ylim(0, 1)
|
||||
ax.set_yticks([])
|
||||
ax.set_xticks([])
|
||||
ax.set_ylabel(name, fontsize=10)
|
||||
|
||||
for i, color in enumerate(colors):
|
||||
ax.add_patch(plt.Rectangle((i, 0), 1, 1, facecolor=color, edgecolor='black', linewidth=0.5))
|
||||
# Add hex code
|
||||
ax.text(i + 0.5, 0.5, color, ha='center', va='center',
|
||||
fontsize=7, color='white' if i >= len(colors) - 1 else 'black')
|
||||
|
||||
fig.suptitle('Colorblind-Friendly Palettes', fontsize=12, fontweight='bold')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
|
||||
def reset_to_default() -> None:
|
||||
"""
|
||||
Reset matplotlib to default settings.
|
||||
"""
|
||||
mpl.rcdefaults()
|
||||
print("✓ Reset to matplotlib defaults")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Matplotlib Style Presets for Scientific Figures")
|
||||
print("=" * 50)
|
||||
|
||||
# Show available styles
|
||||
print("\nAvailable publication styles:")
|
||||
print(" - default")
|
||||
print(" - nature")
|
||||
print(" - science")
|
||||
print(" - cell")
|
||||
print(" - minimal")
|
||||
print(" - presentation")
|
||||
|
||||
print("\nAvailable color palettes:")
|
||||
print(" - okabe_ito (recommended)")
|
||||
print(" - wong")
|
||||
print(" - tol_bright")
|
||||
print(" - tol_muted")
|
||||
print(" - tol_high_contrast")
|
||||
|
||||
print("\nExample usage:")
|
||||
print(" from style_presets import apply_publication_style, set_color_palette")
|
||||
print(" apply_publication_style('nature')")
|
||||
print(" set_color_palette('okabe_ito')")
|
||||
|
||||
# Create example figure
|
||||
print("\nGenerating example figure with 'default' style...")
|
||||
apply_publication_style('default')
|
||||
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
for i in range(5):
|
||||
ax.plot([1, 2, 3, 4], [i, i+1, i+0.5, i+2], marker='o', label=f'Series {i+1}')
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Response (AU)')
|
||||
ax.legend()
|
||||
fig.suptitle('Example with Publication Style')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
# Show color palettes
|
||||
print("\nDisplaying color palettes...")
|
||||
show_color_palettes()
|
||||
Reference in New Issue
Block a user