Initial commit
This commit is contained in:
773
skills/scientific-visualization/SKILL.md
Normal file
773
skills/scientific-visualization/SKILL.md
Normal file
@@ -0,0 +1,773 @@
|
||||
---
|
||||
name: scientific-visualization
|
||||
description: "Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots."
|
||||
---
|
||||
|
||||
# Scientific Visualization
|
||||
|
||||
## Overview
|
||||
|
||||
Scientific visualization transforms data into clear, accurate figures for publication. Create journal-ready plots with multi-panel layouts, error bars, significance markers, and colorblind-safe palettes. Export as PDF/EPS/TIFF using matplotlib, seaborn, and plotly for manuscripts.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
This skill should be used when:
|
||||
- Creating plots or visualizations for scientific manuscripts
|
||||
- Preparing figures for journal submission (Nature, Science, Cell, PLOS, etc.)
|
||||
- Ensuring figures are colorblind-friendly and accessible
|
||||
- Making multi-panel figures with consistent styling
|
||||
- Exporting figures at correct resolution and format
|
||||
- Following specific publication guidelines
|
||||
- Improving existing figures to meet publication standards
|
||||
- Creating figures that need to work in both color and grayscale
|
||||
|
||||
## Quick Start Guide
|
||||
|
||||
### Basic Publication-Quality Figure
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Apply publication style (from scripts/style_presets.py)
|
||||
from style_presets import apply_publication_style
|
||||
apply_publication_style('default')
|
||||
|
||||
# Create figure with appropriate size (single column = 3.5 inches)
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# Plot data
|
||||
x = np.linspace(0, 10, 100)
|
||||
ax.plot(x, np.sin(x), label='sin(x)')
|
||||
ax.plot(x, np.cos(x), label='cos(x)')
|
||||
|
||||
# Proper labeling with units
|
||||
ax.set_xlabel('Time (seconds)')
|
||||
ax.set_ylabel('Amplitude (mV)')
|
||||
ax.legend(frameon=False)
|
||||
|
||||
# Remove unnecessary spines
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# Save in publication formats (from scripts/figure_export.py)
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'figure1', formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
### Using Pre-configured Styles
|
||||
|
||||
Apply journal-specific styles using the matplotlib style files in `assets/`:
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Option 1: Use style file directly
|
||||
plt.style.use('assets/nature.mplstyle')
|
||||
|
||||
# Option 2: Use style_presets.py helper
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', figure_width='single')
|
||||
|
||||
# Now create figures - they'll automatically match Nature specifications
|
||||
fig, ax = plt.subplots()
|
||||
# ... your plotting code ...
|
||||
```
|
||||
|
||||
### Quick Start with Seaborn
|
||||
|
||||
For statistical plots, use seaborn with publication styling:
|
||||
|
||||
```python
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
from style_presets import apply_publication_style
|
||||
|
||||
# Apply publication style
|
||||
apply_publication_style('default')
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
sns.set_palette('colorblind')
|
||||
|
||||
# Create statistical comparison figure
|
||||
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()
|
||||
|
||||
# Save figure
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'treatment_comparison', formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
## Core Principles and Best Practices
|
||||
|
||||
### 1. Resolution and File Format
|
||||
|
||||
**Critical requirements** (detailed in `references/publication_guidelines.md`):
|
||||
- **Raster images** (photos, microscopy): 300-600 DPI
|
||||
- **Line art** (graphs, plots): 600-1200 DPI or vector format
|
||||
- **Vector formats** (preferred): PDF, EPS, SVG
|
||||
- **Raster formats**: TIFF, PNG (never JPEG for scientific data)
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
# Use the figure_export.py script for correct settings
|
||||
from figure_export import save_publication_figure
|
||||
|
||||
# Saves in multiple formats with proper DPI
|
||||
save_publication_figure(fig, 'myfigure', formats=['pdf', 'png'], dpi=300)
|
||||
|
||||
# Or save for specific journal requirements
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', journal='nature', figure_type='combination')
|
||||
```
|
||||
|
||||
### 2. Color Selection - Colorblind Accessibility
|
||||
|
||||
**Always use colorblind-friendly palettes** (detailed in `references/color_palettes.md`):
|
||||
|
||||
**Recommended: Okabe-Ito palette** (distinguishable by all types of color blindness):
|
||||
```python
|
||||
# Option 1: Use assets/color_palettes.py
|
||||
from color_palettes import OKABE_ITO_LIST, apply_palette
|
||||
apply_palette('okabe_ito')
|
||||
|
||||
# Option 2: Manual specification
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=okabe_ito)
|
||||
```
|
||||
|
||||
**For heatmaps/continuous data:**
|
||||
- Use perceptually uniform colormaps: `viridis`, `plasma`, `cividis`
|
||||
- Avoid red-green diverging maps (use `PuOr`, `RdBu`, `BrBG` instead)
|
||||
- Never use `jet` or `rainbow` colormaps
|
||||
|
||||
**Always test figures in grayscale** to ensure interpretability.
|
||||
|
||||
### 3. Typography and Text
|
||||
|
||||
**Font guidelines** (detailed in `references/publication_guidelines.md`):
|
||||
- Sans-serif fonts: Arial, Helvetica, Calibri
|
||||
- Minimum sizes at **final print size**:
|
||||
- Axis labels: 7-9 pt
|
||||
- Tick labels: 6-8 pt
|
||||
- Panel labels: 8-12 pt (bold)
|
||||
- Sentence case for labels: "Time (hours)" not "TIME (HOURS)"
|
||||
- Always include units in parentheses
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
# Set fonts globally
|
||||
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. Figure Dimensions
|
||||
|
||||
**Journal-specific widths** (detailed in `references/journal_requirements.md`):
|
||||
- **Nature**: Single 89 mm, Double 183 mm
|
||||
- **Science**: Single 55 mm, Double 175 mm
|
||||
- **Cell**: Single 85 mm, Double 178 mm
|
||||
|
||||
**Check figure size compliance:**
|
||||
```python
|
||||
from figure_export import check_figure_size
|
||||
|
||||
fig = plt.figure(figsize=(3.5, 3)) # 89 mm for Nature
|
||||
check_figure_size(fig, journal='nature')
|
||||
```
|
||||
|
||||
### 5. Multi-Panel Figures
|
||||
|
||||
**Best practices:**
|
||||
- Label panels with bold letters: **A**, **B**, **C** (uppercase for most journals, lowercase for Nature)
|
||||
- Maintain consistent styling across all panels
|
||||
- Align panels along edges where possible
|
||||
- Use adequate white space between panels
|
||||
|
||||
**Example implementation** (see `references/matplotlib_examples.md` for complete code):
|
||||
```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])
|
||||
# ... create other panels ...
|
||||
|
||||
# Add panel labels
|
||||
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')
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Task 1: Create a Publication-Ready Line Plot
|
||||
|
||||
See `references/matplotlib_examples.md` Example 1 for complete code.
|
||||
|
||||
**Key steps:**
|
||||
1. Apply publication style
|
||||
2. Set appropriate figure size for target journal
|
||||
3. Use colorblind-friendly colors
|
||||
4. Add error bars with correct representation (SEM, SD, or CI)
|
||||
5. Label axes with units
|
||||
6. Remove unnecessary spines
|
||||
7. Save in vector format
|
||||
|
||||
**Using seaborn for automatic confidence intervals:**
|
||||
```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()
|
||||
```
|
||||
|
||||
### Task 2: Create a Multi-Panel Figure
|
||||
|
||||
See `references/matplotlib_examples.md` Example 2 for complete code.
|
||||
|
||||
**Key steps:**
|
||||
1. Use `GridSpec` for flexible layout
|
||||
2. Ensure consistent styling across panels
|
||||
3. Add bold panel labels (A, B, C, etc.)
|
||||
4. Align related panels
|
||||
5. Verify all text is readable at final size
|
||||
|
||||
### Task 3: Create a Heatmap with Proper Colormap
|
||||
|
||||
See `references/matplotlib_examples.md` Example 4 for complete code.
|
||||
|
||||
**Key steps:**
|
||||
1. Use perceptually uniform colormap (`viridis`, `plasma`, `cividis`)
|
||||
2. Include labeled colorbar
|
||||
3. For diverging data, use colorblind-safe diverging map (`RdBu_r`, `PuOr`)
|
||||
4. Set appropriate center value for diverging maps
|
||||
5. Test appearance in grayscale
|
||||
|
||||
**Using seaborn for correlation matrices:**
|
||||
```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)
|
||||
```
|
||||
|
||||
### Task 4: Prepare Figure for Specific Journal
|
||||
|
||||
**Workflow:**
|
||||
1. Check journal requirements: `references/journal_requirements.md`
|
||||
2. Configure matplotlib for journal:
|
||||
```python
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', figure_width='single')
|
||||
```
|
||||
3. Create figure (will auto-size correctly)
|
||||
4. Export with journal specifications:
|
||||
```python
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', journal='nature', figure_type='line_art')
|
||||
```
|
||||
|
||||
### Task 5: Fix an Existing Figure to Meet Publication Standards
|
||||
|
||||
**Checklist approach** (full checklist in `references/publication_guidelines.md`):
|
||||
|
||||
1. **Check resolution**: Verify DPI meets journal requirements
|
||||
2. **Check file format**: Use vector for plots, TIFF/PNG for images
|
||||
3. **Check colors**: Ensure colorblind-friendly
|
||||
4. **Check fonts**: Minimum 6-7 pt at final size, sans-serif
|
||||
5. **Check labels**: All axes labeled with units
|
||||
6. **Check size**: Matches journal column width
|
||||
7. **Test grayscale**: Figure interpretable without color
|
||||
8. **Remove chart junk**: No unnecessary grids, 3D effects, shadows
|
||||
|
||||
### Task 6: Create Colorblind-Friendly Visualizations
|
||||
|
||||
**Strategy:**
|
||||
1. Use approved palettes from `assets/color_palettes.py`
|
||||
2. Add redundant encoding (line styles, markers, patterns)
|
||||
3. Test with colorblind simulator
|
||||
4. Ensure grayscale compatibility
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from color_palettes import apply_palette
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
apply_palette('okabe_ito')
|
||||
|
||||
# Add redundant encoding beyond color
|
||||
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)
|
||||
```
|
||||
|
||||
## Statistical Rigor
|
||||
|
||||
**Always include:**
|
||||
- Error bars (SD, SEM, or CI - specify which in caption)
|
||||
- Sample size (n) in figure or caption
|
||||
- Statistical significance markers (*, **, ***)
|
||||
- Individual data points when possible (not just summary statistics)
|
||||
|
||||
**Example with statistics:**
|
||||
```python
|
||||
# Show individual points with summary statistics
|
||||
ax.scatter(x_jittered, individual_points, alpha=0.4, s=8)
|
||||
ax.errorbar(x, means, yerr=sems, fmt='o', capsize=3)
|
||||
|
||||
# Mark significance
|
||||
ax.text(1.5, max_y * 1.1, '***', ha='center', fontsize=8)
|
||||
```
|
||||
|
||||
## Working with Different Plotting Libraries
|
||||
|
||||
### Matplotlib
|
||||
- Most control over publication details
|
||||
- Best for complex multi-panel figures
|
||||
- Use provided style files for consistent formatting
|
||||
- See `references/matplotlib_examples.md` for extensive examples
|
||||
|
||||
### Seaborn
|
||||
|
||||
Seaborn provides a high-level, dataset-oriented interface for statistical graphics, built on matplotlib. It excels at creating publication-quality statistical visualizations with minimal code while maintaining full compatibility with matplotlib customization.
|
||||
|
||||
**Key advantages for scientific visualization:**
|
||||
- Automatic statistical estimation and confidence intervals
|
||||
- Built-in support for multi-panel figures (faceting)
|
||||
- Colorblind-friendly palettes by default
|
||||
- Dataset-oriented API using pandas DataFrames
|
||||
- Semantic mapping of variables to visual properties
|
||||
|
||||
#### Quick Start with Publication Style
|
||||
|
||||
Always apply matplotlib publication styles first, then configure seaborn:
|
||||
|
||||
```python
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
from style_presets import apply_publication_style
|
||||
|
||||
# Apply publication style
|
||||
apply_publication_style('default')
|
||||
|
||||
# Configure seaborn for publication
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
sns.set_palette('colorblind') # Use colorblind-safe palette
|
||||
|
||||
# Create figure
|
||||
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() # Remove top and right spines
|
||||
```
|
||||
|
||||
#### Common Plot Types for Publications
|
||||
|
||||
**Statistical comparisons:**
|
||||
```python
|
||||
# Box plot with individual points for transparency
|
||||
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()
|
||||
```
|
||||
|
||||
**Distribution analysis:**
|
||||
```python
|
||||
# Violin plot with split comparison
|
||||
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()
|
||||
```
|
||||
|
||||
**Correlation matrices:**
|
||||
```python
|
||||
# Heatmap with proper colormap and annotations
|
||||
fig, ax = plt.subplots(figsize=(5, 4))
|
||||
corr = df.corr()
|
||||
mask = np.triu(np.ones_like(corr, dtype=bool)) # Show only lower triangle
|
||||
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()
|
||||
```
|
||||
|
||||
**Time series with confidence bands:**
|
||||
```python
|
||||
# Line plot with automatic CI calculation
|
||||
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()
|
||||
```
|
||||
|
||||
#### Multi-Panel Figures with Seaborn
|
||||
|
||||
**Using FacetGrid for automatic faceting:**
|
||||
```python
|
||||
# Create faceted plot
|
||||
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()
|
||||
|
||||
# Save with correct DPI
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(g.figure, 'figure_facets',
|
||||
formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
**Combining seaborn with matplotlib subplots:**
|
||||
```python
|
||||
# Create custom multi-panel layout
|
||||
fig, axes = plt.subplots(2, 2, figsize=(7, 6))
|
||||
|
||||
# Panel A: Scatter with regression
|
||||
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')
|
||||
|
||||
# Panel B: Distribution comparison
|
||||
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')
|
||||
|
||||
# Panel C: Heatmap
|
||||
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')
|
||||
|
||||
# Panel D: Time series
|
||||
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()
|
||||
```
|
||||
|
||||
#### Color Palettes for Publications
|
||||
|
||||
Seaborn includes several colorblind-safe palettes:
|
||||
|
||||
```python
|
||||
# Use built-in colorblind palette (recommended)
|
||||
sns.set_palette('colorblind')
|
||||
|
||||
# Or specify custom colorblind-safe colors (Okabe-Ito)
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
sns.set_palette(okabe_ito)
|
||||
|
||||
# For heatmaps and continuous data
|
||||
sns.heatmap(data, cmap='viridis') # Perceptually uniform
|
||||
sns.heatmap(corr, cmap='RdBu_r', center=0) # Diverging, centered
|
||||
```
|
||||
|
||||
#### Choosing Between Axes-Level and Figure-Level Functions
|
||||
|
||||
**Axes-level functions** (e.g., `scatterplot`, `boxplot`, `heatmap`):
|
||||
- Use when building custom multi-panel layouts
|
||||
- Accept `ax=` parameter for precise placement
|
||||
- Better integration with matplotlib subplots
|
||||
- More control over figure composition
|
||||
|
||||
```python
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
sns.scatterplot(data=df, x='x', y='y', hue='group', ax=ax)
|
||||
```
|
||||
|
||||
**Figure-level functions** (e.g., `relplot`, `catplot`, `displot`):
|
||||
- Use for automatic faceting by categorical variables
|
||||
- Create complete figures with consistent styling
|
||||
- Great for exploratory analysis
|
||||
- Use `height` and `aspect` for sizing
|
||||
|
||||
```python
|
||||
g = sns.relplot(data=df, x='x', y='y', col='category', kind='scatter')
|
||||
```
|
||||
|
||||
#### Statistical Rigor with Seaborn
|
||||
|
||||
Seaborn automatically computes and displays uncertainty:
|
||||
|
||||
```python
|
||||
# Line plot: shows mean ± 95% CI by default
|
||||
sns.lineplot(data=df, x='time', y='value', hue='treatment',
|
||||
errorbar=('ci', 95)) # Can change to 'sd', 'se', etc.
|
||||
|
||||
# Bar plot: shows mean with bootstrapped CI
|
||||
sns.barplot(data=df, x='treatment', y='response',
|
||||
errorbar=('ci', 95), capsize=0.1)
|
||||
|
||||
# Always specify error type in figure caption:
|
||||
# "Error bars represent 95% confidence intervals"
|
||||
```
|
||||
|
||||
#### Best Practices for Publication-Ready Seaborn Figures
|
||||
|
||||
1. **Always set publication theme first:**
|
||||
```python
|
||||
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
|
||||
```
|
||||
|
||||
2. **Use colorblind-safe palettes:**
|
||||
```python
|
||||
sns.set_palette('colorblind')
|
||||
```
|
||||
|
||||
3. **Remove unnecessary elements:**
|
||||
```python
|
||||
sns.despine() # Remove top and right spines
|
||||
```
|
||||
|
||||
4. **Control figure size appropriately:**
|
||||
```python
|
||||
# Axes-level: use matplotlib figsize
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# Figure-level: use height and aspect
|
||||
g = sns.relplot(..., height=3, aspect=1.2)
|
||||
```
|
||||
|
||||
5. **Show individual data points when possible:**
|
||||
```python
|
||||
sns.boxplot(...) # Summary statistics
|
||||
sns.stripplot(..., alpha=0.3) # Individual points
|
||||
```
|
||||
|
||||
6. **Include proper labels with units:**
|
||||
```python
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Expression (AU)')
|
||||
```
|
||||
|
||||
7. **Export at correct resolution:**
|
||||
```python
|
||||
from figure_export import save_publication_figure
|
||||
save_publication_figure(fig, 'figure_name',
|
||||
formats=['pdf', 'png'], dpi=300)
|
||||
```
|
||||
|
||||
#### Advanced Seaborn Techniques
|
||||
|
||||
**Pairwise relationships for exploratory analysis:**
|
||||
```python
|
||||
# Quick overview of all relationships
|
||||
g = sns.pairplot(data=df, hue='condition',
|
||||
vars=['gene1', 'gene2', 'gene3'],
|
||||
corner=True, diag_kind='kde', height=2)
|
||||
```
|
||||
|
||||
**Hierarchical clustering heatmap:**
|
||||
```python
|
||||
# Cluster samples and features
|
||||
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'})
|
||||
```
|
||||
|
||||
**Joint distributions with marginals:**
|
||||
```python
|
||||
# Bivariate distribution with context
|
||||
g = sns.jointplot(data=df, x='gene1', y='gene2',
|
||||
hue='treatment', kind='scatter',
|
||||
height=6, ratio=4, marginal_kws={'kde': True})
|
||||
```
|
||||
|
||||
#### Common Seaborn Issues and Solutions
|
||||
|
||||
**Issue: Legend outside plot area**
|
||||
```python
|
||||
g = sns.relplot(...)
|
||||
g._legend.set_bbox_to_anchor((0.9, 0.5))
|
||||
```
|
||||
|
||||
**Issue: Overlapping labels**
|
||||
```python
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||
**Issue: Text too small at final size**
|
||||
```python
|
||||
sns.set_context('paper', font_scale=1.2) # Increase if needed
|
||||
```
|
||||
|
||||
#### Additional Resources
|
||||
|
||||
For more detailed seaborn information, see:
|
||||
- `scientific-packages/seaborn/SKILL.md` - Comprehensive seaborn documentation
|
||||
- `scientific-packages/seaborn/references/examples.md` - Practical use cases
|
||||
- `scientific-packages/seaborn/references/function_reference.md` - Complete API reference
|
||||
- `scientific-packages/seaborn/references/objects_interface.md` - Modern declarative API
|
||||
|
||||
### Plotly
|
||||
- Interactive figures for exploration
|
||||
- Export static images for publication
|
||||
- Configure for publication quality:
|
||||
```python
|
||||
fig.update_layout(
|
||||
font=dict(family='Arial, sans-serif', size=10),
|
||||
plot_bgcolor='white',
|
||||
# ... see matplotlib_examples.md Example 8
|
||||
)
|
||||
fig.write_image('figure.png', scale=3) # scale=3 gives ~300 DPI
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### References Directory
|
||||
|
||||
**Load these as needed for detailed information:**
|
||||
|
||||
- **`publication_guidelines.md`**: Comprehensive best practices
|
||||
- Resolution and file format requirements
|
||||
- Typography guidelines
|
||||
- Layout and composition rules
|
||||
- Statistical rigor requirements
|
||||
- Complete publication checklist
|
||||
|
||||
- **`color_palettes.md`**: Color usage guide
|
||||
- Colorblind-friendly palette specifications with RGB values
|
||||
- Sequential and diverging colormap recommendations
|
||||
- Testing procedures for accessibility
|
||||
- Domain-specific palettes (genomics, microscopy)
|
||||
|
||||
- **`journal_requirements.md`**: Journal-specific specifications
|
||||
- Technical requirements by publisher
|
||||
- File format and DPI specifications
|
||||
- Figure dimension requirements
|
||||
- Quick reference table
|
||||
|
||||
- **`matplotlib_examples.md`**: Practical code examples
|
||||
- 10 complete working examples
|
||||
- Line plots, bar plots, heatmaps, multi-panel figures
|
||||
- Journal-specific figure examples
|
||||
- Tips for each library (matplotlib, seaborn, plotly)
|
||||
|
||||
### Scripts Directory
|
||||
|
||||
**Use these helper scripts for automation:**
|
||||
|
||||
- **`figure_export.py`**: Export utilities
|
||||
- `save_publication_figure()`: Save in multiple formats with correct DPI
|
||||
- `save_for_journal()`: Use journal-specific requirements automatically
|
||||
- `check_figure_size()`: Verify dimensions meet journal specs
|
||||
- Run directly: `python scripts/figure_export.py` for examples
|
||||
|
||||
- **`style_presets.py`**: Pre-configured styles
|
||||
- `apply_publication_style()`: Apply preset styles (default, nature, science, cell)
|
||||
- `set_color_palette()`: Quick palette switching
|
||||
- `configure_for_journal()`: One-command journal configuration
|
||||
- Run directly: `python scripts/style_presets.py` to see examples
|
||||
|
||||
### Assets Directory
|
||||
|
||||
**Use these files in figures:**
|
||||
|
||||
- **`color_palettes.py`**: Importable color definitions
|
||||
- All recommended palettes as Python constants
|
||||
- `apply_palette()` helper function
|
||||
- Can be imported directly into notebooks/scripts
|
||||
|
||||
- **Matplotlib style files**: Use with `plt.style.use()`
|
||||
- `publication.mplstyle`: General publication quality
|
||||
- `nature.mplstyle`: Nature journal specifications
|
||||
- `presentation.mplstyle`: Larger fonts for posters/slides
|
||||
|
||||
## Workflow Summary
|
||||
|
||||
**Recommended workflow for creating publication figures:**
|
||||
|
||||
1. **Plan**: Determine target journal, figure type, and content
|
||||
2. **Configure**: Apply appropriate style for journal
|
||||
```python
|
||||
from style_presets import configure_for_journal
|
||||
configure_for_journal('nature', 'single')
|
||||
```
|
||||
3. **Create**: Build figure with proper labels, colors, statistics
|
||||
4. **Verify**: Check size, fonts, colors, accessibility
|
||||
```python
|
||||
from figure_export import check_figure_size
|
||||
check_figure_size(fig, journal='nature')
|
||||
```
|
||||
5. **Export**: Save in required formats
|
||||
```python
|
||||
from figure_export import save_for_journal
|
||||
save_for_journal(fig, 'figure1', 'nature', 'combination')
|
||||
```
|
||||
6. **Review**: View at final size in manuscript context
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
|
||||
1. **Font too small**: Text unreadable when printed at final size
|
||||
2. **JPEG format**: Never use JPEG for graphs/plots (creates artifacts)
|
||||
3. **Red-green colors**: ~8% of males cannot distinguish
|
||||
4. **Low resolution**: Pixelated figures in publication
|
||||
5. **Missing units**: Always label axes with units
|
||||
6. **3D effects**: Distorts perception, avoid completely
|
||||
7. **Chart junk**: Remove unnecessary gridlines, decorations
|
||||
8. **Truncated axes**: Start bar charts at zero unless scientifically justified
|
||||
9. **Inconsistent styling**: Different fonts/colors across figures in same manuscript
|
||||
10. **No error bars**: Always show uncertainty
|
||||
|
||||
## Final Checklist
|
||||
|
||||
Before submitting figures, verify:
|
||||
|
||||
- [ ] Resolution meets journal requirements (300+ DPI)
|
||||
- [ ] File format is correct (vector for plots, TIFF for images)
|
||||
- [ ] Figure size matches journal specifications
|
||||
- [ ] All text readable at final size (≥6 pt)
|
||||
- [ ] Colors are colorblind-friendly
|
||||
- [ ] Figure works in grayscale
|
||||
- [ ] All axes labeled with units
|
||||
- [ ] Error bars present with definition in caption
|
||||
- [ ] Panel labels present and consistent
|
||||
- [ ] No chart junk or 3D effects
|
||||
- [ ] Fonts consistent across all figures
|
||||
- [ ] Statistical significance clearly marked
|
||||
- [ ] Legend is clear and complete
|
||||
|
||||
Use this skill to ensure scientific figures meet the highest publication standards while remaining accessible to all readers.
|
||||
197
skills/scientific-visualization/assets/color_palettes.py
Normal file
197
skills/scientific-visualization/assets/color_palettes.py
Normal 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
skills/scientific-visualization/assets/nature.mplstyle
Normal file
63
skills/scientific-visualization/assets/nature.mplstyle
Normal 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
skills/scientific-visualization/assets/presentation.mplstyle
Normal file
61
skills/scientific-visualization/assets/presentation.mplstyle
Normal 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
skills/scientific-visualization/assets/publication.mplstyle
Normal file
68
skills/scientific-visualization/assets/publication.mplstyle
Normal 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
skills/scientific-visualization/references/color_palettes.md
Normal file
348
skills/scientific-visualization/references/color_palettes.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# Scientific Color Palettes and Guidelines
|
||||
|
||||
## Overview
|
||||
|
||||
Color choice in scientific visualization is critical for accessibility, clarity, and accurate data representation. This reference provides colorblind-friendly palettes and best practices for color usage.
|
||||
|
||||
## Colorblind-Friendly Palettes
|
||||
|
||||
### Okabe-Ito Palette (Recommended for Categories)
|
||||
|
||||
The Okabe-Ito palette is specifically designed to be distinguishable by people with all forms of color blindness.
|
||||
|
||||
```python
|
||||
# Okabe-Ito colors (RGB values)
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in 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)
|
||||
```
|
||||
|
||||
**Usage in Seaborn:**
|
||||
```python
|
||||
import seaborn as sns
|
||||
|
||||
okabe_ito_palette = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
sns.set_palette(okabe_ito_palette)
|
||||
```
|
||||
|
||||
**Usage in Plotly:**
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
|
||||
okabe_ito_plotly = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
fig = go.Figure()
|
||||
# Apply to discrete color scale
|
||||
```
|
||||
|
||||
### Wong Palette (Alternative for Categories)
|
||||
|
||||
Another excellent colorblind-friendly palette by 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 Palettes
|
||||
|
||||
Paul Tol has designed multiple scientifically-optimized palettes for different use cases.
|
||||
|
||||
**Bright Palette (up to 7 categories):**
|
||||
```python
|
||||
tol_bright = ['#4477AA', '#EE6677', '#228833', '#CCBB44',
|
||||
'#66CCEE', '#AA3377', '#BBBBBB']
|
||||
```
|
||||
|
||||
**Muted Palette (up to 9 categories):**
|
||||
```python
|
||||
tol_muted = ['#332288', '#88CCEE', '#44AA99', '#117733',
|
||||
'#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
```
|
||||
|
||||
**High Contrast (3 categories only):**
|
||||
```python
|
||||
tol_high_contrast = ['#004488', '#DDAA33', '#BB5566']
|
||||
```
|
||||
|
||||
## Sequential Colormaps (Continuous Data)
|
||||
|
||||
Sequential colormaps represent data from low to high values with a single hue.
|
||||
|
||||
### Perceptually Uniform Colormaps
|
||||
|
||||
These colormaps have uniform perceptual change across the color scale.
|
||||
|
||||
**Viridis (default in Matplotlib):**
|
||||
- Colorblind-friendly
|
||||
- Prints well in grayscale
|
||||
- Perceptually uniform
|
||||
```python
|
||||
plt.imshow(data, cmap='viridis')
|
||||
```
|
||||
|
||||
**Cividis:**
|
||||
- Optimized for colorblind viewers
|
||||
- Designed specifically for deuteranopia/protanopia
|
||||
```python
|
||||
plt.imshow(data, cmap='cividis')
|
||||
```
|
||||
|
||||
**Plasma, Inferno, Magma:**
|
||||
- Perceptually uniform alternatives to viridis
|
||||
- Good for different aesthetic preferences
|
||||
```python
|
||||
plt.imshow(data, cmap='plasma')
|
||||
```
|
||||
|
||||
### When to Use Sequential Maps
|
||||
- Heatmaps showing intensity
|
||||
- Geographic elevation data
|
||||
- Probability distributions
|
||||
- Any single-variable continuous data (low → high)
|
||||
|
||||
## Diverging Colormaps (Negative to Positive)
|
||||
|
||||
Diverging colormaps have a neutral middle color with two contrasting colors at extremes.
|
||||
|
||||
### Colorblind-Safe Diverging Maps
|
||||
|
||||
**RdYlBu (Red-Yellow-Blue):**
|
||||
```python
|
||||
plt.imshow(data, cmap='RdYlBu_r') # _r reverses: blue (low) to red (high)
|
||||
```
|
||||
|
||||
**PuOr (Purple-Orange):**
|
||||
- Excellent for colorblind viewers
|
||||
```python
|
||||
plt.imshow(data, cmap='PuOr')
|
||||
```
|
||||
|
||||
**BrBG (Brown-Blue-Green):**
|
||||
- Good colorblind accessibility
|
||||
```python
|
||||
plt.imshow(data, cmap='BrBG')
|
||||
```
|
||||
|
||||
### Avoid These Diverging Maps
|
||||
- **RdGn (Red-Green)**: Problematic for red-green colorblindness
|
||||
- **RdYlGn (Red-Yellow-Green)**: Same issue
|
||||
|
||||
### When to Use Diverging Maps
|
||||
- Correlation matrices
|
||||
- Change/difference data (positive vs. negative)
|
||||
- Deviation from a central value
|
||||
- Temperature anomalies
|
||||
|
||||
## Special Purpose Palettes
|
||||
|
||||
### For Genomics/Bioinformatics
|
||||
|
||||
**Sequence type identification:**
|
||||
```python
|
||||
# DNA/RNA bases
|
||||
nucleotide_colors = {
|
||||
'A': '#00CC00', # Green
|
||||
'C': '#0000CC', # Blue
|
||||
'G': '#FFB300', # Orange
|
||||
'T': '#CC0000', # Red
|
||||
'U': '#CC0000' # Red (RNA)
|
||||
}
|
||||
```
|
||||
|
||||
**Gene expression:**
|
||||
- Use sequential colormaps (viridis, YlOrRd) for expression levels
|
||||
- Use diverging colormaps (RdBu) for log2 fold change
|
||||
|
||||
### For Microscopy
|
||||
|
||||
**Fluorescence channels:**
|
||||
```python
|
||||
# Traditional fluorophore colors (use with caution)
|
||||
fluorophore_colors = {
|
||||
'DAPI': '#0000FF', # Blue - DNA
|
||||
'GFP': '#00FF00', # Green (problematic for colorblind)
|
||||
'RFP': '#FF0000', # Red
|
||||
'Cy5': '#FF00FF' # Magenta
|
||||
}
|
||||
|
||||
# Colorblind-friendly alternatives
|
||||
fluorophore_alt = {
|
||||
'Channel1': '#0072B2', # Blue
|
||||
'Channel2': '#E69F00', # Orange (instead of green)
|
||||
'Channel3': '#D55E00', # Vermillion
|
||||
'Channel4': '#CC79A7' # Magenta
|
||||
}
|
||||
```
|
||||
|
||||
## Color Usage Best Practices
|
||||
|
||||
### Categorical Data (Qualitative Color Schemes)
|
||||
|
||||
**Do:**
|
||||
- Use distinct, saturated colors from Okabe-Ito or Wong palette
|
||||
- Limit to 7-8 categories max in one plot
|
||||
- Use consistent colors for same categories across figures
|
||||
- Add patterns/markers when colors alone might be insufficient
|
||||
|
||||
**Don't:**
|
||||
- Use red/green combinations
|
||||
- Use rainbow (jet) colormap for categories
|
||||
- Use similar hues that are hard to distinguish
|
||||
|
||||
### Continuous Data (Sequential/Diverging Schemes)
|
||||
|
||||
**Do:**
|
||||
- Use perceptually uniform colormaps (viridis, plasma, cividis)
|
||||
- Choose diverging maps when data has meaningful center point
|
||||
- Include colorbar with labeled ticks
|
||||
- Test appearance in grayscale
|
||||
|
||||
**Don't:**
|
||||
- Use rainbow (jet) colormap - not perceptually uniform
|
||||
- Use red-green diverging maps
|
||||
- Omit colorbar on heatmaps
|
||||
|
||||
## Testing for Colorblind Accessibility
|
||||
|
||||
### Online Simulators
|
||||
- **Coblis**: https://www.color-blindness.com/coblis-color-blindness-simulator/
|
||||
- **Color Oracle**: Free downloadable tool for Windows/Mac/Linux
|
||||
- **Sim Daltonism**: Mac application
|
||||
|
||||
### Types of Color Vision Deficiency
|
||||
- **Deuteranopia** (~5% of males): Cannot distinguish green
|
||||
- **Protanopia** (~2% of males): Cannot distinguish red
|
||||
- **Tritanopia** (<1%): Cannot distinguish blue (rare)
|
||||
|
||||
### Python Tools
|
||||
```python
|
||||
# Using colorspacious to simulate colorblind vision
|
||||
from colorspacious import cspace_convert
|
||||
|
||||
def simulate_deuteranopia(image_rgb):
|
||||
from colorspacious import cspace_convert
|
||||
# Convert to colorblind simulation
|
||||
# (Implementation would require colorspacious library)
|
||||
pass
|
||||
```
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
### Setting Global Matplotlib Style
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
# Set Okabe-Ito as default color cycle
|
||||
okabe_ito_colors = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7']
|
||||
mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=okabe_ito_colors)
|
||||
|
||||
# Set default colormap to viridis
|
||||
mpl.rcParams['image.cmap'] = 'viridis'
|
||||
```
|
||||
|
||||
### Seaborn with Custom Palette
|
||||
```python
|
||||
import seaborn as sns
|
||||
|
||||
# Set Paul Tol muted palette
|
||||
tol_muted = ['#332288', '#88CCEE', '#44AA99', '#117733',
|
||||
'#999933', '#DDCC77', '#CC6677', '#882255', '#AA4499']
|
||||
sns.set_palette(tol_muted)
|
||||
|
||||
# For heatmaps
|
||||
sns.heatmap(data, cmap='viridis', annot=True)
|
||||
```
|
||||
|
||||
### Plotly with Discrete Colors
|
||||
```python
|
||||
import plotly.express as px
|
||||
|
||||
# Use Okabe-Ito for categorical data
|
||||
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)
|
||||
```
|
||||
|
||||
## Grayscale Compatibility
|
||||
|
||||
All figures should remain interpretable in grayscale. Test by converting to grayscale:
|
||||
|
||||
```python
|
||||
# Convert figure to grayscale for testing
|
||||
fig.savefig('figure_gray.png', dpi=300, colormap='gray')
|
||||
```
|
||||
|
||||
**Strategies for grayscale compatibility:**
|
||||
1. Use different line styles (solid, dashed, dotted)
|
||||
2. Use different marker shapes (circles, squares, triangles)
|
||||
3. Add hatching patterns to bars
|
||||
4. Ensure sufficient luminance contrast between colors
|
||||
|
||||
## Color Spaces
|
||||
|
||||
### RGB vs CMYK
|
||||
- **RGB** (Red, Green, Blue): For digital/screen display
|
||||
- **CMYK** (Cyan, Magenta, Yellow, Black): For print
|
||||
|
||||
**Important:** Colors appear different in print vs. screen. When preparing for print:
|
||||
1. Convert to CMYK color space
|
||||
2. Check color appearance in CMYK preview
|
||||
3. Ensure sufficient contrast remains
|
||||
|
||||
### Matplotlib Color Spaces
|
||||
```python
|
||||
# Save for print (CMYK)
|
||||
# Note: Direct CMYK support limited; use PDF and let publisher convert
|
||||
fig.savefig('figure.pdf', dpi=300)
|
||||
|
||||
# For RGB (digital)
|
||||
fig.savefig('figure.png', dpi=300)
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
1. **Using jet/rainbow colormap**: Not perceptually uniform; avoid
|
||||
2. **Red-green combinations**: ~8% of males cannot distinguish
|
||||
3. **Too many colors**: More than 7-8 becomes difficult to distinguish
|
||||
4. **Inconsistent color meaning**: Same color should mean same thing across figures
|
||||
5. **Missing colorbar**: Always include for continuous data
|
||||
6. **Low contrast**: Ensure colors differ sufficiently
|
||||
7. **Relying solely on color**: Add texture, patterns, or markers
|
||||
|
||||
## Resources
|
||||
|
||||
- **ColorBrewer**: http://colorbrewer2.org/ - Choose palettes by colorblind-safe option
|
||||
- **Paul Tol's palettes**: https://personal.sron.nl/~pault/
|
||||
- **Okabe-Ito palette origin**: "Color Universal Design" (Okabe & Ito, 2008)
|
||||
- **Matplotlib colormaps**: https://matplotlib.org/stable/tutorials/colors/colormaps.html
|
||||
- **Seaborn palettes**: https://seaborn.pydata.org/tutorial/color_palettes.html
|
||||
@@ -0,0 +1,320 @@
|
||||
# Journal-Specific Figure Requirements
|
||||
|
||||
## Overview
|
||||
|
||||
Different journals have specific technical requirements for figures. This reference compiles common requirements from major scientific publishers. **Always check the specific journal's author guidelines for the most current requirements.**
|
||||
|
||||
## Nature Portfolio (Nature, Nature Methods, etc.)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: PDF, EPS, AI (preferred for graphs)
|
||||
- Raster: TIFF, PNG (for images)
|
||||
- Never: PowerPoint, Word, JPEG
|
||||
|
||||
- **Resolution**:
|
||||
- Line art: 1000-1200 DPI
|
||||
- Combination (line art + images): 600 DPI
|
||||
- Photographs/microscopy: 300 DPI minimum
|
||||
|
||||
- **Color space**: RGB (Nature is digital-first)
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 89 mm (3.5 inches)
|
||||
- 1.5 column: 120 mm (4.7 inches)
|
||||
- Double column: 183 mm (7.2 inches)
|
||||
- Maximum height: 247 mm (9.7 inches)
|
||||
|
||||
- **Fonts**:
|
||||
- Arial or Helvetica (or similar sans-serif)
|
||||
- Minimum 5-7 pt at final size
|
||||
- Embed all fonts in PDF/EPS
|
||||
|
||||
### Nature Specific Guidelines
|
||||
- Panel labels: a, b, c (lowercase, bold) in top-left corner
|
||||
- Scale bars required for microscopy images
|
||||
- Gel images: Include molecular weight markers
|
||||
- Cropping: Indicate with line breaks
|
||||
- Statistics: Mark significance; define symbols in legend
|
||||
- Source data: Required for all graphs
|
||||
|
||||
### File Naming
|
||||
Format: `FirstAuthorLastName_FigureNumber.ext`
|
||||
Example: `Smith_Fig1.pdf`
|
||||
|
||||
## Science (AAAS)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: EPS, PDF (preferred)
|
||||
- Raster: TIFF
|
||||
- Acceptable: AI, PSD (Photoshop)
|
||||
|
||||
- **Resolution**:
|
||||
- Line art: 1000 DPI minimum
|
||||
- Photographs: 300 DPI minimum
|
||||
- Combination: 600 DPI minimum
|
||||
|
||||
- **Color space**: RGB
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 5.5 cm (2.17 inches)
|
||||
- 1.5 column: 12 cm (4.72 inches)
|
||||
- Full width: 17.5 cm (6.89 inches)
|
||||
- Maximum height: 23.3 cm (9.17 inches)
|
||||
|
||||
- **Fonts**:
|
||||
- Helvetica (or Arial)
|
||||
- 6-8 pt minimum at final size
|
||||
- Consistent across all figures
|
||||
|
||||
### Science Specific Guidelines
|
||||
- Panel labels: (A), (B), (C) in parentheses
|
||||
- Minimal text within figures (details in caption)
|
||||
- High contrast for web and print
|
||||
- Error bars required; define in caption
|
||||
- Avoid excessive whitespace
|
||||
|
||||
### File Naming
|
||||
Format: `Manuscript#_Fig#.ext`
|
||||
Example: `abn1234_Fig1.eps`
|
||||
|
||||
## Cell Press (Cell, Neuron, Molecular Cell, etc.)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: PDF, EPS (preferred for graphs/diagrams)
|
||||
- Raster: TIFF (for photographs)
|
||||
|
||||
- **Resolution**:
|
||||
- Line art: 1000 DPI
|
||||
- Photographs: 300 DPI
|
||||
- Combination: 600 DPI
|
||||
|
||||
- **Color space**: RGB
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 85 mm (3.35 inches)
|
||||
- Double column: 178 mm (7.01 inches)
|
||||
- Maximum height: 230 mm (9.06 inches)
|
||||
|
||||
- **Fonts**:
|
||||
- Arial or Helvetica only
|
||||
- 8-12 pt for axis labels
|
||||
- 6-8 pt for tick labels
|
||||
|
||||
### Cell Press Specific Guidelines
|
||||
- Panel labels: (A), (B), (C) or A, B, C in top-left
|
||||
- Related panels should match in size
|
||||
- Scale bars mandatory for microscopy
|
||||
- Western blots: Include molecular weight markers
|
||||
- Arrows/arrowheads: 2 pt minimum width
|
||||
- Line widths: 1-2 pt for data
|
||||
|
||||
## PLOS (Public Library of Science)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: EPS, PDF (preferred)
|
||||
- Raster: TIFF, PNG
|
||||
- TIFF with LZW compression acceptable
|
||||
|
||||
- **Resolution**:
|
||||
- Minimum 300 DPI at final size (all figure types)
|
||||
- 600 DPI preferred for line art
|
||||
|
||||
- **Color space**: RGB
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 8.3 cm (3.27 inches)
|
||||
- 1.5 column: 11.4 cm (4.49 inches)
|
||||
- Double column: 17.3 cm (6.81 inches)
|
||||
- Maximum height: 23.3 cm (9.17 inches)
|
||||
|
||||
- **Fonts**:
|
||||
- Sans-serif preferred (Arial, Helvetica)
|
||||
- 8-12 pt for labels at final size
|
||||
|
||||
### PLOS Specific Guidelines
|
||||
- Figures should be understandable without caption
|
||||
- Color required only if adding information
|
||||
- All figures convertible to grayscale
|
||||
- Panel labels optional but recommended
|
||||
- Open access: Figures must be CC-BY licensed
|
||||
- Source data files encouraged
|
||||
|
||||
## ACS (American Chemical Society)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Preferred: TIFF, PDF, EPS
|
||||
- Application files: AI, CDX (ChemDraw), CDL
|
||||
- Acceptable: PNG (not for publication)
|
||||
|
||||
- **Resolution**:
|
||||
- Minimum 300 DPI at final size
|
||||
- 600 DPI for line art and chemical structures
|
||||
- 1200 DPI for detailed structures
|
||||
|
||||
- **Color space**: RGB or CMYK (check specific journal)
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 3.25 inches (8.25 cm)
|
||||
- Double column: 7 inches (17.78 cm)
|
||||
|
||||
- **Fonts**:
|
||||
- Embedded fonts required
|
||||
- Consistent sizing across figures
|
||||
|
||||
### ACS Specific Guidelines
|
||||
- Chemical structures: Use ChemDraw or equivalent
|
||||
- Atom labels: 10-12 pt
|
||||
- Bond thickness: 2 pt
|
||||
- Panel labels: Lowercase bold (a, b, c)
|
||||
- High contrast required (many ACS journals grayscale print)
|
||||
|
||||
## Elsevier Journals (varies by journal)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: EPS, PDF
|
||||
- Raster: TIFF, JPEG (only for photographs)
|
||||
|
||||
- **Resolution**:
|
||||
- Line art: 1000 DPI minimum
|
||||
- Photographs: 300 DPI minimum
|
||||
- Combination: 600 DPI minimum
|
||||
|
||||
- **Color space**: RGB (for online); CMYK (for print journals)
|
||||
|
||||
- **Dimensions**: Vary by journal
|
||||
- Common single column: 90 mm
|
||||
- Common double column: 190 mm
|
||||
|
||||
- **Fonts**:
|
||||
- Preferred: Arial, Times, Symbol
|
||||
- Minimum 6 pt at final size
|
||||
|
||||
### Elsevier Specific Guidelines
|
||||
- Check individual journal guidelines (highly variable)
|
||||
- Some journals charge for color in print
|
||||
- Panel labels typically (A), (B), (C) or A, B, C
|
||||
- Graphical abstract often required (separate from figures)
|
||||
|
||||
## IEEE (Engineering/Computer Science)
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Vector: PDF, EPS (preferred)
|
||||
- Raster: TIFF, PNG
|
||||
|
||||
- **Resolution**:
|
||||
- Photographs/graphics: 300 DPI minimum at final size
|
||||
- Line art: 600 DPI minimum
|
||||
|
||||
- **Color space**: RGB (online); CMYK (print)
|
||||
|
||||
- **Dimensions**:
|
||||
- Single column: 3.5 inches (8.9 cm)
|
||||
- Double column: 7.16 inches (18.2 cm)
|
||||
|
||||
- **Fonts**:
|
||||
- Sans-serif preferred
|
||||
- Minimum 8-10 pt at final size
|
||||
|
||||
### IEEE Specific Guidelines
|
||||
- Figures should be readable in black and white
|
||||
- Color figures incur no charge (online publication)
|
||||
- Panel labels: (a), (b), (c) in lowercase
|
||||
- Captions below figures (not on separate page)
|
||||
- Use IEEE graphics checker tool before submission
|
||||
|
||||
## BMC (BioMed Central) - Open Access
|
||||
|
||||
### Technical Specifications
|
||||
- **File formats**:
|
||||
- Any standard format accepted
|
||||
- Preferred: TIFF, PDF, EPS, PNG
|
||||
|
||||
- **Resolution**:
|
||||
- Minimum 600 DPI for line art
|
||||
- Minimum 300 DPI for photographs
|
||||
|
||||
- **Color space**: RGB
|
||||
|
||||
- **Dimensions**:
|
||||
- Flexible, but consider readability
|
||||
- Maximum width typically 140 mm
|
||||
|
||||
- **Fonts**:
|
||||
- Embedded and readable
|
||||
|
||||
### BMC Specific Guidelines
|
||||
- Open access: CC-BY license required
|
||||
- Figure files uploaded separately
|
||||
- Panel labels as appropriate for field
|
||||
- Source data encouraged
|
||||
- Accessibility important (colorblind-friendly)
|
||||
|
||||
## Common Requirements Across Journals
|
||||
|
||||
### Universal Best Practices
|
||||
1. **Never use JPEG for graphs/plots**: Compression artifacts
|
||||
2. **Embed all fonts**: In PDF/EPS files
|
||||
3. **Layer structure**: Flatten images (merge layers in Photoshop)
|
||||
4. **RGB vs CMYK**: Most journals now RGB (digital-first)
|
||||
5. **High resolution**: Always better to start high, reduce if needed
|
||||
6. **Consistency**: Same style across all figures in manuscript
|
||||
7. **File size**: Balance quality with reasonable file sizes (typically <10 MB per figure)
|
||||
|
||||
### Submitting Figures
|
||||
- **Initial submission**: Lower resolution often acceptable (for review)
|
||||
- **Revision/acceptance**: High-resolution required
|
||||
- **Separate files**: Each figure as separate file
|
||||
- **File naming**: Clear, systematic naming
|
||||
- **Supporting information**: May have different requirements
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| Publisher | Single Column | Double Column | Min DPI (photos) | Min DPI (line art) | Preferred Format |
|
||||
|-----------|---------------|---------------|------------------|-------------------|------------------|
|
||||
| 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 in | 7 in | 300 | 600 | TIFF, EPS |
|
||||
|
||||
## Checking Requirements
|
||||
|
||||
### Before Submission Checklist
|
||||
1. Read journal's author guidelines (figure section)
|
||||
2. Check file format requirements
|
||||
3. Verify resolution requirements
|
||||
4. Confirm size specifications (width × height)
|
||||
5. Check font requirements
|
||||
6. Verify color space (RGB vs CMYK)
|
||||
7. Check panel labeling style
|
||||
8. Review supplementary materials requirements
|
||||
9. Confirm file naming conventions
|
||||
10. Check file size limits
|
||||
|
||||
### Useful Tools
|
||||
- **ImageJ/Fiji**: Check/adjust DPI
|
||||
- **Adobe Acrobat**: Verify embedded fonts, check PDF properties
|
||||
- **GIMP**: Free alternative to Photoshop for raster editing
|
||||
- **Inkscape**: Free vector graphics editor
|
||||
|
||||
## Resources
|
||||
|
||||
- **Journal websites**: Always check "Author Guidelines" or "Instructions for Authors"
|
||||
- **Publisher resources**: Many provide templates and tools
|
||||
- **Format conversion**: Use reputable tools; check output quality
|
||||
- **Help desks**: Contact journal staff if unclear
|
||||
|
||||
## Notes
|
||||
|
||||
- Requirements change periodically - always verify current guidelines
|
||||
- Preprint servers (bioRxiv, arXiv) often have different requirements
|
||||
- Conference proceedings may have separate requirements
|
||||
- Some journals offer figure preparation services (often paid)
|
||||
- Supplementary figures may have relaxed requirements compared to main text figures
|
||||
@@ -0,0 +1,620 @@
|
||||
# Publication-Ready Matplotlib Examples
|
||||
|
||||
## Overview
|
||||
|
||||
This reference provides practical code examples for creating publication-ready scientific figures using Matplotlib, Seaborn, and Plotly. All examples follow best practices from `publication_guidelines.md` and use colorblind-friendly palettes from `color_palettes.md`.
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### Publication-Quality Matplotlib Configuration
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
import numpy as np
|
||||
|
||||
# Set publication quality parameters
|
||||
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
|
||||
|
||||
# Use colorblind-friendly colors (Okabe-Ito palette)
|
||||
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
|
||||
'#0072B2', '#D55E00', '#CC79A7', '#000000']
|
||||
mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=okabe_ito)
|
||||
|
||||
# Use perceptually uniform colormap
|
||||
mpl.rcParams['image.cmap'] = 'viridis'
|
||||
```
|
||||
|
||||
### Helper Function for Saving
|
||||
|
||||
```python
|
||||
def save_publication_figure(fig, filename, formats=['pdf', 'png'], dpi=300):
|
||||
"""
|
||||
Save figure in multiple formats for publication.
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
fig : matplotlib.figure.Figure
|
||||
Figure to save
|
||||
filename : str
|
||||
Base filename (without extension)
|
||||
formats : list
|
||||
List of file formats to save ['pdf', 'png', 'eps', 'svg']
|
||||
dpi : int
|
||||
Resolution for raster formats
|
||||
"""
|
||||
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"Saved: {output_file}")
|
||||
```
|
||||
|
||||
## Example 1: Line Plot with Error Bars
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Generate sample data
|
||||
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)
|
||||
|
||||
# Calculate means and standard errors for binned data
|
||||
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
|
||||
|
||||
# Create figure with appropriate size (single column width = 3.5 inches)
|
||||
fig, ax = plt.subplots(figsize=(3.5, 2.5))
|
||||
|
||||
# Plot with error bars
|
||||
ax.errorbar(x_binned, y1_mean, yerr=y1_sem,
|
||||
marker='o', markersize=4, capsize=3, capthick=0.5,
|
||||
label='Condition A', linewidth=1.5)
|
||||
|
||||
# Add labels with units
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Fluorescence intensity (a.u.)')
|
||||
|
||||
# Add legend
|
||||
ax.legend(frameon=False, loc='upper left')
|
||||
|
||||
# Remove top and right spines
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# Tight layout
|
||||
fig.tight_layout()
|
||||
|
||||
# Save
|
||||
save_publication_figure(fig, 'line_plot_with_errors')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Example 2: Multi-Panel Figure
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from string import ascii_uppercase
|
||||
|
||||
# Create figure with multiple panels (double column width = 7 inches)
|
||||
fig = plt.figure(figsize=(7, 4))
|
||||
|
||||
# Define grid for panels
|
||||
gs = fig.add_gridspec(2, 3, hspace=0.4, wspace=0.4,
|
||||
left=0.08, right=0.98, top=0.95, bottom=0.08)
|
||||
|
||||
# Panel A: Line plot
|
||||
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'Dataset {i+1}')
|
||||
ax_a.set_xlabel('Time (s)')
|
||||
ax_a.set_ylabel('Amplitude (V)')
|
||||
ax_a.legend(frameon=False, fontsize=6)
|
||||
ax_a.spines['top'].set_visible(False)
|
||||
ax_a.spines['right'].set_visible(False)
|
||||
|
||||
# Panel B: Bar plot
|
||||
ax_b = fig.add_subplot(gs[0, 2])
|
||||
categories = ['Control', 'Treatment\nA', 'Treatment\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('Response (%)')
|
||||
ax_b.spines['top'].set_visible(False)
|
||||
ax_b.spines['right'].set_visible(False)
|
||||
ax_b.set_ylim(0, 160)
|
||||
|
||||
# Panel C: Scatter plot
|
||||
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('Variable X')
|
||||
ax_c.set_ylabel('Variable Y')
|
||||
ax_c.spines['top'].set_visible(False)
|
||||
ax_c.spines['right'].set_visible(False)
|
||||
|
||||
# Panel D: Heatmap
|
||||
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('Sample number')
|
||||
ax_d.set_ylabel('Feature')
|
||||
cbar = plt.colorbar(im, ax=ax_d, fraction=0.046, pad=0.04)
|
||||
cbar.set_label('Intensity (a.u.)', rotation=270, labelpad=12)
|
||||
|
||||
# Add panel labels
|
||||
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()
|
||||
```
|
||||
|
||||
## Example 3: Box Plot with Individual Points
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Generate sample data
|
||||
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))
|
||||
|
||||
# Create box plot
|
||||
bp = ax.boxplot(data, widths=0.5, patch_artist=True,
|
||||
showfliers=False, # We'll add points manually
|
||||
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))
|
||||
|
||||
# Overlay individual points
|
||||
colors = ['#0072B2', '#E69F00', '#009E73', '#D55E00']
|
||||
for i, (d, color) in enumerate(zip(data, colors)):
|
||||
# Add jitter to x positions
|
||||
x = np.random.normal(i+1, 0.04, size=len(d))
|
||||
ax.scatter(x, d, alpha=0.4, s=8, color=color)
|
||||
|
||||
# Customize
|
||||
ax.set_xticklabels(['Control', 'Treatment A', 'Treatment B', 'Treatment C'])
|
||||
ax.set_ylabel('Cell count')
|
||||
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()
|
||||
```
|
||||
|
||||
## Example 4: Heatmap with Colorbar
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Generate correlation matrix
|
||||
np.random.seed(42)
|
||||
n = 10
|
||||
A = np.random.randn(n, n)
|
||||
corr_matrix = np.corrcoef(A)
|
||||
|
||||
# Create figure
|
||||
fig, ax = plt.subplots(figsize=(4, 3.5))
|
||||
|
||||
# Plot heatmap
|
||||
im = ax.imshow(corr_matrix, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto')
|
||||
|
||||
# Add colorbar
|
||||
cbar = plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
||||
cbar.set_label('Correlation coefficient', rotation=270, labelpad=15)
|
||||
|
||||
# Set ticks and labels
|
||||
gene_names = [f'Gene{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)
|
||||
|
||||
# Add grid
|
||||
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()
|
||||
```
|
||||
|
||||
## Example 5: Seaborn Violin Plot
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
# Generate sample data
|
||||
np.random.seed(42)
|
||||
data = pd.DataFrame({
|
||||
'condition': np.repeat(['Control', 'Drug A', 'Drug B'], 50),
|
||||
'value': np.concatenate([
|
||||
np.random.normal(100, 15, 50),
|
||||
np.random.normal(120, 20, 50),
|
||||
np.random.normal(140, 18, 50)
|
||||
])
|
||||
})
|
||||
|
||||
# Set style
|
||||
sns.set_style('ticks')
|
||||
sns.set_palette(['#0072B2', '#E69F00', '#009E73'])
|
||||
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3))
|
||||
|
||||
# Create violin plot
|
||||
sns.violinplot(data=data, x='condition', y='value', ax=ax,
|
||||
inner='box', linewidth=0.8)
|
||||
|
||||
# Add strip plot
|
||||
sns.stripplot(data=data, x='condition', y='value', ax=ax,
|
||||
size=2, alpha=0.3, color='black')
|
||||
|
||||
# Customize
|
||||
ax.set_xlabel('')
|
||||
ax.set_ylabel('Expression level (AU)')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'violin_plot')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Example 6: Scientific Scatter with Regression
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
|
||||
# Generate data with correlation
|
||||
np.random.seed(42)
|
||||
x = np.random.randn(100)
|
||||
y = 2.5 * x + np.random.randn(100) * 0.8
|
||||
|
||||
# Calculate regression
|
||||
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
|
||||
|
||||
# Create figure
|
||||
fig, ax = plt.subplots(figsize=(3.5, 3.5))
|
||||
|
||||
# Scatter plot
|
||||
ax.scatter(x, y, s=15, alpha=0.6, color='#0072B2', edgecolors='none')
|
||||
|
||||
# Regression line
|
||||
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}')
|
||||
|
||||
# Add statistics text
|
||||
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))
|
||||
|
||||
# Customize
|
||||
ax.set_xlabel('Predictor variable')
|
||||
ax.set_ylabel('Response variable')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
save_publication_figure(fig, 'scatter_regression')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Example 7: Time Series with Shaded Error
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Generate time series data
|
||||
np.random.seed(42)
|
||||
time = np.linspace(0, 24, 100)
|
||||
n_replicates = 5
|
||||
|
||||
# Simulate multiple replicates
|
||||
data = np.array([10 * np.exp(-time/10) + np.random.normal(0, 0.5, 100)
|
||||
for _ in range(n_replicates)])
|
||||
|
||||
# Calculate mean and SEM
|
||||
mean = data.mean(axis=0)
|
||||
sem = data.std(axis=0) / np.sqrt(n_replicates)
|
||||
|
||||
# Create figure
|
||||
fig, ax = plt.subplots(figsize=(4, 2.5))
|
||||
|
||||
# Plot mean line
|
||||
ax.plot(time, mean, linewidth=1.5, color='#0072B2', label='Mean ± SEM')
|
||||
|
||||
# Add shaded error region
|
||||
ax.fill_between(time, mean - sem, mean + sem,
|
||||
alpha=0.3, color='#0072B2', linewidth=0)
|
||||
|
||||
# Customize
|
||||
ax.set_xlabel('Time (hours)')
|
||||
ax.set_ylabel('Concentration (μ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()
|
||||
```
|
||||
|
||||
## Example 8: Plotly Interactive Figure
|
||||
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
import numpy as np
|
||||
|
||||
# Generate data
|
||||
np.random.seed(42)
|
||||
x = np.random.randn(100)
|
||||
y = 2*x + np.random.randn(100)
|
||||
colors = np.random.choice(['Group A', 'Group B'], 100)
|
||||
|
||||
# Okabe-Ito colors for Plotly
|
||||
okabe_ito_plotly = ['#E69F00', '#56B4E9']
|
||||
|
||||
# Create figure
|
||||
fig = go.Figure()
|
||||
|
||||
for group, color in zip(['Group A', 'Group 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)
|
||||
))
|
||||
|
||||
# Update layout for publication quality
|
||||
fig.update_layout(
|
||||
width=500,
|
||||
height=400,
|
||||
font=dict(family='Arial, sans-serif', size=10),
|
||||
plot_bgcolor='white',
|
||||
xaxis=dict(
|
||||
title='Variable X',
|
||||
showgrid=False,
|
||||
showline=True,
|
||||
linewidth=1,
|
||||
linecolor='black',
|
||||
mirror=False
|
||||
),
|
||||
yaxis=dict(
|
||||
title='Variable 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
|
||||
)
|
||||
)
|
||||
|
||||
# Save as static image (requires kaleido)
|
||||
fig.write_image('plotly_scatter.png', width=500, height=400, scale=3) # scale=3 gives ~300 DPI
|
||||
fig.write_html('plotly_scatter.html') # Interactive version
|
||||
|
||||
fig.show()
|
||||
```
|
||||
|
||||
## Example 9: Grouped Bar Plot with Significance
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Data
|
||||
categories = ['WT', 'Mutant A', 'Mutant 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))
|
||||
|
||||
# Create bars
|
||||
bars1 = ax.bar(x - width/2, control_means, width, yerr=control_sem,
|
||||
capsize=3, label='Control', color='#0072B2', alpha=0.8)
|
||||
bars2 = ax.bar(x + width/2, treatment_means, width, yerr=treatment_sem,
|
||||
capsize=3, label='Treatment', color='#E69F00', alpha=0.8)
|
||||
|
||||
# Add significance markers
|
||||
def add_significance_bar(ax, x1, x2, y, h, text):
|
||||
"""Add significance bar between two bars"""
|
||||
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)
|
||||
|
||||
# Mark significant differences
|
||||
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, '***')
|
||||
|
||||
# Customize
|
||||
ax.set_ylabel('Activity (% of WT control)')
|
||||
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)
|
||||
|
||||
# Add note about significance
|
||||
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()
|
||||
```
|
||||
|
||||
## Example 10: Publication-Ready Figure for Nature
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from string import ascii_lowercase
|
||||
|
||||
# Nature specifications: 89mm single column
|
||||
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)
|
||||
|
||||
# Panel a: Time course
|
||||
ax_a = fig.add_subplot(gs[0, :])
|
||||
time = np.linspace(0, 48, 100)
|
||||
for i, label in enumerate(['Control', 'Treatment']):
|
||||
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('Time (h)', fontsize=7)
|
||||
ax_a.set_ylabel('Growth (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)
|
||||
|
||||
# Panel b: Bar plot
|
||||
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('Fold change', fontsize=7)
|
||||
ax_b.tick_params(labelsize=6)
|
||||
ax_b.spines['top'].set_visible(False)
|
||||
ax_b.spines['right'].set_visible(False)
|
||||
|
||||
# Panel c: Heatmap
|
||||
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('Sample', fontsize=7)
|
||||
ax_c.set_ylabel('Gene', fontsize=7)
|
||||
ax_c.tick_params(labelsize=6)
|
||||
|
||||
# Panel d: Scatter
|
||||
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('Expression gene X', fontsize=7)
|
||||
ax_d.set_ylabel('Expression gene Y', fontsize=7)
|
||||
ax_d.tick_params(labelsize=6)
|
||||
ax_d.spines['top'].set_visible(False)
|
||||
ax_d.spines['right'].set_visible(False)
|
||||
|
||||
# Add lowercase panel labels (Nature style)
|
||||
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')
|
||||
|
||||
# Save in Nature-preferred format
|
||||
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()
|
||||
```
|
||||
|
||||
## Tips for Each Library
|
||||
|
||||
### Matplotlib
|
||||
- Use `fig.tight_layout()` or `constrained_layout=True` to prevent overlapping
|
||||
- Set DPI to 300-600 for publication
|
||||
- Use vector formats (PDF, EPS) for line plots
|
||||
- Embed fonts in PDF/EPS files
|
||||
|
||||
### Seaborn
|
||||
- Built on matplotlib, so all matplotlib customizations work
|
||||
- Use `sns.set_style('ticks')` or `'whitegrid'` for clean looks
|
||||
- `sns.despine()` removes top and right spines
|
||||
- Set custom palette with `sns.set_palette()`
|
||||
|
||||
### Plotly
|
||||
- Great for interactive exploratory analysis
|
||||
- Export static images with `fig.write_image()` (requires kaleido package)
|
||||
- Use `scale` parameter to control DPI (scale=3 ≈ 300 DPI)
|
||||
- Update layout extensively for publication quality
|
||||
|
||||
## Common Workflow
|
||||
|
||||
1. **Explore with default settings**
|
||||
2. **Apply publication configuration** (see Setup section)
|
||||
3. **Create plot with appropriate size** (check journal requirements)
|
||||
4. **Customize colors** (use colorblind-friendly palettes)
|
||||
5. **Adjust fonts and line widths** (readable at final size)
|
||||
6. **Remove chart junk** (top/right spines, excessive grid)
|
||||
7. **Add clear labels with units**
|
||||
8. **Test in grayscale**
|
||||
9. **Save in multiple formats** (PDF for vector, PNG for raster)
|
||||
10. **Verify in final context** (import into manuscript to check size)
|
||||
|
||||
## Resources
|
||||
|
||||
- Matplotlib documentation: https://matplotlib.org/
|
||||
- Seaborn gallery: https://seaborn.pydata.org/examples/index.html
|
||||
- Plotly documentation: https://plotly.com/python/
|
||||
- Nature Methods Points of View: Data visualization column archive
|
||||
@@ -0,0 +1,205 @@
|
||||
# Publication-Ready Figure Guidelines
|
||||
|
||||
## Core Principles
|
||||
|
||||
Scientific figures must be clear, accurate, and accessible. Publication-ready figures follow these fundamental principles:
|
||||
|
||||
1. **Clarity**: Information should be immediately understandable
|
||||
2. **Accuracy**: Data representation must be truthful and unmanipulated
|
||||
3. **Accessibility**: Figures should be interpretable by all readers, including those with visual impairments
|
||||
4. **Professional**: Clean, polished appearance suitable for peer-reviewed journals
|
||||
|
||||
## Resolution and File Format
|
||||
|
||||
### Resolution Requirements
|
||||
- **Raster images (photos, microscopy)**: 300-600 DPI at final print size
|
||||
- **Line art and graphs**: 600-1200 DPI (or vector format)
|
||||
- **Combined figures**: 300-600 DPI
|
||||
|
||||
### File Formats
|
||||
- **Vector formats (preferred for graphs/plots)**: PDF, EPS, SVG
|
||||
- Infinitely scalable without quality loss
|
||||
- Smaller file sizes for line art
|
||||
- Best for: plots, diagrams, schematics
|
||||
|
||||
- **Raster formats**: TIFF, PNG (never JPEG for scientific data)
|
||||
- Use for: photographs, microscopy, images with continuous tone
|
||||
- TIFF: Lossless, widely accepted
|
||||
- PNG: Lossless, good for web and supplementary materials
|
||||
- **Never use JPEG**: Lossy compression introduces artifacts
|
||||
|
||||
### Size Specifications
|
||||
- **Single column**: 85-90 mm (3.35-3.54 inches) width
|
||||
- **1.5 column**: 114-120 mm (4.49-4.72 inches) width
|
||||
- **Double column**: 174-180 mm (6.85-7.08 inches) width
|
||||
- **Maximum height**: Usually 230-240 mm (9-9.5 inches)
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Guidelines
|
||||
- **Font family**: Sans-serif fonts (Arial, Helvetica, Calibri) for most journals
|
||||
- Some journals prefer specific fonts (check guidelines)
|
||||
- Consistency across all figures in manuscript
|
||||
|
||||
- **Font sizes at final print size**:
|
||||
- Axis labels: 7-9 pt minimum
|
||||
- Tick labels: 6-8 pt minimum
|
||||
- Legends: 6-8 pt
|
||||
- Panel labels (A, B, C): 8-12 pt, bold
|
||||
- Title: Generally avoided in multi-panel figures
|
||||
|
||||
- **Font weight**: Regular weight for most text; bold for panel labels only
|
||||
|
||||
### Text Best Practices
|
||||
- Use sentence case for axis labels ("Time (hours)" not "TIME (HOURS)")
|
||||
- Include units in parentheses
|
||||
- Avoid abbreviations unless space-constrained (define in caption)
|
||||
- No text smaller than 5-6 pt at final size
|
||||
|
||||
## Color Usage
|
||||
|
||||
### Color Selection Principles
|
||||
1. **Colorblind-friendly**: ~8% of males have color vision deficiency
|
||||
- Avoid red/green combinations
|
||||
- Use blue/orange, blue/yellow, or add texture/pattern
|
||||
- Test with colorblindness simulators
|
||||
|
||||
2. **Purposeful color**: Color should convey meaning, not just aesthetics
|
||||
- Use color to distinguish categories or highlight key data
|
||||
- Maintain consistency across figures (same treatment = same color)
|
||||
|
||||
3. **Print considerations**:
|
||||
- Colors may appear different in print vs. screen
|
||||
- Use CMYK color space for print, RGB for digital
|
||||
- Ensure sufficient contrast (especially for grayscale conversion)
|
||||
|
||||
### Recommended Color Palettes
|
||||
- **Qualitative (categories)**: ColorBrewer, Okabe-Ito palette
|
||||
- **Sequential (low to high)**: Viridis, Cividis, Blues, Oranges
|
||||
- **Diverging (negative to positive)**: RdBu, PuOr, BrBG (ensure colorblind-safe)
|
||||
|
||||
### Grayscale Compatibility
|
||||
- All figures should be interpretable in grayscale
|
||||
- Use different line styles (solid, dashed, dotted) and markers
|
||||
- Add patterns/hatching to bars and areas
|
||||
|
||||
## Layout and Composition
|
||||
|
||||
### Multi-Panel Figures
|
||||
- **Panel labels**: Use bold uppercase letters (A, B, C) in top-left corner
|
||||
- **Spacing**: Adequate white space between panels
|
||||
- **Alignment**: Align panels along edges or axes where possible
|
||||
- **Sizing**: Related panels should have consistent sizes
|
||||
- **Arrangement**: Logical flow (left-to-right, top-to-bottom)
|
||||
|
||||
### Plot Elements
|
||||
|
||||
#### Axes
|
||||
- **Axis lines**: 0.5-1 pt thickness
|
||||
- **Tick marks**: Point inward or outward consistently
|
||||
- **Tick frequency**: Enough to read values, not cluttered (typically 4-7 major ticks)
|
||||
- **Axis labels**: Required on all plots; state units
|
||||
- **Axis ranges**: Start from zero for bar charts (unless scientifically inappropriate)
|
||||
|
||||
#### Lines and Markers
|
||||
- **Line width**: 1-2 pt for data lines; 0.5-1 pt for reference lines
|
||||
- **Marker size**: 3-6 pt, larger than line width
|
||||
- **Marker types**: Differentiate when multiple series (circles, squares, triangles)
|
||||
- **Error bars**: 0.5-1 pt width; include caps if appropriate
|
||||
|
||||
#### Legends
|
||||
- **Position**: Inside plot area if space permits, outside otherwise
|
||||
- **Frame**: Optional; if used, thin line (0.5 pt)
|
||||
- **Order**: Match order of data appearance (top to bottom or left to right)
|
||||
- **Content**: Concise descriptions; full details in caption
|
||||
|
||||
### White Space and Margins
|
||||
- Remove unnecessary white space around plots
|
||||
- Maintain consistent margins
|
||||
- `tight_layout()` or `constrained_layout=True` in matplotlib
|
||||
|
||||
## Data Representation Best Practices
|
||||
|
||||
### Statistical Rigor
|
||||
- **Error bars**: Always show uncertainty (SD, SEM, CI) and state which in caption
|
||||
- **Sample size**: Indicate n in figure or caption
|
||||
- **Significance**: Mark statistical significance clearly (*, **, ***)
|
||||
- **Replicates**: Show individual data points when possible, not just summary statistics
|
||||
|
||||
### Appropriate Chart Types
|
||||
- **Bar plots**: Comparing discrete categories; always start y-axis at zero
|
||||
- **Line plots**: Time series or continuous relationships
|
||||
- **Scatter plots**: Correlation between variables; add regression line if appropriate
|
||||
- **Box plots**: Distribution comparisons; show outliers
|
||||
- **Heatmaps**: Matrix data, correlations, expression patterns
|
||||
- **Violin plots**: Distribution shape comparison (better than box plots for bimodal data)
|
||||
|
||||
### Avoiding Distortion
|
||||
- **No 3D effects**: Distorts perception of values
|
||||
- **No unnecessary decorations**: No gradients, shadows, or chart junk
|
||||
- **Consistent scales**: Use same scale for comparable panels
|
||||
- **No truncated axes**: Unless clearly indicated and scientifically justified
|
||||
- **Linear vs. log scales**: Choose appropriate scale; always label clearly
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Colorblind Considerations
|
||||
- Test with online simulators (e.g., Coblis, Color Oracle)
|
||||
- Use patterns/textures in addition to color
|
||||
- Provide alternative representations in supplementary materials if needed
|
||||
|
||||
### Visual Impairment
|
||||
- High contrast between elements
|
||||
- Thick enough lines (minimum 0.5 pt)
|
||||
- Clear, uncluttered layouts
|
||||
|
||||
### Data Availability
|
||||
- Include data tables in supplementary materials
|
||||
- Provide source data files for graphs
|
||||
- Consider interactive figures for online supplementary materials
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
1. **Font too small**: Text unreadable at final print size
|
||||
2. **Low resolution**: Pixelated or blurry images
|
||||
3. **Chart junk**: Unnecessary grid lines, 3D effects, decorations
|
||||
4. **Poor color choices**: Red/green combinations, low contrast
|
||||
5. **Missing elements**: No axis labels, no units, no error bars
|
||||
6. **Inconsistent styling**: Different fonts/sizes within figure or between figures
|
||||
7. **Data distortion**: Truncated axes, inappropriate scales, 3D effects
|
||||
8. **JPEG compression**: Artifacts around text and lines
|
||||
9. **Too much information**: Cramming too many data series into one plot
|
||||
10. **Inaccessible legends**: Legends outside the figure boundary after export
|
||||
|
||||
## Figure Checklist
|
||||
|
||||
Before submission, verify:
|
||||
|
||||
- [ ] Resolution meets journal requirements (300+ DPI for raster)
|
||||
- [ ] File format is acceptable (vector for plots, TIFF/PNG for images)
|
||||
- [ ] Figure dimensions match journal specifications
|
||||
- [ ] All text is readable at final size (minimum 6-7 pt)
|
||||
- [ ] Fonts are consistent and embedded (for PDF/EPS)
|
||||
- [ ] Colors are colorblind-friendly
|
||||
- [ ] Figure is interpretable in grayscale
|
||||
- [ ] All axes are labeled with units
|
||||
- [ ] Error bars or uncertainty indicators are present
|
||||
- [ ] Statistical significance is marked if applicable
|
||||
- [ ] Panel labels are present and consistent (A, B, C)
|
||||
- [ ] Legend is clear and complete
|
||||
- [ ] No chart junk or unnecessary elements
|
||||
- [ ] File naming follows journal conventions
|
||||
- [ ] Figure caption is comprehensive
|
||||
- [ ] Source data is available
|
||||
|
||||
## Journal-Specific Considerations
|
||||
|
||||
Always consult the specific journal's author guidelines. Common variations include:
|
||||
|
||||
- **Nature journals**: RGB, 300 DPI minimum, specific size requirements
|
||||
- **Science**: EPS or high-res TIFF, specific font requirements
|
||||
- **Cell Press**: PDF or EPS preferred, Arial or Helvetica fonts
|
||||
- **PLOS**: TIFF or EPS, specific color space requirements
|
||||
- **ACS journals**: Application files (AI, EPS) or high-res TIFF
|
||||
|
||||
See `journal_requirements.md` for detailed specifications from major publishers.
|
||||
343
skills/scientific-visualization/scripts/figure_export.py
Normal file
343
skills/scientific-visualization/scripts/figure_export.py
Normal 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
skills/scientific-visualization/scripts/style_presets.py
Normal file
416
skills/scientific-visualization/scripts/style_presets.py
Normal 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()
|
||||
Reference in New Issue
Block a user