figure-graphing — community figure-graphing, Cardiac_RODEO, community, ide skills

v1.0.0

Об этом навыке

Идеально подходит для агентов научного анализа, которым необходимы расширенные возможности графиков для создания публикаций высокого качества. Create publication-quality figures and graphs for scientific analysis. Use when creating bar charts, ROC curves, confusion matrices, scatter plots, heatmaps, 3D surface plots, or any data visualization. Supports PDF and PNG output with consistent styling.

NoahB10 NoahB10
[0]
[0]
Updated: 3/9/2026

Killer-Skills Review

Decision support comes first. Repository text comes second.

Reference-Only Page Review Score: 9/11

This page remains useful for operators, but Killer-Skills treats it as reference material instead of a primary organic landing page.

Original recommendation layer Concrete use-case guidance Explicit limitations and caution Quality floor passed for review
Review Score
9/11
Quality Score
60
Canonical Locale
en
Detected Body Locale
en

Идеально подходит для агентов научного анализа, которым необходимы расширенные возможности графиков для создания публикаций высокого качества. Create publication-quality figures and graphs for scientific analysis. Use when creating bar charts, ROC curves, confusion matrices, scatter plots, heatmaps, 3D surface plots, or any data visualization. Supports PDF and PNG output with consistent styling.

Зачем использовать этот навык

Позволяет агентам создавать публикации высокого качества для научных статей и отчетов с помощью Python, с автоматической синхронизацией PowerPoint через файлы .pptx, и создает PNG-файлы для бесшовной интеграции.

Подходит лучше всего

Идеально подходит для агентов научного анализа, которым необходимы расширенные возможности графиков для создания публикаций высокого качества.

Реализуемые кейсы использования for figure-graphing

Автоматизировать создание графиков для научных статей
Создавать публикации высокого качества с автоматической синхронизацией PowerPoint
Создавать PNG-файлы для отчетов и презентаций

! Безопасность и ограничения

  • Требуется среда Python
  • Ограничен созданием графиков только в формате PNG
  • Зависит от скрипта `generate_paper_figures.py` для автоматизации

Why this page is reference-only

  • - Current locale does not satisfy the locale-governance contract.

Source Boundary

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

After The Review

Decide The Next Action Before You Keep Reading Repository Material

Killer-Skills should not stop at opening repository instructions. It should help you decide whether to install this skill, when to cross-check against trusted collections, and when to move into workflow rollout.

Labs Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox

FAQ & Installation Steps

These questions and steps mirror the structured data on this page for better search understanding.

? Frequently Asked Questions

What is figure-graphing?

Идеально подходит для агентов научного анализа, которым необходимы расширенные возможности графиков для создания публикаций высокого качества. Create publication-quality figures and graphs for scientific analysis. Use when creating bar charts, ROC curves, confusion matrices, scatter plots, heatmaps, 3D surface plots, or any data visualization. Supports PDF and PNG output with consistent styling.

How do I install figure-graphing?

Run the command: npx killer-skills add NoahB10/Cardiac_RODEO/figure-graphing. It works with Cursor, Windsurf, VS Code, Claude Code, and 19+ other IDEs.

What are the use cases for figure-graphing?

Key use cases include: Автоматизировать создание графиков для научных статей, Создавать публикации высокого качества с автоматической синхронизацией PowerPoint, Создавать PNG-файлы для отчетов и презентаций.

Which IDEs are compatible with figure-graphing?

This skill is compatible with Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer. Use the Killer-Skills CLI for universal one-command installation.

Are there any limitations for figure-graphing?

Требуется среда Python. Ограничен созданием графиков только в формате PNG. Зависит от скрипта `generate_paper_figures.py` для автоматизации.

How To Install

  1. 1. Open your terminal

    Open the terminal or command line in your project directory.

  2. 2. Run the install command

    Run: npx killer-skills add NoahB10/Cardiac_RODEO/figure-graphing. The CLI will automatically detect your IDE or AI agent and configure the skill.

  3. 3. Start using the skill

    The skill is now active. Your AI agent can use figure-graphing immediately in the current project.

! Reference-Only Mode

This page remains useful for installation and reference, but Killer-Skills no longer treats it as a primary indexable landing page. Read the review above before relying on the upstream repository instructions.

Upstream Repository Material

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

Upstream Source

figure-graphing

Install figure-graphing, an AI agent skill for AI agent workflows and automation. Review the use cases, limitations, and setup path before rollout.

SKILL.md
Readonly
Upstream Repository Material
The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.
Supporting Evidence

Figure Graphing Skill

Create publication-quality figures for scientific papers and reports.

Automatic PowerPoint Sync

IMPORTANT: When generating figures using generate_paper_figures.py, the PowerPoint is automatically updated:

bash
1# Generate figure and auto-update PowerPoint 2python generate_paper_figures.py --figure 3 3 4# Skip PowerPoint update (figures only) 5python generate_paper_figures.py --figure 3 --no-pptx

The script automatically:

  1. Generates figure PNGs to Output/PowerPoint_Figures/Fig_X/
  2. Unpacks Output/PowerPoint_Figures/Cardiac_RODEO_Tracked.pptx
  3. Copies updated images to the correct media slots
  4. Repacks the PowerPoint

Figure-to-Image Mapping:

FigurePowerPoint ImagesSlide
Fig_3 (a-e)image4-83
Fig_6 (a-h)image11-186
Fig_7 (a-h)image19-267
Fig_8 (a-f)image27-328

Quick Reference

Standard Figure Types

  1. Bar Charts - Accuracy, AUC, metric comparisons (always with error bars)
  2. Grouped Bar Charts - Multiple metrics per model (always with error bars)
  3. ROC Curves - MUST have shaded confidence bands (bootstrap ±1 std, use fill_between)
  4. Confusion Matrices - With counts and percentages (Blues cmap)
  5. Scatter Plots - Per-drug/per-sample predictions
  6. Heatmaps - Correlation (RdBu_r), performance (RdYlGn), SHAP (coolwarm)
  7. Threshold Analysis - Horizontal scatter, drugs on Y-axis, green/red colors
  8. 3D Surface Plots - PK-PD response surfaces
  9. SHAP Aligned Pairs - Positive/negative SHAP paired by magnitude, blue/grey colors
  10. Accuracy vs AUC Scatter - X=Accuracy, Y=AUC, comparing equations across ML models

Color Palettes

Primary Color Palette (MANDATORY)

Use these colors consistently across ALL figures:

