import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from PyComplexHeatmap import ClusterMapPlotter, HeatmapAnnotation, anno_simpleComplex Heatmap (Python)
A complex heatmap clusters a matrix and stacks annotation layers (cohort, sex, subtype) beside it, turning an expression table into a single self-contained figure. seaborn.clustermap covers the basics; PyComplexHeatmap adds the publication-grade annotation system โ categorical tracks, per-track legends, and split clustering โ used across omics papers.
Example

Setup
- System Requirements: Cross-platform (Linux/MacOS/Windows)
- Programming Language: Python
- Dependencies:
matplotlib,pandas,numpy,PyComplexHeatmap
Data Preparation
We simulate a 30-gene ร 20-sample expression matrix from two expression programmes plus cohort/sex sample annotations, z-scaled per gene.
np.random.seed(181)
genes = [f'GENE{i:02d}' for i in range(1, 31)]
samples = [f'S{i:02d}' for i in range(1, 21)]
cohort = np.random.choice(['Responder', 'Non-responder'], 20, p=[0.5, 0.5])
sex = np.random.choice(['F', 'M'], 20)
prog1 = np.outer(np.r_[np.ones(10), np.zeros(20)],
np.r_[np.full(10, 1.4), np.zeros(10)])
prog2 = np.outer(np.r_[np.zeros(10), np.ones(20)],
np.r_[np.zeros(10), np.full(10, 1.2)])
expr = prog1 + prog2 + np.random.normal(0, 0.5, (30, 20))
expr = (expr - expr.mean(axis=1, keepdims=True)) / expr.std(axis=1, keepdims=True)
mat = pd.DataFrame(expr, index=genes, columns=samples)
col_meta = pd.DataFrame({'Cohort': cohort, 'Sex': sex}, index=samples)Visualization
Clustered Heatmap with Sample Annotations
HeatmapAnnotation stacks categorical tracks above the matrix; ClusterMapPlotter clusters rows and columns and builds the per-track legends automatically.
col_ha = HeatmapAnnotation(
Cohort=anno_simple(col_meta.Cohort, colors={'Responder': '#66c2a5',
'Non-responder': '#e63946'}),
Sex=anno_simple(col_meta.Sex, colors={'F': '#e78ac3', 'M': '#8da0cb'}),
axis=1, verbose=0)
plt.figure(figsize=(8, 6.5))
cm = ClusterMapPlotter(data=mat, top_annotation=col_ha,
col_cluster=True, row_cluster=True,
show_colnames=False, show_rownames=False,
cmap='RdBu_r', vmin=-2, vmax=2, center=0,
xticklabels=False, yticklabels=False,
rasterized=True, verbose=0)
plt.suptitle('Gene expression programmes by cohort', y=1.02, fontsize=12)
plt.show()
With Gene Labels
At 30 rows a compact font keeps every gene name legible while the two expression programmes stay visible as annotation-separated blocks.
plt.figure(figsize=(8, 6.5))
cm = ClusterMapPlotter(data=mat, top_annotation=col_ha,
col_cluster=True, row_cluster=True,
show_colnames=False, show_rownames=True,
row_names_side='right',
yticklabels_kws=dict(labelsize=7),
cmap='RdBu_r', vmin=-2, vmax=2, center=0,
rasterized=True, verbose=0)
plt.show()
References
- Zhang, W. (2023). PyComplexHeatmap: A Python package to visualize complex heatmaps. https://ding-lab.github.io/PyComplexHeatmap/
- Gu, Z., Eils, R., & Schlesner, M. (2016). Complex heatmaps reveal patterns and correlations in multidimensional genomic data. Bioinformatics, 32(18), 2847-2849.
