Initial commit
This commit is contained in:
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