install
source · Clone the upstream repo
git clone https://github.com/FreedomIntelligence/OpenClaw-Medical-Skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/FreedomIntelligence/OpenClaw-Medical-Skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data-visualization-biomedical" ~/.claude/skills/freedomintelligence-openclaw-medical-skills-data-visualization-biomedical && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/FreedomIntelligence/OpenClaw-Medical-Skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/data-visualization-biomedical" ~/.openclaw/skills/freedomintelligence-openclaw-medical-skills-data-visualization-biomedical && rm -rf "$T"
manifest:
skills/data-visualization-biomedical/SKILL.mdsource content
<!--
# COPYRIGHT NOTICE
# This file is part of the "Universal Biomedical Skills" project.
# Copyright (c) 2026 MD BABU MIA, PhD <md.babu.mia@mssm.edu>
# All Rights Reserved.
#
# This code is proprietary and confidential.
# Unauthorized copying of this file, via any medium is strictly prohibited.
#
# Provenance: Authenticated by MD BABU MIA
-->
name: data-visualization-biomedical description: "Publication-quality visualizations for biomedical and genomics data. Use when creating volcano plots, heatmaps, UMAP plots, dot plots, survival curves, forest plots, or multi-panel figures. Includes scanpy, matplotlib, seaborn, plotly workflows with journal-ready aesthetics and proper statistical annotations." license: Proprietary
Biomedical Data Visualization
Publication-Quality Settings
import matplotlib.pyplot as plt import seaborn as sns import numpy as np import pandas as pd # Nature/Blood style settings plt.rcParams.update({ 'font.family': 'Arial', 'font.size': 8, 'axes.labelsize': 8, 'axes.titlesize': 9, 'xtick.labelsize': 7, 'ytick.labelsize': 7, 'legend.fontsize': 7, 'figure.dpi': 300, 'savefig.dpi': 300, 'savefig.bbox': 'tight', 'axes.linewidth': 0.5, 'xtick.major.width': 0.5, 'ytick.major.width': 0.5, }) # Color palettes NATURE_COLORS = ['#E64B35', '#4DBBD5', '#00A087', '#3C5488', '#F39B7F', '#8491B4'] BLOOD_COLORS = ['#D62728', '#1F77B4', '#2CA02C', '#FF7F0E', '#9467BD', '#8C564B']
Volcano Plot
def volcano_plot(df, log2fc_col='log2FC', pval_col='pval_adj', gene_col='gene', fc_thresh=1, pval_thresh=0.05, highlight_genes=None, figsize=(4, 4)): """Publication-quality volcano plot.""" fig, ax = plt.subplots(figsize=figsize) df = df.copy() df['-log10pval'] = -np.log10(df[pval_col].clip(lower=1e-300)) # Categorize points df['category'] = 'NS' df.loc[(df[log2fc_col] > fc_thresh) & (df[pval_col] < pval_thresh), 'category'] = 'Up' df.loc[(df[log2fc_col] < -fc_thresh) & (df[pval_col] < pval_thresh), 'category'] = 'Down' colors = {'NS': '#CCCCCC', 'Up': '#E64B35', 'Down': '#4DBBD5'} for cat, color in colors.items(): subset = df[df['category'] == cat] ax.scatter(subset[log2fc_col], subset['-log10pval'], c=color, s=10, alpha=0.7, edgecolors='none', label=cat) # Add threshold lines ax.axhline(-np.log10(pval_thresh), color='grey', linestyle='--', linewidth=0.5) ax.axvline(-fc_thresh, color='grey', linestyle='--', linewidth=0.5) ax.axvline(fc_thresh, color='grey', linestyle='--', linewidth=0.5) # Label specific genes if highlight_genes: for gene in highlight_genes: if gene in df[gene_col].values: row = df[df[gene_col] == gene].iloc[0] ax.annotate(gene, (row[log2fc_col], row['-log10pval']), fontsize=6, ha='center') ax.set_xlabel('log₂ Fold Change') ax.set_ylabel('-log₁₀ Adjusted P-value') ax.legend(frameon=False, loc='upper right') plt.tight_layout() return fig, ax
Heatmap with Clustering
import scipy.cluster.hierarchy as sch from matplotlib.colors import LinearSegmentedColormap def clustered_heatmap(data, row_labels=None, col_labels=None, cmap='RdBu_r', center=0, figsize=(8, 10), row_cluster=True, col_cluster=True): """Hierarchically clustered heatmap.""" # Clustering if row_cluster: row_linkage = sch.linkage(data, method='ward') row_order = sch.dendrogram(row_linkage, no_plot=True)['leaves'] data = data[row_order, :] if row_labels is not None: row_labels = [row_labels[i] for i in row_order] if col_cluster: col_linkage = sch.linkage(data.T, method='ward') col_order = sch.dendrogram(col_linkage, no_plot=True)['leaves'] data = data[:, col_order] if col_labels is not None: col_labels = [col_labels[i] for i in col_order] fig, ax = plt.subplots(figsize=figsize) im = ax.imshow(data, aspect='auto', cmap=cmap, vmin=center-np.abs(data).max(), vmax=center+np.abs(data).max()) if row_labels: ax.set_yticks(range(len(row_labels))) ax.set_yticklabels(row_labels) if col_labels: ax.set_xticks(range(len(col_labels))) ax.set_xticklabels(col_labels, rotation=45, ha='right') plt.colorbar(im, ax=ax, shrink=0.5, label='Expression (z-score)') plt.tight_layout() return fig, ax
Scanpy Visualization Enhancements
import scanpy as sc def enhanced_dotplot(adata, genes, groupby, figsize=(10, 8)): """Enhanced dot plot with proper visibility.""" sc.pl.dotplot( adata, var_names=genes, groupby=groupby, expression_cutoff=0.0001, mean_only_expressed=False, standard_scale='None', smallest_dot=0.1, dot_max=1.0, cmap='Reds', colorbar_title='Mean expression', size_title='Fraction of cells (%)', figsize=figsize, show=False ) plt.tight_layout() return plt.gcf() def multi_batch_umap(adata, color_by, batch_key='batch', figsize_per=(4, 4)): """UMAP plots per batch.""" batches = adata.obs[batch_key].unique() n_batches = len(batches) fig, axes = plt.subplots(1, n_batches, figsize=(figsize_per[0]*n_batches, figsize_per[1])) if n_batches == 1: axes = [axes] for ax, batch in zip(axes, batches): adata_batch = adata[adata.obs[batch_key] == batch] sc.pl.umap(adata_batch, color=color_by, ax=ax, show=False, title=f'{batch}') plt.tight_layout() return fig
Statistical Annotation
from scipy import stats def add_significance(ax, x1, x2, y, h, p_value): """Add significance bar to plot.""" ax.plot([x1, x1, x2, x2], [y, y+h, y+h, y], 'k-', linewidth=0.5) if p_value < 0.0001: sig = '****' elif p_value < 0.001: sig = '***' elif p_value < 0.01: sig = '**' elif p_value < 0.05: sig = '*' else: sig = 'ns' ax.text((x1+x2)/2, y+h, sig, ha='center', va='bottom', fontsize=8)
Multi-Panel Figure Assembly
from matplotlib.gridspec import GridSpec def create_figure_panel(n_rows, n_cols, width_ratios=None, height_ratios=None): """Create multi-panel figure.""" fig = plt.figure(figsize=(3*n_cols, 3*n_rows)) gs = GridSpec(n_rows, n_cols, figure=fig, width_ratios=width_ratios or [1]*n_cols, height_ratios=height_ratios or [1]*n_rows, wspace=0.3, hspace=0.3) axes = [] for i in range(n_rows): row = [] for j in range(n_cols): ax = fig.add_subplot(gs[i, j]) row.append(ax) axes.append(row) return fig, axes def label_panels(axes, labels=None, fontsize=12, fontweight='bold'): """Add A, B, C... labels to panels.""" if labels is None: labels = [chr(65+i) for i in range(len(axes))] # A, B, C... for ax, label in zip(axes, labels): ax.text(-0.15, 1.05, label, transform=ax.transAxes, fontsize=fontsize, fontweight=fontweight, va='top')
Export for Journals
def save_figure(fig, filename, formats=['pdf', 'png', 'svg']): """Save in multiple formats for journals.""" for fmt in formats: fig.savefig(f"{filename}.{fmt}", format=fmt, dpi=300, bbox_inches='tight', facecolor='white', edgecolor='none') print(f"Saved: {filename}.{{{'|'.join(formats)}}}")
See
references/color_guidelines.md for accessibility standards.
See scripts/figure_templates.py for pre-built templates.
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->