621 lines
18 KiB
Markdown
621 lines
18 KiB
Markdown
# 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
|