{"spec_id":"heatmap-clustered","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nheatmap-clustered: Clustered Heatmap\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 96/100 | Updated: 2026-05-09\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.patches import Patch\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Data: Gene expression matrix (simulated)\nnp.random.seed(42)\n\n# Create realistic gene expression data with natural clusters\nn_genes = 30\nn_samples = 20\n\n# Gene groups (3 clusters)\ngene_groups = np.repeat([\"Immune\", \"Metabolic\", \"Signaling\"], [10, 10, 10])\n\n# Sample groups (2 conditions)\nsample_groups = np.repeat([\"Control\", \"Treatment\"], [10, 10])\n\n# Generate base expression patterns for each gene cluster\nexpression = np.zeros((n_genes, n_samples))\n\n# Immune genes: higher in treatment\nexpression[0:10, 0:10] = np.random.normal(-1, 0.5, (10, 10))\nexpression[0:10, 10:20] = np.random.normal(1.5, 0.5, (10, 10))\n\n# Metabolic genes: lower in treatment\nexpression[10:20, 0:10] = np.random.normal(1, 0.5, (10, 10))\nexpression[10:20, 10:20] = np.random.normal(-1.2, 0.5, (10, 10))\n\n# Signaling genes: mixed response\nexpression[20:30, 0:10] = np.random.normal(0.3, 0.8, (10, 10))\nexpression[20:30, 10:20] = np.random.normal(-0.3, 0.8, (10, 10))\n\n# Create gene and sample labels\ngene_labels = [f\"{gene_groups[i][0]}{i + 1:02d}\" for i in range(n_genes)]\nsample_labels = [f\"{sample_groups[i][0]}{i + 1:02d}\" for i in range(n_samples)]\n\n# Create DataFrame\ndf = pd.DataFrame(expression, index=gene_labels, columns=sample_labels)\n\n# Create color palettes for annotations\ngene_palette = {\"Immune\": \"#306998\", \"Metabolic\": \"#FFD43B\", \"Signaling\": \"#7B9F35\"}\nsample_palette = {\"Control\": \"#E57373\", \"Treatment\": \"#64B5F6\"}\n\ngene_colors = pd.Series([gene_palette[g] for g in gene_groups], index=gene_labels)\nsample_colors = pd.Series([sample_palette[s] for s in sample_groups], index=sample_labels)\n\n# Set seaborn theme for consistent styling\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Plot: Clustered heatmap with dendrograms\nsns.set_context(\"talk\", font_scale=1.1)\n\ng = sns.clustermap(\n    df,\n    method=\"ward\",\n    metric=\"euclidean\",\n    cmap=\"RdBu_r\",\n    center=0,\n    vmin=-3,\n    vmax=3,\n    row_colors=gene_colors,\n    col_colors=sample_colors,\n    dendrogram_ratio=(0.15, 0.15),\n    cbar_pos=(0.02, 0.3, 0.03, 0.4),\n    figsize=(16, 12),\n    linewidths=0.5,\n    linecolor=INK_SOFT,\n    xticklabels=True,\n    yticklabels=True,\n    tree_kws={\"linewidths\": 2},\n)\n\n# Set figure background\ng.figure.patch.set_facecolor(PAGE_BG)\n\n# Style adjustments\ng.ax_heatmap.set_xlabel(\"Samples\", fontsize=20, color=INK)\ng.ax_heatmap.set_ylabel(\"Genes\", fontsize=20, color=INK)\ng.ax_heatmap.tick_params(axis=\"x\", labelsize=12, rotation=45, colors=INK_SOFT)\ng.ax_heatmap.tick_params(axis=\"y\", labelsize=12, colors=INK_SOFT)\ng.ax_heatmap.set_facecolor(PAGE_BG)\n\n# Title\ng.figure.suptitle(\"heatmap-clustered · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, y=0.98)\n\n# Colorbar label\ng.cax.set_ylabel(\"Expression (z-score)\", fontsize=14, color=INK)\ng.cax.tick_params(labelsize=12, colors=INK_SOFT)\ng.cax.set_facecolor(PAGE_BG)\n\n# Add legends for row/column colors with better positioning\n# Gene group legend\ngene_legend = [Patch(facecolor=gene_palette[k], label=k) for k in gene_palette]\ng.ax_heatmap.legend(\n    handles=gene_legend,\n    title=\"Gene Group\",\n    loc=\"upper left\",\n    bbox_to_anchor=(1.15, 1.0),\n    fontsize=12,\n    title_fontsize=14,\n    frameon=True,\n    fancybox=False,\n    edgecolor=INK_SOFT,\n    facecolor=ELEVATED_BG,\n)\n\n# Sample group legend positioned lower to avoid overlap\nsample_legend = [Patch(facecolor=sample_palette[k], label=k) for k in sample_palette]\ng.figure.legend(\n    handles=sample_legend,\n    title=\"Condition\",\n    loc=\"lower left\",\n    bbox_to_anchor=(0.88, 0.25),\n    fontsize=12,\n    title_fontsize=14,\n    frameon=True,\n    fancybox=False,\n    edgecolor=INK_SOFT,\n    facecolor=ELEVATED_BG,\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}