python
1PRIMARY_COLORS = { 2 'beige': '#E3D5B2', # Warm neutral - backgrounds, secondary elements 3 'blue': '#6C92ED', # Primary accent - main data series 4 'pink': '#ECA0C0', # Secondary accent - comparison data 5 'orange': '#F8B274', # Tertiary accent - highlights 6} 7 8# Extended palette (same theme) 9EXTENDED_COLORS = { 10 'dark_blue': '#4A6FBF', # Darker blue for emphasis 11 'light_pink': '#F5C6D6', # Lighter pink for fills 12 'coral': '#E89B7A', # Warm coral 13 'sage': '#A8C4A2', # Muted green 14 'lavender': '#B8A9D9', # Soft purple 15 'cream': '#F5EFE0', # Light background 16}

Model Comparison Colors

python
1colors = { 2 'CNN (DIQT Transfer)': '#ECA0C0', # Pink 3 'CNN (5-fold on 25)': '#6C92ED', # Blue 4 'Organoid (5-fold)': '#F8B274', # Orange 5 'ADMET-AI': '#6C92ED', # Blue 6 'SwissADME': '#ECA0C0', # Pink 7}

Metric Colors

python
1metric_colors = { 2 'Accuracy': '#6C92ED', # Blue 3 'F1 Score': '#F8B274', # Orange 4 'MCC': '#ECA0C0', # Pink 5 'AUC': '#4A6FBF', # Dark Blue 6 'Sensitivity': '#E89B7A', # Coral 7 'Specificity': '#A8C4A2', # Sage 8}

Classification Colors

python
1class_colors = { 2 'Positive': '#ECA0C0', # Pink 3 'Negative': '#6C92ED', # Blue 4 'True Positive': '#6C92ED', 5 'False Positive': '#F8B274', 6 'True Negative': '#A8C4A2', 7 'False Negative': '#E89B7A', 8}

Output Requirements

CRITICAL: Size Changes Mean GRAPH Size, Not Image Canvas

When the user asks to change figure dimensions (e.g., "make it smaller", "1.7 inches"), they mean the ACTUAL GRAPH/PLOT size, not the overall image canvas.

DO NOT:

  • Shrink the graph while keeping the image canvas the same size (creates wasted whitespace)
  • Add padding/margins to achieve a target image size
  • Scale down the plot area within a larger figure

DO:

  • Change figsize=(width, height) to directly control the plot dimensions
  • The graph should fill the figure with minimal margins
  • Use bbox_inches='tight' when saving to remove excess whitespace
python
1# CORRECT: figsize controls the actual graph size 2fig, ax = plt.subplots(figsize=(1.7, 1.7)) # Graph is 1.7" x 1.7" 3plt.savefig('output.png', dpi=600, bbox_inches='tight') 4 5# WRONG: Large canvas with small graph inside 6fig, ax = plt.subplots(figsize=(4, 4)) # 4" canvas 7ax.set_position([0.3, 0.3, 0.4, 0.4]) # Graph only 1.6" - DON'T DO THIS

The figsize parameter IS the graph size (plus minimal axis labels/title). When asked for "1.7 inch square", use figsize=(1.7, 1.7).

Always Save Both Formats

python
1# Save PDF for LaTeX/publication 2plt.savefig('Output/path/figures/figure_name.pdf', bbox_inches='tight') 3# Save PNG at 600 DPI for high-quality viewing 4plt.savefig('Output/path/Figure_Name.png', dpi=600, bbox_inches='tight') 5plt.close()

Standard Figure Sizes

python
1# Single panel 2fig, ax = plt.subplots(figsize=(8, 6)) 3 4# Side-by-side panels 5fig, axes = plt.subplots(1, 2, figsize=(12, 5)) 6 7# Three panels horizontal 8fig, axes = plt.subplots(1, 3, figsize=(14, 4)) 9 10# Grid layout 11fig, axes = plt.subplots(2, 2, figsize=(12, 10)) 12 13# Wide scatter plot (per-drug) 14fig, ax = plt.subplots(figsize=(16, 6)) 15 16# Default square graph (for bar charts, scatter plots, Accuracy vs AUC, etc.) 17SQUARE_SIZE = 1.7 # inches - standard square panel 18fig, ax = plt.subplots(figsize=(SQUARE_SIZE, SQUARE_SIZE)) 19 20# Heatmap (MANDATORY size - 1:2 ratio) 21HEATMAP_HEIGHT = 1.7 # inches 22HEATMAP_WIDTH = 3.4 # inches (2x height) 23fig, ax = plt.subplots(figsize=(HEATMAP_WIDTH, HEATMAP_HEIGHT)) 24 25# Accuracy vs AUC scatter (use square size, same as bar charts) 26fig, ax = plt.subplots(figsize=(1.7, 1.7)) # 1:1 square 27# Or for 3-panel comparison with shared axis: 28fig, axes = plt.subplots(1, 3, figsize=(5.1, 1.7), sharey=True) # 3 × 1.7" width 29 30# Bar chart (use square size) 31fig, ax = plt.subplots(figsize=(1.7, 1.7)) # Standard square

Common Figure Templates

1. Grouped Bar Chart (Accuracy, F1, MCC) with Error Bars

python
1import numpy as np 2import matplotlib.pyplot as plt 3 4models = ['Model A', 'Model B', 'Model C'] 5metrics = ['Accuracy', 'F1 Score', 'MCC'] 6# Mean values 7data = np.array([ 8 [0.56, 0.72, 0.00], # Model A 9 [0.68, 0.73, 0.34], # Model B 10 [0.74, 0.77, 0.46], # Model C 11]) 12# Standard deviations (ALWAYS include error bars) 13data_std = np.array([ 14 [0.05, 0.04, 0.00], # Model A 15 [0.03, 0.05, 0.08], # Model B 16 [0.04, 0.03, 0.06], # Model C 17]) 18 19x = np.arange(len(models)) 20width = 0.25 21colors = ['#6C92ED', '#F8B274', '#ECA0C0'] # Use project color palette 22 23# Use 1.7" square for single bar charts (or scale up for grouped) 24SQUARE_SIZE = 1.7 25fig, ax = plt.subplots(figsize=(SQUARE_SIZE * 2, SQUARE_SIZE)) # 2:1 ratio for grouped 26for i, (metric, color) in enumerate(zip(metrics, colors)): 27 bars = ax.bar(x + i*width - width, data[:, i], width, 28 yerr=data_std[:, i], capsize=4, # Error bars with caps 29 label=metric, color=color, edgecolor='black') 30 # Add value labels above error bars 31 for j, bar in enumerate(bars): 32 height = bar.get_height() 33 err = data_std[j, i] 34 ax.annotate(f'{height:.2f}', 35 xy=(bar.get_x() + bar.get_width()/2, height + err), 36 xytext=(0, 3), textcoords='offset points', 37 ha='center', va='bottom', fontsize=9, fontweight='bold') 38 39ax.set_ylabel('Score', fontsize=9) 40ax.set_title('Performance Comparison', fontsize=10, fontweight='bold') 41ax.set_xticks(x) 42ax.set_xticklabels(models, fontsize=8) 43ax.tick_params(axis='both', labelsize=8) 44ax.legend(loc='upper left', fontsize=8) 45ax.set_ylim(0, 1.1) # Extra space for error bars 46ax.grid(axis='y', alpha=0.3) 47plt.tight_layout()

2. ROC Curve with Bootstrap Confidence Bands (MANDATORY)

CRITICAL: ALL ROC curves MUST include shaded confidence bands. ROC curves without shaded uncertainty regions are NOT acceptable for publication figures.

What the shaded band represents:

  • The band shows ±1 standard deviation of TPR at each FPR point
  • Computed via bootstrap resampling (default: 300 iterations)
  • Wider bands = more uncertainty, narrower bands = more confidence
  • Band is clamped to [0, 1] range (valid probability bounds)
python
1import figure_config # FIRST LINE - registers Helvetica 2from sklearn.metrics import roc_curve, auc 3import numpy as np 4import matplotlib.pyplot as plt 5 6def bootstrap_roc_stats(y_true, y_prob, n_boot=300, seed=42): 7 """ 8 Bootstrap ROC statistics for confidence intervals. 9 10 Returns: 11 mean_fpr: Common FPR points (0 to 1, 100 points) 12 mean_tpr: Mean TPR at each FPR point 13 std_tpr: Standard deviation of TPR (for shaded band) 14 auc_mean: Mean AUC across bootstrap samples 15 auc_std: Standard deviation of AUC 16 """ 17 rng = np.random.default_rng(seed) 18 y_true, y_prob = np.asarray(y_true), np.asarray(y_prob) 19 n = len(y_true) 20 mean_fpr = np.linspace(0, 1, 100) 21 tprs, aucs = [], [] 22 23 for _ in range(n_boot): 24 idx = rng.integers(0, n, n) 25 if len(np.unique(y_true[idx])) < 2: 26 continue 27 fpr, tpr, _ = roc_curve(y_true[idx], y_prob[idx]) 28 tpr_interp = np.interp(mean_fpr, fpr, tpr) 29 tpr_interp[0] = 0.0 30 tprs.append(tpr_interp) 31 aucs.append(auc(mean_fpr, tpr_interp)) 32 33 tprs = np.array(tprs) 34 return mean_fpr, tprs.mean(axis=0), tprs.std(axis=0), np.mean(aucs), np.std(aucs) 35 36def plot_roc_with_bands(ax, mean_fpr, mean_tpr, std_tpr, auc_val, auc_std, color, label): 37 """ 38 Plot ROC curve with MANDATORY shaded confidence band. 39 40 The shaded region represents ±1 std of TPR at each FPR point, 41 showing the uncertainty from bootstrap resampling. 42 """ 43 # Plot the mean ROC curve 44 ax.plot(mean_fpr, mean_tpr, color=color, lw=2, 45 label=f'{label} (AUC={auc_val:.2f}±{auc_std:.2f})') 46 47 # MANDATORY: Shaded confidence band between upper and lower bounds 48 lower_bound = np.maximum(mean_tpr - std_tpr, 0) # Clamp to 0 minimum 49 upper_bound = np.minimum(mean_tpr + std_tpr, 1) # Clamp to 1 maximum 50 51 ax.fill_between( 52 mean_fpr, # X values (FPR points) 53 lower_bound, # Lower edge of shaded region 54 upper_bound, # Upper edge of shaded region 55 color=color, 56 alpha=0.2, # Semi-transparent shading 57 edgecolor='none' # No edge line on the shaded region 58 ) 59 60# COMPLETE USAGE EXAMPLE 61fig, ax = plt.subplots(figsize=(7, 6)) 62 63# Example: Plot ROC for multiple models 64models_data = [ 65 ('Model A', y_true_a, y_prob_a, '#6C92ED'), # Blue 66 ('Model B', y_true_b, y_prob_b, '#ECA0C0'), # Pink 67 ('Model C', y_true_c, y_prob_c, '#F8B274'), # Orange 68] 69 70for label, y_true, y_prob, color in models_data: 71 # Compute bootstrap statistics 72 mean_fpr, mean_tpr, std_tpr, auc_val, auc_std = bootstrap_roc_stats(y_true, y_prob) 73 # Plot with MANDATORY shaded band 74 plot_roc_with_bands(ax, mean_fpr, mean_tpr, std_tpr, auc_val, auc_std, color, label) 75 76# Random classifier baseline (diagonal) 77ax.plot([0, 1], [0, 1], 'k--', lw=1, label='Random (AUC=0.50)') 78 79ax.set_xlabel('False Positive Rate', fontsize=9) 80ax.set_ylabel('True Positive Rate', fontsize=9) 81ax.set_title('ROC Curves with Confidence Bands', fontsize=10, fontweight='bold') 82ax.tick_params(axis='both', labelsize=8) 83ax.legend(loc='lower right', fontsize=8) 84ax.set_xlim([0, 1]) 85ax.set_ylim([0, 1]) 86ax.grid(alpha=0.3) 87plt.tight_layout() 88 89# Save both formats 90plt.savefig('roc_curves.pdf', bbox_inches='tight') 91plt.savefig('roc_curves.png', dpi=600, bbox_inches='tight') 92plt.close()

Visual representation of shaded band:

TPR
 1 ┤                    ╭───────── Upper bound (mean + std)
   │                 ╭──┤░░░░░░░░░
   │              ╭──┤░░░░░░░░░░░│ ← Shaded region shows uncertainty
   │           ╭──┤░░░░░░░░░░░░░│
   │        ╭──┤░░░░░░░░░░░░░░░─╯
   │     ╭──┤░░░░░░░░░░░░░░░╯     ← Mean ROC curve (solid line)
   │  ╭──┤░░░░░░░░░░░░░╯
   │╭─┤░░░░░░░░░░░░╯               Lower bound (mean - std)
 0 ┼──────────────────────────────
   0                            1  FPR

3. Confusion Matrix with Percentages

CRITICAL Sizing Rules:

  • Use square figure size (SQUARE_SIZE = 1.7")
  • Use aspect='equal' in imshow() to force square cells
  • Margins: left=0.18, right=0.98, top=0.85, bottom=0.18 to maximize plot area
  • DO NOT use set_box_aspect(1) - it constrains plot size and wastes space
python
1import figure_config # FIRST LINE - registers Helvetica 2import numpy as np 3import matplotlib.pyplot as plt 4 5SQUARE_SIZE = 1.7 # Standard square panel size 6 7def plot_confusion_matrix(cm, labels, title, output_path=None): 8 """ 9 Plot confusion matrix with counts and row percentages. 10 Uses square cells that fill the figure properly. 11 """ 12 cm = np.asarray(cm, dtype=float) 13 row_sums = cm.sum(axis=1, keepdims=True) 14 row_sums[row_sums == 0] = 1.0 15 row_pct = cm / row_sums 16 17 # Square figure with tight margins to maximize plot area 18 fig, ax = plt.subplots(figsize=(SQUARE_SIZE, SQUARE_SIZE)) 19 fig.subplots_adjust(left=0.18, right=0.98, top=0.85, bottom=0.18) 20 21 # CRITICAL: Use aspect='equal' for square cells (NOT set_box_aspect) 22 im = ax.imshow(cm, cmap='Blues', aspect='equal') 23 24 ax.set_title(title, fontsize=10, fontweight='bold') 25 ax.set_xticks(np.arange(len(labels))) 26 ax.set_yticks(np.arange(len(labels))) 27 ax.set_xticklabels(labels, fontsize=8) 28 ax.set_yticklabels(labels, fontsize=8) 29 ax.set_xlabel('Predicted', fontsize=9) 30 ax.set_ylabel('Actual', fontsize=9) 31 32 max_val = cm.max() if cm.size else 0 33 for i in range(cm.shape[0]): 34 for j in range(cm.shape[1]): 35 count = int(cm[i, j]) 36 pct = row_pct[i, j] * 100 37 color = 'white' if max_val > 0 and cm[i, j] > max_val / 2 else 'black' 38 ax.text(j, i, f"{count}\n{pct:.1f}%", 39 ha='center', va='center', color=color, fontsize=9) 40 41 if output_path: 42 plt.savefig(output_path, dpi=600, bbox_inches='tight') 43 plt.close() 44 return fig, ax, im

4. Per-Drug Scatter Plot

python
1fig, ax = plt.subplots(figsize=(16, 6)) 2x_pos = np.arange(len(drugs)) 3 4# Plot each model with offset 5for i, (model, probs, color, marker) in enumerate(model_data): 6 offset = (i - 1) * 0.15 7 mask = true_labels.astype(bool) 8 colors_arr = np.where(mask, color, 'lightgray') 9 ax.scatter(x_pos + offset, probs, s=100, c=colors_arr, 10 marker=marker, edgecolor='black', linewidth=1.5, 11 zorder=3, label=model) 12 13ax.axhline(0.5, color='red', linestyle='--', linewidth=2, alpha=0.7) 14ax.set_ylabel('Probability', fontsize=9) 15ax.set_xlabel('Drug', fontsize=9) 16ax.set_xticks(x_pos) 17ax.set_xticklabels(drugs, rotation=45, ha='right', fontsize=8) 18ax.tick_params(axis='both', labelsize=8) 19ax.set_ylim(-0.05, 1.15) 20ax.grid(axis='y', alpha=0.3) 21ax.legend(loc='upper right')

5. Heatmap (Red-White-Blue Diverging)

CRITICAL Heatmap Rules:

  1. Square cells - Always use square=True, never rectangles
  2. No borders/gaps - Always use linewidths=0 (no white space between cells)
  3. Axis orientation - X-axis = Time (hours), Y-axis = Concentration (mM)
  4. Data matrix - Rows = concentrations, Columns = time points
  5. Clean labels - Remove duplicate decimal suffixes (8.1 → 8), keep meaningful ones (0.1 stays 0.1)
python
1import figure_config # FIRST LINE - registers Helvetica 2import numpy as np 3import matplotlib.pyplot as plt 4import seaborn as sns 5import pandas as pd 6import re 7 8def clean_concentration_labels(labels): 9 """ 10 Clean concentration labels by removing duplicate decimal suffixes. 11 12 Examples: 13 '8.1' → '8' (removes .1 suffix that's just numbering) 14 '8.2' → '8' (removes .2 suffix) 15 '0.1' → '0.1' (keeps meaningful decimal - it's the actual value) 16 '0.1.1' → '0.1' (removes duplicate suffix from 0.1) 17 """ 18 cleaned = [] 19 for label in labels: 20 label_str = str(label) 21 # Pattern: number.number.number (like 0.1.1) → keep first two parts 22 if re.match(r'^\d+\.\d+\.\d+$', label_str): 23 parts = label_str.split('.') 24 cleaned.append(f"{parts[0]}.{parts[1]}") 25 # Pattern: integer.single_digit at end (like 8.1, 8.2) → remove suffix 26 elif re.match(r'^(\d+)\.[1-9]$', label_str): 27 cleaned.append(re.match(r'^(\d+)\.[1-9]$', label_str).group(1)) 28 else: 29 cleaned.append(label_str) 30 return cleaned 31 32# Example: Drug response heatmap 33# CRITICAL: Rows = concentrations (Y-axis), Columns = time (X-axis) 34n_concentrations = 8 35n_timepoints = 40 36 37data_matrix = pd.DataFrame( 38 np.random.randn(n_concentrations, n_timepoints), 39 index=[f'{c}' for c in [0, 0.1, 1, 2, 4, 8, 16, 32]], # Concentration labels 40 columns=[f'{t}' for t in range(0, n_timepoints * 2, 2)] # Time labels (hours) 41) 42 43# Clean concentration labels (Y-axis) 44y_labels = clean_concentration_labels(data_matrix.index.tolist()) 45x_labels = data_matrix.columns.tolist() # Time labels (X-axis) 46 47# Setup custom colormap with project colors 48from matplotlib.colors import LinearSegmentedColormap 49 50# Project heatmap colors (MANDATORY) 51HEATMAP_BLUE = '#123BFF' # Low values 52HEATMAP_RED = '#FF2908' # High values 53 54# Create custom diverging colormap: Blue -> White -> Red 55cmap = LinearSegmentedColormap.from_list( 56 'cardiac_rodeo', 57 [HEATMAP_BLUE, 'white', HEATMAP_RED] 58) 59cmap.set_bad('white') # NaN values display as white 60 61# MANDATORY Figure size for heatmaps (1:2 ratio) 62HEATMAP_HEIGHT = 1.7 # inches 63HEATMAP_WIDTH = 3.4 # inches (2x height) 64fig, ax = plt.subplots(figsize=(HEATMAP_WIDTH, HEATMAP_HEIGHT)) 65 66# Create heatmap with SQUARE cells and NO borders/gaps 67sns.heatmap( 68 data_matrix, 69 annot=False, # No cell annotations 70 cmap=cmap, 71 cbar_kws={'label': 'Response', 'shrink': 0.8}, 72 xticklabels=x_labels, # Time labels (X-axis) 73 yticklabels=y_labels, # Concentration labels (Y-axis) 74 square=True, # CRITICAL: Square cells, not rectangles 75 mask=False, 76 linewidths=0 # CRITICAL: No borders/gaps between cells 77) 78 79# Customize tick labels - FIXED SIZES 80ax.set_xticklabels(x_labels, rotation=0, ha='center', fontsize=8) 81ax.set_yticklabels(y_labels, fontsize=8, rotation=0) 82 83# CRITICAL: X = Time, Y = Concentration - FIXED FONT SIZES 84ax.set_xlabel('Time (Hours)', fontsize=9) 85ax.set_ylabel('Concentration (mM)', fontsize=9) 86ax.set_title('Drug Response Heatmap', fontsize=10, fontweight='bold') 87 88plt.tight_layout()

Heatmap Axis Orientation (MANDATORY):

         X-axis: Time (Hours) →
        ┌─────────────────────────┐
    Y   │  0   2   4   6  ...  78 │
    a   ├─────────────────────────┤
    x   │ 32 │ ■ │ ■ │ ■ │     │ ■ │
    i   │ 16 │ ■ │ ■ │ ■ │     │ ■ │
    s   │  8 │ ■ │ ■ │ ■ │     │ ■ │
    :   │  4 │ ■ │ ■ │ ■ │     │ ■ │
    C   │  2 │ ■ │ ■ │ ■ │     │ ■ │
    o   │  1 │ ■ │ ■ │ ■ │     │ ■ │
    n   │0.1 │ ■ │ ■ │ ■ │     │ ■ │
    c   │  0 │ ■ │ ■ │ ■ │     │ ■ │
        └─────────────────────────┘

Heatmap Colormap Reference:

Data TypeColormapSetupNotes
Drug response (DEFAULT)Custom cardiac_rodeoSee code above#123BFF (blue) → White → #FF2908 (red)
CorrelationCustom or RdBu_rcenter=0, vmin=-1, vmax=1Symmetric around zero
Performance (R², accuracy)RdYlGncenter=0Red (bad) → Yellow → Green (good)
Confusion MatrixBluesN/AOne-sided, counts only
SHAP valuescoolwarmN/ABlue (neg) → White → Red (pos)

MANDATORY Heatmap Colors:

python
1HEATMAP_BLUE = '#123BFF' # For low values 2HEATMAP_RED = '#FF2908' # For high values

6. Threshold Analysis Scatter Plot (Horizontal)

python
1import numpy as np 2import matplotlib.pyplot as plt 3import pandas as pd 4 5# Example data 6drugs = ['Amiodarone', 'Bortezomib', 'Chlorpromazine', 'Doxorubicin', 'Erlotinib', 7 'Ibuprofen', 'Nifedipine', 'Sotalol', 'Vincristine', 'Vioxx'] 8predictions = np.array([85, 72, 45, 90, 30, 25, 55, 78, 40, 65]) # Probability % 9actual_positive = np.array([True, True, False, True, False, False, True, True, False, True]) 10 11# Sort drugs: by classification (positive first), then alphabetically 12df = pd.DataFrame({'Drug': drugs, 'Pred': predictions, 'Positive': actual_positive}) 13df = df.sort_values(['Positive', 'Drug'], ascending=[False, True]) 14drugs_sorted = df['Drug'].tolist() 15preds_sorted = df['Pred'].values 16status_sorted = df['Positive'].values 17 18# Colors: Green = positive, Red = negative 19pos_color = '#2ca02c' # Green 20neg_color = '#d62728' # Red 21threshold_color = '#1f77b4' # Blue for threshold line 22 23# Create horizontal scatter plot (drugs on Y-axis) 24fig, ax = plt.subplots(figsize=(10, max(6, len(drugs) * 0.4))) 25 26positions = np.arange(len(drugs_sorted)) 27point_colors = [pos_color if s else neg_color for s in status_sorted] 28 29# Scatter: X = probability, Y = drug position 30ax.scatter(preds_sorted, positions, c=point_colors, s=100, 31 edgecolors='black', linewidth=0.5, zorder=3) 32 33# Compute threshold: max(negative samples) + margin, rounded to nearest 5 34margin_pp = 2.0 35neg_preds = preds_sorted[~status_sorted] 36if len(neg_preds) > 0: 37 threshold = float(np.max(neg_preds)) + margin_pp 38else: 39 threshold = 50.0 40threshold = float(5 * np.ceil(threshold / 5.0)) # Round up to nearest 5 41 42# Vertical threshold line 43ax.axvline(threshold, color=threshold_color, linestyle='--', linewidth=2, zorder=2) 44ax.text(threshold + 1, len(drugs_sorted) - 0.5, f'{threshold:.0f}%', 45 color=threshold_color, fontsize=10, fontweight='bold', va='top') 46 47# Axis setup with padding for 0% and 100% visibility 48ax.set_xlim(-5, 105) # Padding so edge points are visible 49ax.set_ylim(-0.5, len(drugs_sorted) - 0.5) 50 51ax.set_yticks(positions) 52ax.set_yticklabels(drugs_sorted, fontsize=8) 53ax.set_xlabel('Predicted Probability (%)', fontsize=9) 54ax.set_title('Threshold Analysis: Arrhythmia Prediction', fontsize=10, fontweight='bold') 55ax.tick_params(axis='both', labelsize=8) 56 57ax.grid(axis='x', linestyle='--', alpha=0.4) 58ax.invert_yaxis() # First drug at top 59 60# Legend 61from matplotlib.lines import Line2D 62legend_elements = [ 63 Line2D([0], [0], marker='o', color='w', markerfacecolor=pos_color, 64 markersize=10, label='Positive (Actual)'), 65 Line2D([0], [0], marker='o', color='w', markerfacecolor=neg_color, 66 markersize=10, label='Negative (Actual)'), 67 Line2D([0], [0], color=threshold_color, linestyle='--', linewidth=2, label='Threshold') 68] 69ax.legend(handles=legend_elements, loc='lower right', fontsize=8) 70 71plt.tight_layout()

Key Features:

  • Drugs on Y-axis for readable labels
  • X-axis: 0-100% with -5, 105 limits for edge visibility
  • Green = positive class, Red = negative class
  • Sorted by classification (positive first) then alphabetically
  • Threshold = max(negative) + margin, rounded to nearest 5%
  • Vertical threshold line with axvline

7. 3D Surface Plot (PK-PD)

python
1from mpl_toolkits.mplot3d import Axes3D 2import numpy as np 3 4fig = plt.figure(figsize=(10, 8)) 5ax = fig.add_subplot(111, projection='3d') 6 7# Create meshgrid 8time = np.linspace(0, 96, 50) 9dose_ratio = np.linspace(0, 2, 50) 10T, Dr = np.meshgrid(time, dose_ratio) 11Response = compute_response(T, Dr, params) # Your function 12 13surf = ax.plot_surface(T, Dr, Response, cmap='viridis', 14 edgecolor='none', alpha=0.9) 15ax.set_xlabel('Time (hours)', fontsize=9) 16ax.set_ylabel('Dose Ratio (C0/Cmax)', fontsize=9) 17ax.set_zlabel('Response', fontsize=9) 18ax.set_title('Drug Response Surface', fontsize=10, fontweight='bold') 19ax.tick_params(axis='both', labelsize=8) 20ax.view_init(elev=25, azim=-158) 21fig.colorbar(surf, shrink=0.5, aspect=10)

8. SHAP Aligned Pairs Plot

python
1import figure_config # FIRST LINE - registers Helvetica 2import numpy as np 3import matplotlib.pyplot as plt 4import pandas as pd 5from matplotlib.lines import Line2D 6 7# Load SHAP values (example: arrhythmia) 8shap_df = pd.read_csv('Output/SHAP_Data/shap_arrhythmia_values.csv') 9drug_class = pd.read_csv('Cleaned_Data/drug_classification.csv') 10 11# Create label map: drug -> actual class (True/False) 12label_map = {} 13for _, row in drug_class.iterrows(): 14 drug = row['Drug'] 15 val = row['Arrhythmia'] # or 'heart_damage', 'Concern', etc. 16 label_map[drug] = str(val).lower() == 'true' if isinstance(val, str) else bool(val) 17 18# Colors: Blue for positive class, Grey for negative class 19COLORS = { 20 'pass': '#6C92ED', # Blue - positive class 21 'fail': '#888888', # Grey - negative class 22} 23 24# Get feature columns and select top 5 by |mean SHAP| 25feature_cols = [col for col in shap_df.columns if col != 'Drug'] 26mean_shap = shap_df[feature_cols].abs().mean() 27top_5_features = mean_shap.nlargest(5).index.tolist() 28 29# Figure size: 2x width for SHAP plots 30SQUARE_SIZE = 1.7 31fig, ax = plt.subplots(figsize=(SQUARE_SIZE * 2, SQUARE_SIZE)) 32fig.subplots_adjust(left=0.25, right=0.85, top=0.88, bottom=0.12) 33 34n_features = len(top_5_features) 35n_drugs = len(shap_df) 36feature_spacing = 1.0 37drug_offset = 0.03 38 39y_positions = [] 40y_labels = [] 41 42for feat_idx, feature in enumerate(reversed(top_5_features)): 43 base_y = feat_idx * feature_spacing 44 y_positions.append(base_y) 45 y_labels.append(feature) 46 47 # Sort drugs by SHAP value for this feature 48 sorted_idx = np.argsort(shap_df[feature].values) 49 50 for i, idx in enumerate(sorted_idx): 51 drug = shap_df['Drug'].iloc[idx] 52 val = shap_df[feature].iloc[idx] 53 y = base_y + (i - n_drugs/2) * drug_offset 54 55 # Color by actual class 56 is_positive = label_map.get(drug, False) 57 color = COLORS['pass'] if is_positive else COLORS['fail'] 58 59 # Draw line from 0 to SHAP value 60 ax.hlines(y, 0, val, colors=color, linewidth=1.2, alpha=0.8) 61 62ax.axvline(x=0, color='black', linewidth=0.8) 63ax.set_yticks(y_positions) 64ax.set_yticklabels(y_labels, fontsize=8) 65ax.set_xlabel('SHAP Value', fontsize=9) 66ax.set_title('SHAP Feature Importance', fontsize=10, fontweight='bold') 67ax.spines['top'].set_visible(False) 68ax.spines['right'].set_visible(False) 69ax.grid(axis='x', alpha=0.3) 70ax.tick_params(axis='x', labelsize=8) 71 72legend_elements = [ 73 Line2D([0], [0], color=COLORS['pass'], linewidth=2, label='Pos'), 74 Line2D([0], [0], color=COLORS['fail'], linewidth=2, label='Neg'), 75] 76ax.legend(handles=legend_elements, fontsize=8, loc='upper right') 77 78plt.savefig('Output/SHAP_Data/shap_aligned_target.png', dpi=600) 79plt.close()

Key Features:

  • Shows top 5 features by |mean SHAP|
  • For each feature, all drugs are plotted as horizontal lines from 0 to their SHAP value
  • Lines sorted by SHAP value within each feature group
  • Color indicates actual class (not SHAP sign): blue=positive, grey=negative
  • Simple, clean visualization without complex pairing logic
  • Compact size (3.4" × 1.7") for PowerPoint integration

Data Export Pattern (for Excel recreation):

python
1excel_data = [] 2for feature, y_pos, shap_val, drug, actual in plot_records: 3 excel_data.append({ 4 'Feature': feature, 5 'Y_Position': y_pos, 6 'SHAP_Value': shap_val, 7 'Drug': drug, 8 'Actual_Class': 'Positive' if actual else 'Negative' 9 }) 10pd.DataFrame(excel_data).to_excel('shap_aligned_pairs_data.xlsx', index=False)

9. Accuracy vs AUC Scatter Plot (Equation/Model Comparison)

NOT a bar chart! This is a scatter plot comparing equations across ML models.

  • X-axis = Accuracy
  • Y-axis = AUC
  • Each point = one (equation, model) combination
  • Color/marker by equation, grouped by model
python
1import figure_config # FIRST LINE - registers Helvetica 2import numpy as np 3import matplotlib.pyplot as plt 4import pandas as pd 5 6# Example data: 3 equations × 3 ML models = 9 points 7equations = ['dual_exponential', 'modified_hill_hormesis', 'pkpd_elimination'] 8models = ['XGBoost', 'SVM_RBF', 'RandomForest'] 9 10# Simulated results (replace with actual loocv_results.csv data) 11data = { 12 ('dual_exponential', 'XGBoost'): {'Accuracy': 0.72, 'AUC': 0.78}, 13 ('dual_exponential', 'SVM_RBF'): {'Accuracy': 0.68, 'AUC': 0.72}, 14 ('dual_exponential', 'RandomForest'): {'Accuracy': 0.70, 'AUC': 0.75}, 15 ('modified_hill_hormesis', 'XGBoost'): {'Accuracy': 0.76, 'AUC': 0.82}, 16 ('modified_hill_hormesis', 'SVM_RBF'): {'Accuracy': 0.74, 'AUC': 0.80}, 17 ('modified_hill_hormesis', 'RandomForest'): {'Accuracy': 0.73, 'AUC': 0.78}, 18 ('pkpd_elimination', 'XGBoost'): {'Accuracy': 0.80, 'AUC': 0.86}, 19 ('pkpd_elimination', 'SVM_RBF'): {'Accuracy': 0.78, 'AUC': 0.84}, 20 ('pkpd_elimination', 'RandomForest'): {'Accuracy': 0.77, 'AUC': 0.82}, 21} 22 23# Colors by equation 24equation_colors = { 25 'dual_exponential': '#e74c3c', # Red 26 'modified_hill_hormesis': '#3498db', # Blue 27 'pkpd_elimination': '#2ecc71', # Green 28} 29 30# Markers by model 31model_markers = { 32 'XGBoost': 'o', # Circle 33 'SVM_RBF': 's', # Square 34 'RandomForest': '^', # Triangle 35} 36 37# Use 1.7" square (same as bar charts) 38SQUARE_SIZE = 1.7 39fig, ax = plt.subplots(figsize=(SQUARE_SIZE, SQUARE_SIZE)) 40 41# Plot each point 42for (eq, model), metrics in data.items(): 43 ax.scatter( 44 metrics['Accuracy'], metrics['AUC'], 45 c=equation_colors[eq], 46 marker=model_markers[model], 47 s=40, # Appropriate markers for 1.7" square 48 edgecolors='black', 49 linewidth=1, 50 label=f'{eq} / {model}' if model == models[0] else '', # Only label once per equation 51 zorder=3 52 ) 53 54# Reference lines 55ax.axhline(0.5, color='gray', linestyle='--', alpha=0.5, linewidth=1) # AUC baseline 56ax.axvline(0.5, color='gray', linestyle='--', alpha=0.5, linewidth=1) # Accuracy baseline 57 58# Axis settings - FIXED FONT SIZES 59ax.set_xlabel('Accuracy', fontsize=9) 60ax.set_ylabel('AUC', fontsize=9) 61ax.set_title('Equation Comparison: Accuracy vs AUC', fontsize=10, fontweight='bold') 62ax.tick_params(axis='both', labelsize=8) 63ax.set_xlim(0.4, 1.0) 64ax.set_ylim(0.4, 1.0) 65ax.set_aspect('equal') # Square plot for equal scaling 66ax.grid(True, alpha=0.3) 67 68# Custom legend 69from matplotlib.lines import Line2D 70from matplotlib.patches import Patch 71 72# Equation colors legend 73eq_legend = [Patch(facecolor=c, edgecolor='black', label=eq.replace('_', ' ').title()) 74 for eq, c in equation_colors.items()] 75 76# Model markers legend 77model_legend = [Line2D([0], [0], marker=m, color='w', markerfacecolor='gray', 78 markersize=10, label=model, markeredgecolor='black') 79 for model, m in model_markers.items()] 80 81legend1 = ax.legend(handles=eq_legend, title='Equation', loc='upper left', fontsize=8) 82ax.add_artist(legend1) 83ax.legend(handles=model_legend, title='Model', loc='lower right', fontsize=8) 84 85plt.tight_layout()

Key Features:

  • X = Accuracy, Y = AUC (NOT bar chart)
  • Each point represents one (equation, model) combination
  • Color distinguishes equations
  • Marker shape distinguishes ML models
  • Square plot with equal scaling for fair comparison
  • Reference lines at 0.5 for both axes (random baseline)

Space-Saving Rules for Multi-Panel Figures

CRITICAL: Shared Axes for Same-Scale Graphs

When multiple graphs share the same axis scale, place them side-by-side and show only ONE axis instead of repeating.

This eliminates wasted space from duplicate axis labels and tick marks.

python
1import figure_config 2import matplotlib.pyplot as plt 3import matplotlib.gridspec as gridspec 4 5# WRONG: Each subplot has its own Y-axis (wastes space) 6fig, axes = plt.subplots(1, 3, figsize=(9, 3)) 7for ax in axes: 8 ax.set_ylabel('Response') # Repeated 3 times! 9 10# CORRECT: Share Y-axis, only label the leftmost 11fig, axes = plt.subplots(1, 3, figsize=(7, 3), sharey=True) 12axes[0].set_ylabel('Response') # Only once on the left 13for ax in axes[1:]: 14 ax.tick_params(labelleft=False) # Hide Y tick labels on others 15 16# BEST: Use GridSpec for tight control and minimal gaps 17fig = plt.figure(figsize=(5.19, 1.44)) # 3 heatmaps side-by-side 18gs = gridspec.GridSpec(1, 3, wspace=0.05) # Minimal horizontal gap 19 20axes = [fig.add_subplot(gs[0, i]) for i in range(3)] 21axes[0].set_ylabel('Concentration (mM)') # Only on leftmost 22for ax in axes[1:]: 23 ax.set_yticklabels([]) # No Y labels on middle/right panels

Visual comparison:

WRONG (wasted space):                    CORRECT (shared axis):
┌─────────┐ ┌─────────┐ ┌─────────┐     ┌─────────────────────────┐
│  Y      │ │  Y      │ │  Y      │     │  Y                      │
│  │ ■■■  │ │  │ ■■■  │ │  │ ■■■  │     │  │ ■■■ │ ■■■ │ ■■■     │
│  │ ■■■  │ │  │ ■■■  │ │  │ ■■■  │     │  │ ■■■ │ ■■■ │ ■■■     │
│  └──X   │ │  └──X   │ │  └──X   │     │  └──────────────X       │
└─────────┘ └─────────┘ └─────────┘     └─────────────────────────┘
 Drug A      Drug B      Drug C           Drug A  Drug B  Drug C

CRITICAL: Shared Legends and Colorbars

When multiple graphs in the same panel use the same color scale or legend, show only ONE colorbar/legend for all of them.

python
1import matplotlib.pyplot as plt 2import matplotlib.gridspec as gridspec 3import numpy as np 4 5# Example: 3 heatmaps with one shared colorbar 6fig = plt.figure(figsize=(5.5, 1.44)) 7 8# Create GridSpec: 3 heatmaps + 1 narrow colorbar column 9gs = gridspec.GridSpec(1, 4, width_ratios=[1, 1, 1, 0.05], wspace=0.1) 10 11# Determine shared vmin/vmax across all data 12all_data = [data1, data2, data3] 13vmin = min(d.min() for d in all_data) 14vmax = max(d.max() for d in all_data) 15 16# Plot heatmaps with same color scale 17for i, data in enumerate(all_data): 18 ax = fig.add_subplot(gs[0, i]) 19 im = ax.imshow(data, vmin=vmin, vmax=vmax, cmap='RdBu_r') 20 if i == 0: 21 ax.set_ylabel('Concentration') 22 else: 23 ax.set_yticklabels([]) # No Y labels on non-leftmost 24 25# Single colorbar for all three 26cbar_ax = fig.add_subplot(gs[0, 3]) 27fig.colorbar(im, cax=cbar_ax, label='Response')

Rules summary:

ElementWhen to ShareHow to Implement
Y-axisSame units & rangesharey=True, label only leftmost
X-axisSame units & rangesharex=True, label only bottom
ColorbarSame color scaleSingle colorbar() with cax=
LegendSame categoriesSingle fig.legend() outside subplots

Applying to Heatmaps

For multiple drug heatmaps (e.g., Contractility responses for different drugs):

python
1import figure_config 2import matplotlib.pyplot as plt 3import matplotlib.gridspec as gridspec 4import seaborn as sns 5 6# Standard heatmap dimensions (1:2 ratio) 7HEATMAP_HEIGHT = 1.7 # inches 8HEATMAP_WIDTH = 3.4 # inches (2x height) 9 10n_drugs = 3 11fig = plt.figure(figsize=(HEATMAP_WIDTH * n_drugs + 0.3, HEATMAP_HEIGHT)) 12 13# GridSpec: n heatmaps + colorbar 14gs = gridspec.GridSpec(1, n_drugs + 1, 15 width_ratios=[1]*n_drugs + [0.05], 16 wspace=0.05) 17 18# Shared color scale 19vmin, vmax = 0, 100 # Or compute from data 20 21# Create custom colormap with project colors 22from matplotlib.colors import LinearSegmentedColormap 23HEATMAP_BLUE = '#123BFF' 24HEATMAP_RED = '#FF2908' 25cmap = LinearSegmentedColormap.from_list('cardiac_rodeo', [HEATMAP_BLUE, 'white', HEATMAP_RED]) 26 27for i, drug in enumerate(drugs): 28 ax = fig.add_subplot(gs[0, i]) 29 sns.heatmap(data[drug], ax=ax, vmin=vmin, vmax=vmax, 30 cmap=cmap, cbar=False, square=True) 31 ax.set_title(drug, fontsize=10) 32 33 if i == 0: 34 ax.set_ylabel('Concentration (mM)', fontsize=10) 35 else: 36 ax.set_ylabel('') 37 ax.set_yticklabels([]) 38 39 ax.set_xlabel('Time (h)', fontsize=10) 40 41# Single shared colorbar 42cbar_ax = fig.add_subplot(gs[0, -1]) 43sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin, vmax)) 44fig.colorbar(sm, cax=cbar_ax, label='Response') 45 46plt.tight_layout()

Style Guidelines

Font Settings (MANDATORY: Use Helvetica)

ALWAYS use Helvetica as the default font for all figures. No fallbacks.

CRITICAL: Add import figure_config as the FIRST line of any plotting code.

Font Sizes (MANDATORY: Fixed Sizes for ALL Text Elements)

CRITICAL: Use these EXACT font sizes for ALL text in ALL figures regardless of figure dimensions. This applies to EVERYTHING:

  • Titles
  • Axis labels (xlabel, ylabel)
  • Tick labels
  • Legend text
  • Annotations
  • Panel labels (a), (b), (c)
  • Colorbar labels

Font sizes must be UNIFORM across all graphs - never scale fonts based on figure size.

How Font Sizes Work in Matplotlib

Font sizes are in POINTS (absolute units), where 1 point = 1/72 inch.

This means:

  • A 10pt title is ALWAYS 10/72 = 0.139 inches tall when printed/saved
  • A 9pt axis label is ALWAYS 9/72 = 0.125 inches tall
  • An 8pt tick label is ALWAYS 8/72 = 0.111 inches tall
  • This is true regardless of figsize - fonts don't scale with the figure
  • When you save at 600 DPI, fonts render at their true point size

The result: A 1.7" figure and a 4" figure with the same font sizes will have ALL text (titles, axis labels, tick labels, legends, etc.) at the SAME PHYSICAL SIZE when printed. Text takes up MORE of the small figure's area, but is the same size in inches.

This is the desired behavior for publication consistency. When figures are placed side-by-side or in a grid, all text elements are the same readable size.

python
1# MANDATORY FONT SIZE CONSTANTS - use these exact values everywhere 2FONT_SIZES = { 3 'title': 10, # Figure/panel titles (0.139") 4 'axis_label': 9, # X and Y axis labels (0.125") 5 'tick_label': 8, # Tick mark labels (0.111") 6 'legend': 8, # Legend text 7 'annotation': 8, # Text annotations on plots 8 'panel_label': 10, # Panel labels (a), (b), (c) 9 'colorbar_label': 8, # Colorbar labels 10} 11 12# Apply in every figure: 13ax.set_title('Title', fontsize=10, fontweight='bold') 14ax.set_xlabel('X Label', fontsize=9) 15ax.set_ylabel('Y Label', fontsize=9) 16ax.tick_params(axis='both', labelsize=8) 17ax.legend(fontsize=8)

Why This Ensures Consistency

When you have multiple figures of different sizes:

  • 1.7" square confusion matrix
  • 3.4" wide ROC curve
  • 6" wide per-drug scatter

All will have 10pt titles that are physically the same size. This means:

  • Readers can compare figures without adjusting to different text sizes
  • Printed/PDF figures look professional and consistent
  • PowerPoint slides have uniform text regardless of figure dimensions

DO NOT do this:

python
1# WRONG - scaling font based on figure size 2fontsize = fig.get_figwidth() * 2 # NO! 3ax.set_title('Title', fontsize=14) # NO - not standard 4ax.set_xlabel('Label', fontsize=12) # NO - not standard 5 6# WRONG - trying to make text "fit" smaller figures 7small_fig_fontsize = 6 # NO - will be unreadable and inconsistent

DO this:

python
1# CORRECT - fixed sizes from standard (same for ALL figures) 2ax.set_title('Title', fontsize=10, fontweight='bold') # Always 10pt 3ax.set_xlabel('Label', fontsize=9) # Always 9pt 4ax.tick_params(labelsize=8) # Always 8pt 5 6# This applies to 1.7" figures AND 6" figures - same font sizes

Ensuring Fonts Look Consistent When Viewing

Since fonts are absolute, a 1.7" figure will have relatively larger text (more of the figure area is text). To verify consistency:

  1. Save at consistent DPI (600) - ensures font rendering matches
  2. View figures at 100% zoom - see actual print size
  3. Never resize figures non-proportionally - stretching changes aspect but not fonts
python
1import figure_config # LINE 1 - registers Helvetica from fonts/ folder 2import matplotlib.pyplot as plt 3import numpy as np 4 5# Now plot - Helvetica is already configured 6fig, ax = plt.subplots(figsize=(8, 6)) 7# ... your plotting code

The figure_config.py module (located at project root) automatically:

  • Registers all fonts from the fonts/ directory
  • Sets Helvetica as the default font family
  • Configures standard font sizes and weights

For inline/ad-hoc code without figure_config:

python
1from pathlib import Path 2import matplotlib.font_manager as fm 3import matplotlib.pyplot as plt 4 5# Register Helvetica from fonts/ folder 6font_dir = Path(r'C:\Users\NoahB\Documents\HebrewU Bioengineering\Cardiac_RODEO\fonts') 7for font_file in font_dir.glob('*.ttf'): 8 fm.fontManager.addfont(str(font_file)) 9plt.rcParams['font.family'] = 'sans-serif' 10plt.rcParams['font.sans-serif'] = ['Helvetica']

Always Include

  • Clear axis labels with units where applicable
  • Title with fontweight='bold'
  • Grid with alpha=0.3 for readability
  • Legend in appropriate location
  • tight_layout() before saving

Directory Structure

Output/
├── MoLFormer_Comparison/
│   ├── figures/           # PDF versions
│   │   ├── accuracy_bar.pdf
│   │   ├── roc_curves_all.pdf
│   │   └── confusion_matrices_all.pdf
│   ├── Accuracy_Bar.png   # PNG versions (capitalized)
│   └── ROC_Curves_All.png
├── ADMET_Comparison/
│   └── figures/
└── LaTeX_Reports/
    └── figures/           # Symlink or copy PDFs here

Checklist Before Saving

  • import figure_config is the first line (registers Helvetica)
  • Font sizes are UNIFORM (title=10, axis_label=9, tick_label=8, legend=8)
  • Title is descriptive and bold
  • Axis labels include units if applicable
  • Legend doesn't obscure data
  • Color scheme is consistent with project
  • Saved as both PDF and PNG
  • tight_layout() called
  • Figure closed with plt.close()
  • ROC curves: Shaded confidence band included (use fill_between with bootstrap ±1 std)
  • Heatmaps: No borders/gaps between cells (use linewidths=0, square=True)

PowerPoint Figure System

This section defines the conventions for creating figures intended for PowerPoint presentations and publication, with full traceability and consistency.

CRITICAL: Automatic PowerPoint Insertion

When generating or modifying a tracked figure, you MUST automatically insert it into the target PowerPoint file.

This is NOT optional. After saving the PNG/PDF and Excel files, the figure must be placed into the PowerPoint at its designated location with proper sizing and labels.

Target file: Output/PowerPoint_Figures/Cardiac_RODEO_Tracked.pptx

Always LINK images to PowerPoint instead of embedding them. This enables automatic updates when figures are regenerated.

Why link instead of embed:

  • When you regenerate a figure with Python, the PowerPoint updates automatically
  • No need to manually delete and re-insert figures
  • Keeps file size smaller (images stored once on disk)
  • Enables rapid iteration: edit Python → run script → switch to PowerPoint → see updated figure

Workflow:

  1. Python script saves figure to a fixed path (e.g., Output/PowerPoint_Figures/Fig_2/Fig_2_a_ROC.png)
  2. PowerPoint links to that exact path
  3. When you re-run the script, the PNG is overwritten
  4. PowerPoint automatically shows the new version (may need to refresh/reopen)

python-pptx: Link instead of embed:

python
1from pptx import Presentation 2from pptx.util import Inches 3from pptx.oxml.ns import qn 4from pptx.oxml import parse_xml 5from lxml import etree 6 7def add_linked_picture(slide, image_path, left, top, width=None, height=None): 8 """ 9 Add a picture to a slide as a LINKED image (not embedded). 10 The image will update automatically when the source file changes. 11 12 Args: 13 slide: pptx slide object 14 image_path: Absolute path to the image file 15 left, top: Position in Inches 16 width, height: Size in Inches (optional, preserves aspect ratio if only one given) 17 """ 18 from pptx.util import Emu 19 from PIL import Image 20 import os 21 22 # Get image dimensions for aspect ratio 23 with Image.open(image_path) as img: 24 img_width, img_height = img.size 25 aspect = img_width / img_height 26 27 # Calculate size 28 if width and not height: 29 height = width / aspect 30 elif height and not width: 31 width = height * aspect 32 elif not width and not height: 33 width = Inches(4) # default 34 height = width / aspect 35 36 # Add picture shape (this embeds, but we'll convert to link) 37 picture = slide.shapes.add_picture( 38 image_path, 39 Inches(left), Inches(top), 40 Inches(width), Inches(height) 41 ) 42 43 # To make it a TRUE linked picture, you need to manually edit the .pptx 44 # after creation, or use the method below to store the path for reference 45 46 # Store the source path in the shape's name for tracking 47 picture.name = f"LINKED:{image_path}" 48 49 return picture 50 51# For true linking, use Insert > Picture > Link to File in PowerPoint GUI 52# python-pptx doesn't natively support linked pictures, but you can: 53# 1. Use the GUI to insert linked pictures initially 54# 2. Use python-pptx only for positioning/sizing 55# 3. Or manually edit the XML (advanced)

Recommended workflow for linked images:

  1. Initial setup (GUI): Insert pictures using PowerPoint's "Insert → Pictures → This Device" then click the dropdown arrow on "Insert" and select "Link to File"
  2. Subsequent updates: Just re-run your Python scripts - the linked images update automatically
  3. Tracking: Keep figure_registry.csv updated with exact file paths

PowerPoint GUI steps to link an image:

  1. Insert → Pictures → This Device
  2. Navigate to your figure (e.g., Output/PowerPoint_Figures/Fig_2/Fig_2_a_ROC.png)
  3. Click the dropdown arrow next to "Insert" button
  4. Select "Link to File" (not "Insert")
  5. The image is now linked - it will update when the source file changes

Slide Dimensions (MANDATORY)

python
1SLIDE_WIDTH = 7.09 # inches 2SLIDE_HEIGHT = 8.47 # inches (portrait orientation) 3MARGIN = 0.5 # inches from edges 4GAP = 0.15 # inches between figures

Figure Hierarchy

Structure:

  • Figure number (1, 2, 3...) = Main figure, gets its own slide
  • Panel letter (a, b, c...) = Subfigure boxes within that figure
  • Images = One or more graphics inside each panel box

CRITICAL: Horizontal-First Panel Filling Panels fill horizontally first (left to right), then wrap to next row:

  • Panels a, b fill the first row side-by-side
  • If there's room, panel c goes next to b
  • If not, panel c starts a new row below
  • Never stack panels vertically if horizontal space is available
CORRECT (horizontal-first):           WRONG (vertical stacking):
┌──────────────────────────────┐      ┌──────────────────────────────┐
│  Figure 3                    │      │  Figure 3                    │
├──────────────────────────────┤      ├──────────────────────────────┤
│  ┌──────────┐  ┌──────────┐  │      │  ┌──────────────────────┐    │
│  │ (a) ROC  │  │ (b) CM   │  │      │  │ (a) ROC              │    │
│  └──────────┘  └──────────┘  │      │  └──────────────────────┘    │
│  ┌──────────┐                │      │  ┌──────────────────────┐    │
│  │ (c) SHAP │                │      │  │ (b) Confusion Matrix │    │
│  └──────────┘                │      │  └──────────────────────┘    │
└──────────────────────────────┘      │  ┌──────────────────────┐    │
                                      │  │ (c) SHAP             │    │
Layout: '2x2' with 3 panels           │  └──────────────────────┘    │
(a=top-left, b=top-right, c=btm-left) └──────────────────────────────┘

                                      Layout: '3x1' - DON'T USE unless
                                      panels are very wide

File naming:  Fig_3_a_ROC.png      →  Figure 3, Panel a
              Fig_3_b_SHAP.png     →  Figure 3, Panel b
              Fig_3_c_scatter.png  →  Figure 3, Panel c

Panel Count to Layout Mapping:

PanelsPreferred LayoutPanel Positions
21x2a=left, b=right
32x2a=top-left, b=top-right, c=bottom-left
42x2a=top-left, b=top-right, c=bottom-left, d=bottom-right
5-62x3Fill left-to-right, top-to-bottom

Multi-Figure Layouts

Use these standard layouts for placing subfigure boxes (e.g., Fig 3a, 3b, 3c) on the same slide:

python
1from pptx import Presentation 2from pptx.util import Inches, Pt 3from pptx.enum.text import PP_ALIGN 4from pathlib import Path 5 6# Slide dimensions 7SLIDE_WIDTH = 7.09 8SLIDE_HEIGHT = 8.47 9MARGIN = 0.5 10GAP = 0.15 11 12# Usable area 13USABLE_WIDTH = SLIDE_WIDTH - 2 * MARGIN # 6.09" 14USABLE_HEIGHT = SLIDE_HEIGHT - 2 * MARGIN # 7.47" 15 16def get_layout_positions(layout, title_height=0.6): 17 """ 18 Get positions and sizes for standard multi-figure layouts. 19 20 Args: 21 layout: '1x1', '1x2', '2x1', '2x2', '3x1', '1x3', '2x3', '3x2' 22 title_height: Height reserved for slide title (inches) 23 24 Returns: 25 List of (left, top, width, height) tuples for each panel position 26 """ 27 top_start = MARGIN + title_height 28 available_height = USABLE_HEIGHT - title_height 29 30 layouts = { 31 # Single figure (full width) 32 '1x1': [ 33 (MARGIN, top_start, USABLE_WIDTH, available_height) 34 ], 35 # 2 figures side-by-side (1 row, 2 cols) 36 '1x2': [ 37 (MARGIN, top_start, (USABLE_WIDTH - GAP) / 2, available_height), 38 (MARGIN + (USABLE_WIDTH + GAP) / 2, top_start, (USABLE_WIDTH - GAP) / 2, available_height), 39 ], 40 # 2 figures stacked (2 rows, 1 col) 41 '2x1': [ 42 (MARGIN, top_start, USABLE_WIDTH, (available_height - GAP) / 2), 43 (MARGIN, top_start + (available_height + GAP) / 2, USABLE_WIDTH, (available_height - GAP) / 2), 44 ], 45 # 2x2 grid (4 panels) 46 '2x2': [ 47 (MARGIN, top_start, (USABLE_WIDTH - GAP) / 2, (available_height - GAP) / 2), 48 (MARGIN + (USABLE_WIDTH + GAP) / 2, top_start, (USABLE_WIDTH - GAP) / 2, (available_height - GAP) / 2), 49 (MARGIN, top_start + (available_height + GAP) / 2, (USABLE_WIDTH - GAP) / 2, (available_height - GAP) / 2), 50 (MARGIN + (USABLE_WIDTH + GAP) / 2, top_start + (available_height + GAP) / 2, (USABLE_WIDTH - GAP) / 2, (available_height - GAP) / 2), 51 ], 52 # 3 figures stacked vertically (3 rows, 1 col) 53 '3x1': [ 54 (MARGIN, top_start, USABLE_WIDTH, (available_height - 2 * GAP) / 3), 55 (MARGIN, top_start + (available_height + GAP) / 3, USABLE_WIDTH, (available_height - 2 * GAP) / 3), 56 (MARGIN, top_start + 2 * (available_height + GAP) / 3, USABLE_WIDTH, (available_height - 2 * GAP) / 3), 57 ], 58 # 3 figures side-by-side (1 row, 3 cols) 59 '1x3': [ 60 (MARGIN, top_start, (USABLE_WIDTH - 2 * GAP) / 3, available_height), 61 (MARGIN + (USABLE_WIDTH + GAP) / 3, top_start, (USABLE_WIDTH - 2 * GAP) / 3, available_height), 62 (MARGIN + 2 * (USABLE_WIDTH + GAP) / 3, top_start, (USABLE_WIDTH - 2 * GAP) / 3, available_height), 63 ], 64 # 2 rows x 3 cols (6 panels) 65 '2x3': [ 66 (MARGIN, top_start, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 67 (MARGIN + (USABLE_WIDTH + GAP) / 3, top_start, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 68 (MARGIN + 2 * (USABLE_WIDTH + GAP) / 3, top_start, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 69 (MARGIN, top_start + (available_height + GAP) / 2, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 70 (MARGIN + (USABLE_WIDTH + GAP) / 3, top_start + (available_height + GAP) / 2, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 71 (MARGIN + 2 * (USABLE_WIDTH + GAP) / 3, top_start + (available_height + GAP) / 2, (USABLE_WIDTH - 2 * GAP) / 3, (available_height - GAP) / 2), 72 ], 73 } 74 return layouts.get(layout, layouts['1x1']) 75 76 77def insert_subfigure_boxes(pptx_path, slide_index, subfigures, layout, title=None): 78 """ 79 Insert subfigure boxes onto a slide, where each box can contain multiple images. 80 81 Args: 82 pptx_path: Path to the .pptx file 83 slide_index: 0-based slide index 84 subfigures: List of subfigure definitions, each is: 85 {'label': 'a', 'images': [path1, path2, ...], 'image_layout': '1x2'} 86 layout: Layout for subfigure BOXES ('1x1', '2x1', '3x1', etc.) 87 title: Optional slide title (e.g., 'Figure 3') 88 """ 89 from pptx.dml.color import RGBColor 90 from pptx.enum.shapes import MSO_SHAPE 91 92 prs = Presentation(pptx_path) 93 slide = prs.slides[slide_index] 94 95 # Add title if specified 96 title_height = 0.6 if title else 0 97 if title: 98 txBox = slide.shapes.add_textbox(Inches(MARGIN), Inches(MARGIN), 99 Inches(USABLE_WIDTH), Inches(title_height)) 100 tf = txBox.text_frame 101 p = tf.paragraphs[0] 102 p.text = title 103 p.font.size = Pt(18) 104 p.font.bold = True 105 106 # Get positions for subfigure boxes 107 box_positions = get_layout_positions(layout, title_height) 108 109 for i, subfig in enumerate(subfigures): 110 if i >= len(box_positions): 111 print(f"Warning: More subfigures than layout positions. Skipping box {subfig['label']}") 112 continue 113 114 box_left, box_top, box_width, box_height = box_positions[i] 115 label = subfig.get('label', '') 116 images = subfig.get('images', []) 117 img_layout = subfig.get('image_layout', '1x1') 118 119 # Add subfigure box border (optional - light gray rectangle) 120 box_shape = slide.shapes.add_shape( 121 MSO_SHAPE.RECTANGLE, 122 Inches(box_left), Inches(box_top), 123 Inches(box_width), Inches(box_height) 124 ) 125 box_shape.fill.background() # Transparent fill 126 box_shape.line.color.rgb = RGBColor(200, 200, 200) # Light gray border 127 box_shape.line.width = Pt(0.5) 128 129 # Add label in top-left corner of box 130 if label: 131 lbl_box = slide.shapes.add_textbox( 132 Inches(box_left + 0.05), Inches(box_top + 0.05), 133 Inches(0.3), Inches(0.25) 134 ) 135 tf = lbl_box.text_frame 136 p = tf.paragraphs[0] 137 p.text = f"({label})" 138 p.font.size = Pt(12) 139 p.font.bold = True 140 141 # Calculate image positions within the box 142 label_offset = 0.3 # Space for label 143 inner_left = box_left + 0.1 144 inner_top = box_top + label_offset 145 inner_width = box_width - 0.2 146 inner_height = box_height - label_offset - 0.1 147 inner_gap = 0.1 148 149 # Parse image layout (e.g., '1x2' = 1 row, 2 cols) 150 if 'x' in img_layout: 151 rows, cols = map(int, img_layout.split('x')) 152 else: 153 rows, cols = 1, len(images) 154 155 img_width = (inner_width - (cols - 1) * inner_gap) / cols 156 img_height = (inner_height - (rows - 1) * inner_gap) / rows 157 158 # Place images in grid within box 159 for j, img_path in enumerate(images): 160 row = j // cols 161 col = j % cols 162 img_left = inner_left + col * (img_width + inner_gap) 163 img_top = inner_top + row * (img_height + inner_gap) 164 165 slide.shapes.add_picture( 166 str(img_path), 167 Inches(img_left), Inches(img_top), 168 width=Inches(img_width), height=Inches(img_height) 169 ) 170 171 prs.save(pptx_path) 172 print(f"Inserted {len(subfigures)} subfigure boxes into slide {slide_index + 1}") 173 174 175# Example: Figure 3 with subfigure boxes a, b, c 176pptx_path = Path('Output/PowerPoint_Figures/Cardiac_RODEO_Tracked.pptx') 177 178subfigures = [ 179 { 180 'label': 'a', 181 'images': [ 182 Path('Output/ROC_Data/roc_arrhythmia.png'), 183 Path('Output/Confusion_Matrices/cm_arrhythmia.png'), 184 ], 185 'image_layout': '1x2' # 2 images side-by-side within box a 186 }, 187 { 188 'label': 'b', 189 'images': [ 190 Path('Output/ROC_Data/roc_heart_damage.png'), 191 Path('Output/Confusion_Matrices/cm_heart_damage.png'), 192 ], 193 'image_layout': '1x2' # 2 images side-by-side within box b 194 }, 195 { 196 'label': 'c', 197 'images': [ 198 Path('Output/SHAP_Data/shap_aligned_arrhythmia.png'), 199 ], 200 'image_layout': '1x1' # 1 image in box c 201 }, 202] 203 204insert_subfigure_boxes( 205 pptx_path=pptx_path, 206 slide_index=2, # Slide 3 (0-indexed) 207 subfigures=subfigures, 208 layout='3x1', # 3 subfigure boxes stacked vertically 209 title='Figure 3' 210)

Layout Reference Table

LayoutDescriptionPanel CountBest For
1x1Single full-width1Single large figure
1x2Side-by-side2Comparison (e.g., ROC vs Confusion)
2x1Stacked vertical2Sequential data
2x22×2 grid4Four related panels
3x13 stacked vertical3Fig 3a, 3b, 3c stacked
1x33 side-by-side3Wide comparison
2x32×3 grid6Six panels

Workflow for tracked figures:

  1. Generate and save PNG at 600 DPI
  2. Save Excel with data and metadata
  3. Insert into PowerPoint using insert_figures_to_slide() with appropriate layout
  4. Update figure_registry.csv

If replacing an existing figure:

  1. Delete the old shape from the slide first
  2. Insert the new figure at the same position
  3. Preserve any existing labels/annotations

Figure Tracking System

Every figure must have a corresponding Excel file with the exact data used to generate it.

Naming Convention

Fig_X_letter_description.png   # The figure image
Fig_X_letter_description.xlsx  # The source data

Examples:

  • Fig_2_a_pipeline_overview.png / Fig_2_a_pipeline_overview.xlsx
  • Fig_2_b_ROC_Arrhythmia.png / Fig_2_b_ROC_Arrhythmia.xlsx
  • Fig_3_c_SHAP_importance.png / Fig_3_c_SHAP_importance.xlsx

Excel file contents should include:

  • Raw data used for plotting
  • Any computed values (means, standard deviations, etc.)
  • Column headers matching axis labels
  • A "Metadata" sheet with generation timestamp and source script

Folder Structure for PowerPoint Projects

Output/PowerPoint_Figures/
├── Fig_1/
│   ├── Fig_1a_pipeline_diagram.png
│   ├── Fig_1a_pipeline_diagram.xlsx (if applicable)
│   ├── Fig_1b_experimental_setup.png
│   └── Fig_1b_experimental_setup.xlsx
├── Fig_2/
│   ├── Fig_2a_organoid_formation.png
│   ├── Fig_2a_organoid_formation.xlsx
│   ├── Fig_2b_ROC_Arrhythmia.png
│   └── Fig_2b_ROC_Arrhythmia.xlsx
├── Fig_3/
│   └── ...
├── scripts_reference.txt   # Lists all source scripts used
├── external_sources.txt    # Notes externally generated images
└── figure_registry.csv     # Master tracking file

Figure Registry (CSV format)

The figure_registry.csv file tracks all figures in the project.

Columns:

ColumnDescription
Figure_IDFigure number (e.g., "1", "2", "3")
LetterPanel letter (e.g., "a", "b", "c")
DescriptionBrief description of the figure
PNG_PathRelative path to PNG file
Excel_PathRelative path to Excel data file (or "N/A")
Source_ScriptScript that generated the figure (or "N/A")
External"TRUE" if externally generated, "FALSE" otherwise
NotesAdditional notes (source software, manual edits, etc.)

Example figure_registry.csv:

csv
1Figure_ID,Letter,Description,PNG_Path,Excel_Path,Source_Script,External,Notes 21,a,Pipeline diagram,Fig_1/Fig_1_a_pipeline_diagram.png,N/A,N/A,TRUE,Created in BioRender 31,b,Experimental setup,Fig_1/Fig_1_b_experimental_setup.png,Fig_1/Fig_1_b_experimental_setup.xlsx,generate_setup_fig.py,FALSE, 42,a,ROC Arrhythmia,Fig_2/Fig_2_a_ROC_Arrhythmia.png,Fig_2/Fig_2_a_ROC_Arrhythmia.xlsx,plot_roc_curves.py,FALSE, 52,b,ROC Heart Damage,Fig_2/Fig_2_b_ROC_HeartDamage.png,Fig_2/Fig_2_b_ROC_HeartDamage.xlsx,plot_roc_curves.py,FALSE, 63,a,SHAP importance,Fig_3/Fig_3_a_SHAP_importance.png,Fig_3/Fig_3_a_SHAP_importance.xlsx,shap_analysis.py,FALSE,Updated 2026-01-15

Consistency Rules

CRITICAL: If one figure changes style, ALL figures must be regenerated.

  1. Font Consistency

    • Always use import figure_config as the first line
    • Helvetica is mandatory for all text
    • Standard sizes: title=14pt bold, axis labels=12pt, tick labels=10pt
  2. Color Palette Consistency

    • Use the defined color palettes from this skill
    • Document any custom colors in figure_registry.csv Notes column
  3. DPI Requirement

    • All figures must be saved at 600 DPI
    python
    1plt.savefig('Fig_2_a_ROC.png', dpi=600, bbox_inches='tight')
  4. Error Bars / Standard Deviation (MANDATORY)

    • Always include error bars on bar plots, grouped bar charts, and box-and-whisker plots
  5. Shared Axes for Multi-Panel Figures (MANDATORY)

    • When multiple panels have the same axis range, share that axis
    • Use sharey=True or sharex=True in plt.subplots()
    • Only show axis labels on the leftmost (Y) or bottom (X) panel
    • This reduces redundancy and makes comparisons easier
    python
    1# CORRECT: Share Y-axis across 3 panels 2fig, axes = plt.subplots(1, 3, figsize=(8, 3), sharey=True) 3for idx, ax in enumerate(axes): 4 if idx == 0: 5 ax.set_ylabel('AUC') # Only first panel gets Y label 6 ax.set_xlabel('Accuracy') 7 8# WRONG: Repeating same Y-axis 3 times 9fig, axes = plt.subplots(1, 3, figsize=(8, 3)) 10for ax in axes: 11 ax.set_ylabel('AUC') # Redundant!
  6. Square Scatter Plots (MANDATORY for Accuracy vs AUC)

    • Use ax.set_box_aspect(1) to force square aspect ratio
    • X and Y axis ranges must be identical (e.g., both 0.3-0.9)
    python
    1ax.set_xlim(0.3, 0.9) 2ax.set_ylim(0.3, 0.9) 3ax.set_box_aspect(1) # Force square
    • Use standard deviation (std) or standard error of the mean (SEM) as appropriate
    • Error bars must be clearly visible with cap lines
    python
    1# Bar plot with error bars 2ax.bar(x, means, yerr=stds, capsize=5, color='#6C92ED', edgecolor='black') 3 4# Grouped bar chart with error bars 5bars = ax.bar(x + offset, values, width, yerr=errors, capsize=3, 6 label=label, color=color, edgecolor='black')
  7. Regeneration Protocol When any style element changes:

    • Update figure_config.py with new settings
    • Run all source scripts listed in scripts_reference.txt
    • Verify all figures in figure_registry.csv are updated
    • Update timestamps in Excel metadata sheets

PowerPoint Integration

Panel Labels

Figures should include letter labels (a, b, c) in boxes for multi-panel figures.

python
1# Add panel label in upper-left corner 2ax.text(-0.12, 1.05, 'a', transform=ax.transAxes, 3 fontsize=16, fontweight='bold', va='top', 4 bbox=dict(boxstyle='square,pad=0.3', facecolor='white', edgecolor='black'))

Standard Panel Sizes

Based on slide dimensions: 7.09" × 8.47" (portrait), with 0.5" margins:

LayoutPanel WidthPanel HeightUse Case
Full slide (1x1)6.09"6.87"Single large figure
Half width (1x2)2.97"6.87"Side-by-side panels
Half height (2x1)6.09"3.36"Stacked panels
Quarter (2x2)2.97"3.36"4-panel grid
Third height (3x1)6.09"2.19"3 stacked (Fig 3a,b,c)
Third width (1x3)1.93"6.87"3 side-by-side

Convert to matplotlib figsize (use 3x scale for 600 DPI export):

python
1# Single full-slide figure 2fig, ax = plt.subplots(figsize=(6.09 * 3, 6.87 * 3)) 3 4# Half-height panel (for 2x1 or 3x1 layouts) 5fig, ax = plt.subplots(figsize=(6.09 * 3, 3.36 * 3)) 6 7# Third-height panel (for 3 stacked figures) 8fig, ax = plt.subplots(figsize=(6.09 * 3, 2.19 * 3)) 9 10# Quarter panel (for 2x2 grid) 11fig, ax = plt.subplots(figsize=(2.97 * 3, 3.36 * 3))

Alignment on Slides

  • Use consistent margins (0.5" from slide edges)
  • Align panel edges to PowerPoint grid
  • Group related panels with consistent spacing (0.1" gap)

External Images

For images not generated by Python scripts (e.g., BioRender, microscopy, schematics):

  1. Mark as "EXTERNAL" in registry

    csv
    11,a,Pipeline diagram,Fig_1/Fig_1_a_pipeline.png,N/A,N/A,TRUE,Created in BioRender
  2. Note original source in external_sources.txt

    Fig_1a_pipeline_diagram.png
    - Source: BioRender
    - Created: 2026-01-10
    - Author: Noah B.
    - License: Academic license, BioRender.com
    - Original file: pipeline_biorender_v3.png
    
    Fig_1b_microscopy.png
    - Source: Confocal microscope (Zeiss LSM 880)
    - Acquisition date: 2025-11-15
    - Sample: Cardiac organoid batch #42
    - Settings: 40x objective, 488nm excitation
    
  3. Still follow naming convention

    • Rename external files to match Fig_X_letter_description.png format
    • Keep originals in a separate _originals/ subfolder if needed

Template: Creating a New PowerPoint Figure Set

python
1import figure_config # FIRST LINE - registers Helvetica 2import matplotlib.pyplot as plt 3import pandas as pd 4from pathlib import Path 5from datetime import datetime 6 7# Setup paths 8output_dir = Path('Output/PowerPoint_Figures/Fig_2') 9output_dir.mkdir(parents=True, exist_ok=True) 10 11# Your data 12data = pd.DataFrame({ 13 'Model': ['Model A', 'Model B', 'Model C'], 14 'AUC': [0.85, 0.92, 0.78], 15 'AUC_std': [0.03, 0.02, 0.05] 16}) 17 18# Create figure 19fig, ax = plt.subplots(figsize=(2.42 * 3, 1.36 * 3)) # 3x scale for high-res 20 21# ... plotting code ... 22 23# Add panel label 24ax.text(-0.12, 1.05, 'a', transform=ax.transAxes, 25 fontsize=16, fontweight='bold', va='top', 26 bbox=dict(boxstyle='square,pad=0.3', facecolor='white', edgecolor='black')) 27 28plt.tight_layout() 29 30# Save figure at 600 DPI 31fig_path = output_dir / 'Fig_2_a_AUC_comparison.png' 32plt.savefig(fig_path, dpi=600, bbox_inches='tight') 33plt.close() 34 35# Save Excel with data and metadata 36excel_path = output_dir / 'Fig_2_a_AUC_comparison.xlsx' 37with pd.ExcelWriter(excel_path, engine='openpyxl') as writer: 38 data.to_excel(writer, sheet_name='Data', index=False) 39 metadata = pd.DataFrame({ 40 'Field': ['Generated', 'Script', 'Figure ID'], 41 'Value': [datetime.now().isoformat(), __file__, 'Fig_2_a'] 42 }) 43 metadata.to_excel(writer, sheet_name='Metadata', index=False) 44 45print(f"Saved: {fig_path}") 46print(f"Saved: {excel_path}")

Checklist for PowerPoint Figures

  • import figure_config is the first line
  • Figure saved at 600 DPI
  • Naming follows Fig_X_letter_description convention (e.g., Fig_1_a_ROC.png)
  • Excel file created with matching name
  • Excel includes "Metadata" sheet with timestamp and source script
  • Panel label added (if multi-panel figure)
  • CRITICAL: Figure inserted into PowerPoint at designated position
  • Entry added to figure_registry.csv
  • External sources documented in external_sources.txt
  • All related figures use consistent colors/fonts/styles
  • Error bars included on bar plots and box-and-whisker plots (std or SEM)

Связанные навыки

Looking for an alternative to figure-graphing or another community skill for your workflow? Explore these related open-source skills.

Показать все

openclaw-release-maintainer

Logo of openclaw
openclaw

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

widget-generator

Logo of f
f

Generate customizable widget plugins for the prompts.chat feed system

flags

Logo of vercel
vercel

The React Framework

138.4k
0
Браузер

pr-review

Logo of pytorch
pytorch

Tensors and Dynamic neural networks in Python with strong GPU acceleration

98.6k
0
Разработчик