chore: import zh skill scientific-visualization

This commit is contained in:
wehub-skill-sync
2026-07-13 21:35:36 +08:00
commit 19bd4edf01
12 changed files with 3427 additions and 0 deletions
+9
View File
@@ -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 清单整理
- 原作者、版权和许可证信息以上游仓库为准
+777
View File
@@ -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 效果
- [ ] 所有图形字体一致
- [ ] 统计显著性已清晰标记
- [ ] 图例清晰完整
使用本技能,确保科学图形达到最高发表标准,同时对所有读者保持无障碍可读。
+197
View File
@@ -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}")
+63
View File
@@ -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
+61
View File
@@ -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
+68
View File
@@ -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
+348
View File
@@ -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']
```
## 顺序色图(连续数据)
顺序色图使用单一色相表示从低到高的数据值。
### 感知均匀色图
这些色图在整个色阶上具有均匀的感知变化。
**ViridisMatplotlib 默认色图):**
- 色盲友好
- 灰度打印效果好
- 感知均匀
```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. **颜色过多**:超过 78 种颜色后难以区分
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
+320
View File
@@ -0,0 +1,320 @@
# 期刊专属图片要求
## 概述
不同期刊对图片有特定的技术要求。本参考文档汇总了主要学术出版商的常见要求。**请务必查阅具体期刊的作者指南以获取最新要求。**
## Nature 系列(Nature、Nature Methods 等)
### 技术规格
- **文件格式**
- 矢量图:PDF、EPS、AI(图表首选)
- 位图:TIFF、PNG(用于图像)
- 禁止使用:PowerPoint、Word、JPEG
- **分辨率**
- 线条图:10001200 DPI
- 组合图(线条图 + 图像):600 DPI
- 照片/显微图像:最低 300 DPI
- **色彩模式**RGBNature 以数字版优先)
- **尺寸**
- 单栏:89 mm3.5 英寸)
- 1.5 栏:120 mm4.7 英寸)
- 双栏:183 mm7.2 英寸)
- 最大高度:247 mm(9.7 英寸)
- **字体**
- Arial 或 Helvetica(或类似无衬线字体)
- 最终尺寸下最小 57 pt
- 所有 PDF/EPS 文件需嵌入字体
### Nature 特定指南
- 面板标签:a、b、c(小写加粗),位于左上角
- 显微图像必须带有比例尺
- 凝胶图像:需包含分子量标记
- 裁剪:需用分隔线标示
- 统计:标明显著性;在图例中定义符号
- 源数据:所有图表均需提供
### 文件命名
格式:`FirstAuthorLastName_FigureNumber.ext`
示例:`Smith_Fig1.pdf`
## ScienceAAAS
### 技术规格
- **文件格式**
- 矢量图:EPS、PDF(首选)
- 位图:TIFF
- 可接受:AI、PSDPhotoshop
- **分辨率**
- 线条图:最低 1000 DPI
- 照片:最低 300 DPI
- 组合图:最低 600 DPI
- **色彩模式**RGB
- **尺寸**
- 单栏:5.5 cm2.17 英寸)
- 1.5 栏:12 cm4.72 英寸)
- 全宽:17.5 cm6.89 英寸)
- 最大高度:23.3 cm9.17 英寸)
- **字体**
- Helvetica(或 Arial
- 最终尺寸下最小 68 pt
- 所有图片字体须一致
### Science 特定指南
- 面板标签:(A)、(B)、(C) 加括号
- 图中文字尽量精简(详细信息放图注)
- 兼顾网页与印刷的高对比度
- 必须标注误差线;在图注中说明
- 避免过多空白
### 文件命名
格式:`Manuscript#_Fig#.ext`
示例:`abn1234_Fig1.eps`
## Cell PressCell、Neuron、Molecular Cell 等)
### 技术规格
- **文件格式**
- 矢量图:PDF、EPS(图表/示意图首选)
- 位图:TIFF(用于照片)
- **分辨率**
- 线条图:1000 DPI
- 照片:300 DPI
- 组合图:600 DPI
- **色彩模式**RGB
- **尺寸**
- 单栏:85 mm3.35 英寸)
- 双栏:178 mm7.01 英寸)
- 最大高度:230 mm9.06 英寸)
- **字体**
- 仅限 Arial 或 Helvetica
- 坐标轴标签:812 pt
- 刻度标签:68 pt
### Cell Press 特定指南
- 面板标签:(A)、(B)、(C) 或 A、B、C,位于左上角
- 相关面板尺寸应一致
- 显微图片必须带比例尺
- Western blot:需包含分子量标记
- 箭头/箭头符号:最小线宽 2 pt
- 线条宽度:数据线条 12 pt
## PLOS(公共科学图书馆)
### 技术规格
- **文件格式**
- 矢量图:EPS、PDF(首选)
- 位图:TIFF、PNG
- TIFF 可使用 LZW 压缩
- **分辨率**
- 最终尺寸下所有图片类型最低 300 DPI
- 线条图建议 600 DPI
- **色彩模式**RGB
- **尺寸**
- 单栏:8.3 cm3.27 英寸)
- 1.5 栏:11.4 cm4.49 英寸)
- 双栏:17.3 cm6.81 英寸)
- 最大高度:23.3 cm9.17 英寸)
- **字体**
- 推荐无衬线字体(Arial、Helvetica
- 最终尺寸下标签 812 pt
### PLOS 特定指南
- 图片应在不依赖图注的情况下可理解
- 仅在能增加信息量时使用彩色
- 所有图片应可转换为灰度
- 面板标签可选但建议使用
- 开放获取:图片必须采用 CC-BY 许可
- 鼓励提供源数据文件
## ACS(美国化学会)
### 技术规格
- **文件格式**
- 首选:TIFF、PDF、EPS
- 应用程序文件:AI、CDXChemDraw)、CDL
- 可接受:PNG(不可用于正式出版)
- **分辨率**
- 最终尺寸下最低 300 DPI
- 线条图与化学结构式:600 DPI
- 精细结构式:1200 DPI
- **色彩模式**:RGB 或 CMYK(请查阅具体期刊要求)
- **尺寸**
- 单栏:3.25 英寸(8.25 cm
- 双栏:7 英寸(17.78 cm
- **字体**
- 必须嵌入字体
- 所有图片字体大小保持一致
### ACS 特定指南
- 化学结构式:使用 ChemDraw 或同等软件
- 原子标签:1012 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
- **字体**
- 推荐无衬线字体
- 最终尺寸下最小 810 pt
### IEEE 特定指南
- 图片应在黑白模式下可读
- 彩色图片不另行收费(在线出版)
- 面板标签:(a)、(b)、(c) 小写
- 图注位于图片下方(不另起一页)
- 投稿前请使用 IEEE 图片检查工具
## BMCBioMed 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)通常有不同的要求
- 会议论文集可能有单独的要求
- 部分期刊提供图片制作服务(通常为收费项目)
- 补充图片的要求可能比正文图片宽松
+620
View File
@@ -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()
```
## 示例 5Seaborn 小提琴图
```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()
```
## 示例 8Plotly 交互式图形
```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()
```
## 示例 10Nature 级别可发表图形
```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` 参数控制 DPIscale=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:数据可视化专栏存档
+205
View File
@@ -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 mm3.35-3.54 英寸)
- **1.5 栏**:宽 114-120 mm4.49-4.72 英寸)
- **双栏**:宽 174-180 mm6.85-7.08 英寸)
- **最大高度**:通常为 230-240 mm9-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`
+343
View File
@@ -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)
+416
View File
@@ -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()