Skill Tracer
Skills › Scientific Agent Skills › scanpy › plotting_guide.md

plotting_guide.md

376 lines · skills/scanpy/references/plotting_guide.md at 5f2bc03

Open on GitHub
1# Scanpy Plotting Guide
2
3Reviewed for Scanpy 1.12.4 / AnnData 0.13.4 on 2026-10-01. These snippets are
4illustrative and require the named genes, groups and prior results to exist.
5Use the tested CLI workflow for a runnable starting point. Counts, normalized
6expression and `.raw` have distinct roles; see [upstream-review.md](upstream-review.md).
7
8
9Comprehensive guide for creating publication-quality visualizations with scanpy.

Scanpy Plotting Guide

Reviewed for Scanpy 1.12.4 / AnnData 0.13.4 on 2026-10-01. These snippets are illustrative and require the named genes, groups and prior results to exist. Use the tested CLI workflow for a runnable starting point. Counts, normalized expression and .raw have distinct roles; see upstream-review.md.

Comprehensive guide for creating publication-quality visualizations with scanpy.

Raw lines 1 to 10
11## General Plotting Principles
13All scanpy plotting functions follow consistent patterns:
14- Functions in `sc.pl.*` mirror analysis functions in `sc.tl.*`
15- Most accept `color` parameter for gene names or metadata columns
16- Prefer `sc.settings.autosave = True` and `sc.settings.figdir` for saving (the per-plot `save=` parameter is deprecated in scanpy 1.12)
17- Multiple plots can be generated in a single call
19```python
20sc.settings.figdir = './figures/'
21sc.settings.autosave = True
22sc.settings.file_format_figs = 'pdf'
23```

General Plotting Principles

All scanpy plotting functions follow consistent patterns:

  • Functions in sc.pl.* mirror analysis functions in sc.tl.*
  • Most accept color parameter for gene names or metadata columns
  • Prefer sc.settings.autosave = True and sc.settings.figdir for saving (the per-plot save= parameter is deprecated in scanpy 1.12)
  • Multiple plots can be generated in a single call
sc.settings.figdir = './figures/'
sc.settings.autosave = True
sc.settings.file_format_figs = 'pdf'
Raw lines 11 to 24
25## Essential Quality Control Plots

Essential Quality Control Plots

Raw lines 25 to 26
27### Visualize QC Metrics
29```python
30# Violin plots for QC metrics
31sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'],
32 jitter=0.4, multi_panel=True, save='_qc_violin.pdf')
34# Scatter plots to identify outliers
35sc.pl.scatter(adata, x='total_counts', y='pct_counts_mt', save='_qc_mt.pdf')
36sc.pl.scatter(adata, x='total_counts', y='n_genes_by_counts', save='_qc_genes.pdf')
38# Highest expressing genes
39sc.pl.highest_expr_genes(adata, n_top=20, save='_highest_expr.pdf')
40```

Visualize QC Metrics

# Violin plots for QC metrics
sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'],
             jitter=0.4, multi_panel=True, save='_qc_violin.pdf')

# Scatter plots to identify outliers
sc.pl.scatter(adata, x='total_counts', y='pct_counts_mt', save='_qc_mt.pdf')
sc.pl.scatter(adata, x='total_counts', y='n_genes_by_counts', save='_qc_genes.pdf')

# Highest expressing genes
sc.pl.highest_expr_genes(adata, n_top=20, save='_highest_expr.pdf')
Raw lines 27 to 41
42### Post-filtering QC
44```python
45# Compare before and after filtering
46sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts'],
47 groupby='sample', save='_post_filter.pdf')
48```

Post-filtering QC

# Compare before and after filtering
sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts'],
             groupby='sample', save='_post_filter.pdf')
Raw lines 42 to 49
50## Dimensionality Reduction Visualizations

Dimensionality Reduction Visualizations

Raw lines 50 to 51
52### PCA Plots
54```python
55# Basic PCA
56sc.pl.pca(adata, color='leiden', save='_pca.pdf')
58# PCA colored by gene expression
59sc.pl.pca(adata, color=['gene1', 'gene2', 'gene3'], save='_pca_genes.pdf')
61# Variance ratio plot (elbow plot)
62sc.pl.pca_variance_ratio(adata, log=True, n_pcs=50, save='_variance.pdf')
64# PCA loadings
65sc.pl.pca_loadings(adata, components=[1, 2, 3], save='_loadings.pdf')
66```

PCA Plots

# Basic PCA
sc.pl.pca(adata, color='leiden', save='_pca.pdf')

# PCA colored by gene expression
sc.pl.pca(adata, color=['gene1', 'gene2', 'gene3'], save='_pca_genes.pdf')

# Variance ratio plot (elbow plot)
sc.pl.pca_variance_ratio(adata, log=True, n_pcs=50, save='_variance.pdf')

# PCA loadings
sc.pl.pca_loadings(adata, components=[1, 2, 3], save='_loadings.pdf')
Raw lines 52 to 67
68### UMAP Plots
70```python
71# Basic UMAP with clusters
72sc.pl.umap(adata, color='leiden', legend_loc='on data', save='_umap_leiden.pdf')
74# UMAP colored by multiple variables
75sc.pl.umap(adata, color=['leiden', 'cell_type', 'batch'],
76 save='_umap_multi.pdf')
78# UMAP with gene expression
79sc.pl.umap(adata, color=['CD3D', 'CD14', 'MS4A1'],
80 use_raw=False, save='_umap_genes.pdf')
82# Customize appearance
83sc.pl.umap(adata, color='leiden',
84 palette='Set2',
85 size=50,
86 alpha=0.8,
87 frameon=False,
88 title='Cell Types',
89 save='_umap_custom.pdf')
90```

UMAP Plots

# Basic UMAP with clusters
sc.pl.umap(adata, color='leiden', legend_loc='on data', save='_umap_leiden.pdf')

# UMAP colored by multiple variables
sc.pl.umap(adata, color=['leiden', 'cell_type', 'batch'],
           save='_umap_multi.pdf')

# UMAP with gene expression
sc.pl.umap(adata, color=['CD3D', 'CD14', 'MS4A1'],
           use_raw=False, save='_umap_genes.pdf')

# Customize appearance
sc.pl.umap(adata, color='leiden',
           palette='Set2',
           size=50,
           alpha=0.8,
           frameon=False,
           title='Cell Types',
           save='_umap_custom.pdf')
Raw lines 68 to 91
92### t-SNE Plots
94```python
95# t-SNE with clusters
96sc.pl.tsne(adata, color='leiden', legend_loc='right margin', save='_tsne.pdf')
98# Multiple t-SNE perplexities (if computed)
99sc.pl.tsne(adata, color='leiden', save='_tsne_default.pdf')
100```

t-SNE Plots

# t-SNE with clusters
sc.pl.tsne(adata, color='leiden', legend_loc='right margin', save='_tsne.pdf')

# Multiple t-SNE perplexities (if computed)
sc.pl.tsne(adata, color='leiden', save='_tsne_default.pdf')
Raw lines 92 to 101
102## Clustering Visualizations

Clustering Visualizations

Raw lines 102 to 103
104### Basic Cluster Plots
106```python
107# UMAP with cluster annotations
108sc.pl.umap(adata, color='leiden', add_outline=True,
109 legend_loc='on data', legend_fontsize=12,
110 legend_fontoutline=2, frameon=False,
111 save='_clusters.pdf')
113# Overlay single-cell graph edges (does not measure cluster proportions)
114sc.pl.umap(adata, color='leiden', size=50, edges=True,
115 edges_width=0.1, save='_clusters_edges.pdf')
116```

Basic Cluster Plots

# UMAP with cluster annotations
sc.pl.umap(adata, color='leiden', add_outline=True,
           legend_loc='on data', legend_fontsize=12,
           legend_fontoutline=2, frameon=False,
           save='_clusters.pdf')

# Overlay single-cell graph edges (does not measure cluster proportions)
sc.pl.umap(adata, color='leiden', size=50, edges=True,
           edges_width=0.1, save='_clusters_edges.pdf')
Raw lines 104 to 117
118### Cluster Comparison
120```python
121# Compare clustering resolutions
122sc.pl.umap(adata, color=['leiden_0.3', 'leiden_0.5', 'leiden_0.8'],
123 save='_cluster_comparison.pdf')
125# Cluster dendrogram
126sc.tl.dendrogram(adata, groupby='leiden')
127sc.pl.dendrogram(adata, groupby='leiden', save='_dendrogram.pdf')
128```

Cluster Comparison

# Compare clustering resolutions
sc.pl.umap(adata, color=['leiden_0.3', 'leiden_0.5', 'leiden_0.8'],
           save='_cluster_comparison.pdf')

# Cluster dendrogram
sc.tl.dendrogram(adata, groupby='leiden')
sc.pl.dendrogram(adata, groupby='leiden', save='_dendrogram.pdf')
Raw lines 118 to 129
130## Marker Gene Visualizations

Marker Gene Visualizations

Raw lines 130 to 131
132### Ranked Marker Genes
134```python
135# Overview of top markers per cluster
136sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False,
137 save='_marker_overview.pdf')
139# Heatmap of top markers
140sc.pl.rank_genes_groups_heatmap(adata, n_genes=10, groupby='leiden',
141 show_gene_labels=True,
142 save='_marker_heatmap.pdf')
144# Dot plot of markers
145sc.pl.rank_genes_groups_dotplot(adata, n_genes=5,
146 save='_marker_dotplot.pdf')
148# Stacked violin plots
149sc.pl.rank_genes_groups_stacked_violin(adata, n_genes=5,
150 save='_marker_violin.pdf')
152# Matrix plot
153sc.pl.rank_genes_groups_matrixplot(adata, n_genes=5,
154 save='_marker_matrix.pdf')
155```

Ranked Marker Genes

# Overview of top markers per cluster
sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False,
                        save='_marker_overview.pdf')

# Heatmap of top markers
sc.pl.rank_genes_groups_heatmap(adata, n_genes=10, groupby='leiden',
                                 show_gene_labels=True,
                                 save='_marker_heatmap.pdf')

# Dot plot of markers
sc.pl.rank_genes_groups_dotplot(adata, n_genes=5,
                                 save='_marker_dotplot.pdf')

# Stacked violin plots
sc.pl.rank_genes_groups_stacked_violin(adata, n_genes=5,
                                        save='_marker_violin.pdf')

# Matrix plot
sc.pl.rank_genes_groups_matrixplot(adata, n_genes=5,
                                    save='_marker_matrix.pdf')
Raw lines 132 to 156
157### Specific Gene Expression
159```python
160# Violin plots for specific genes
161marker_genes = ['CD3D', 'CD14', 'MS4A1', 'NKG7', 'FCGR3A']
162sc.pl.violin(adata, keys=marker_genes, groupby='leiden',
163 save='_markers_violin.pdf')
165# Dot plot for curated markers
166sc.pl.dotplot(adata, var_names=marker_genes, groupby='leiden',
167 save='_markers_dotplot.pdf')
169# Heatmap for specific genes
170sc.pl.heatmap(adata, var_names=marker_genes, groupby='leiden',
171 swap_axes=True, save='_markers_heatmap.pdf')
173# Stacked violin for gene sets
174sc.pl.stacked_violin(adata, var_names=marker_genes, groupby='leiden',
175 save='_markers_stacked.pdf')
176```

Specific Gene Expression

# Violin plots for specific genes
marker_genes = ['CD3D', 'CD14', 'MS4A1', 'NKG7', 'FCGR3A']
sc.pl.violin(adata, keys=marker_genes, groupby='leiden',
             save='_markers_violin.pdf')

# Dot plot for curated markers
sc.pl.dotplot(adata, var_names=marker_genes, groupby='leiden',
              save='_markers_dotplot.pdf')

# Heatmap for specific genes
sc.pl.heatmap(adata, var_names=marker_genes, groupby='leiden',
              swap_axes=True, save='_markers_heatmap.pdf')

# Stacked violin for gene sets
sc.pl.stacked_violin(adata, var_names=marker_genes, groupby='leiden',
                     save='_markers_stacked.pdf')
Raw lines 157 to 177
178### Gene Expression on Embeddings
180```python
181# Multiple genes on UMAP
182genes = ['CD3D', 'CD14', 'MS4A1', 'NKG7']
183sc.pl.umap(adata, color=genes, cmap='viridis',
184 save='_umap_markers.pdf')
186# Gene expression with custom colormap
187sc.pl.umap(adata, color='CD3D', cmap='Reds',
188 vmin=0, vmax=3, save='_umap_cd3d.pdf')
189```

Gene Expression on Embeddings

# Multiple genes on UMAP
genes = ['CD3D', 'CD14', 'MS4A1', 'NKG7']
sc.pl.umap(adata, color=genes, cmap='viridis',
           save='_umap_markers.pdf')

# Gene expression with custom colormap
sc.pl.umap(adata, color='CD3D', cmap='Reds',
           vmin=0, vmax=3, save='_umap_cd3d.pdf')
Raw lines 178 to 190
191## Trajectory and Pseudotime Visualizations

Trajectory and Pseudotime Visualizations

Raw lines 191 to 192
193### PAGA Plots
195```python
196# PAGA graph
197sc.pl.paga(adata, color='leiden', save='_paga.pdf')
199# PAGA with gene expression
200sc.pl.paga(adata, color=['leiden', 'dpt_pseudotime'],
201 save='_paga_pseudotime.pdf')
203# Single-cell neighbor edges on UMAP (not the PAGA graph)
204sc.pl.umap(adata, color='leiden', save='_umap_with_paga.pdf',
205 edges=True, edges_color='gray')
206```

PAGA Plots

# PAGA graph
sc.pl.paga(adata, color='leiden', save='_paga.pdf')

# PAGA with gene expression
sc.pl.paga(adata, color=['leiden', 'dpt_pseudotime'],
           save='_paga_pseudotime.pdf')

# Single-cell neighbor edges on UMAP (not the PAGA graph)
sc.pl.umap(adata, color='leiden', save='_umap_with_paga.pdf',
           edges=True, edges_color='gray')
Raw lines 193 to 207
208### Pseudotime Plots
210```python
211# DPT pseudotime on UMAP
212sc.pl.umap(adata, color='dpt_pseudotime', save='_umap_dpt.pdf')
214# Gene expression along pseudotime
215sc.pl.dpt_timeseries(adata, save='_dpt_timeseries.pdf')
217# Heatmap grouped by cluster (not ordered by pseudotime)
218sc.pl.heatmap(adata, var_names=genes, groupby='leiden',
219 use_raw=False, show_gene_labels=True,
220 save='_pseudotime_heatmap.pdf')
221```

Pseudotime Plots

# DPT pseudotime on UMAP
sc.pl.umap(adata, color='dpt_pseudotime', save='_umap_dpt.pdf')

# Gene expression along pseudotime
sc.pl.dpt_timeseries(adata, save='_dpt_timeseries.pdf')

# Heatmap grouped by cluster (not ordered by pseudotime)
sc.pl.heatmap(adata, var_names=genes, groupby='leiden',
              use_raw=False, show_gene_labels=True,
              save='_pseudotime_heatmap.pdf')
Raw lines 208 to 222
223## Advanced Visualizations

Advanced Visualizations

Raw lines 223 to 224
225### Tracks Plot (Gene Expression Trends)
227```python
228# Show gene expression across cell types
229sc.pl.tracksplot(adata, var_names=marker_genes, groupby='leiden',
230 save='_tracks.pdf')
231```

Tracks Plot (Gene Expression Trends)

# Show gene expression across cell types
sc.pl.tracksplot(adata, var_names=marker_genes, groupby='leiden',
                 save='_tracks.pdf')
Raw lines 225 to 232
233### Correlation Matrix
235```python
236# Correlation between clusters
237sc.pl.correlation_matrix(adata, groupby='leiden',
238 save='_correlation.pdf')
239```

Correlation Matrix

# Correlation between clusters
sc.pl.correlation_matrix(adata, groupby='leiden',
                         save='_correlation.pdf')
Raw lines 233 to 240
241### Embedding Density
243```python
244# Cell density on UMAP
245sc.tl.embedding_density(adata, basis='umap', groupby='cell_type')
246sc.pl.embedding_density(adata, basis='umap', key='umap_density_cell_type',
247 save='_density.pdf')
248```

Embedding Density

# Cell density on UMAP
sc.tl.embedding_density(adata, basis='umap', groupby='cell_type')
sc.pl.embedding_density(adata, basis='umap', key='umap_density_cell_type',
                        save='_density.pdf')
Raw lines 241 to 249
250## Multi-Panel Figures

Multi-Panel Figures

Raw lines 250 to 251
252### Creating Panel Figures
254```python
255import matplotlib.pyplot as plt
257# Create multi-panel figure
258fig, axes = plt.subplots(2, 2, figsize=(12, 12))
260# Plot on specific axes
261sc.pl.umap(adata, color='leiden', ax=axes[0, 0], show=False)
262sc.pl.umap(adata, color='CD3D', ax=axes[0, 1], show=False)
263sc.pl.umap(adata, color='CD14', ax=axes[1, 0], show=False)
264sc.pl.umap(adata, color='MS4A1', ax=axes[1, 1], show=False)
266plt.tight_layout()
267plt.savefig('figures/multi_panel.pdf')
268plt.show()
269```

Creating Panel Figures

import matplotlib.pyplot as plt

# Create multi-panel figure
fig, axes = plt.subplots(2, 2, figsize=(12, 12))

# Plot on specific axes
sc.pl.umap(adata, color='leiden', ax=axes[0, 0], show=False)
sc.pl.umap(adata, color='CD3D', ax=axes[0, 1], show=False)
sc.pl.umap(adata, color='CD14', ax=axes[1, 0], show=False)
sc.pl.umap(adata, color='MS4A1', ax=axes[1, 1], show=False)

plt.tight_layout()
plt.savefig('figures/multi_panel.pdf')
plt.show()
Raw lines 252 to 270
271## Publication-Quality Customization

Publication-Quality Customization

Raw lines 271 to 272
273### High-Quality Settings
275```python
276# Set publication-quality defaults
277sc.set_figure_params(dpi=300, frameon=False, figsize=(5, 5),
278 facecolor='white')
280# Vector graphics output
281sc.settings.figdir = './figures/'
282sc.settings.file_format_figs = 'pdf' # or 'svg'
283```

High-Quality Settings

# Set publication-quality defaults
sc.set_figure_params(dpi=300, frameon=False, figsize=(5, 5),
                               facecolor='white')

# Vector graphics output
sc.settings.figdir = './figures/'
sc.settings.file_format_figs = 'pdf'  # or 'svg'
Raw lines 273 to 284
285### Custom Color Palettes
287```python
288# Use custom colors
289custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
290sc.pl.umap(adata, color='leiden', palette=custom_colors,
291 save='_custom_colors.pdf')
293# Continuous color maps
294sc.pl.umap(adata, color='CD3D', cmap='viridis', save='_viridis.pdf')
295sc.pl.umap(adata, color='CD3D', cmap='RdBu_r', save='_rdbu.pdf')
296```

Custom Color Palettes

# Use custom colors
custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
sc.pl.umap(adata, color='leiden', palette=custom_colors,
           save='_custom_colors.pdf')

# Continuous color maps
sc.pl.umap(adata, color='CD3D', cmap='viridis', save='_viridis.pdf')
sc.pl.umap(adata, color='CD3D', cmap='RdBu_r', save='_rdbu.pdf')
Raw lines 285 to 297
298### Remove Axes and Frames
300```python
301# Clean plot without axes
302sc.pl.umap(adata, color='leiden', frameon=False,
303 save='_clean.pdf')
305# No legend
306sc.pl.umap(adata, color='leiden', legend_loc=None,
307 save='_no_legend.pdf')
308```

Remove Axes and Frames

# Clean plot without axes
sc.pl.umap(adata, color='leiden', frameon=False,
           save='_clean.pdf')

# No legend
sc.pl.umap(adata, color='leiden', legend_loc=None,
           save='_no_legend.pdf')
Raw lines 298 to 309
310## Exporting Plots

Exporting Plots

Raw lines 310 to 311
312### Save via Settings (recommended)
314```python
315sc.settings.figdir = './figures/'
316sc.settings.autosave = True
317sc.settings.file_format_figs = 'pdf'
319sc.pl.umap(adata, color='leiden') # Saves to figures/umap.pdf
320```
322The per-plot `save=` parameter still works but is deprecated in scanpy 1.12.

Save via Settings (recommended)

sc.settings.figdir = './figures/'
sc.settings.autosave = True
sc.settings.file_format_figs = 'pdf'

sc.pl.umap(adata, color='leiden')  # Saves to figures/umap.pdf

The per-plot save= parameter still works but is deprecated in scanpy 1.12.

Raw lines 312 to 323
324### Manual Saving
326```python
327import matplotlib.pyplot as plt
328fig = sc.pl.umap(adata, color='leiden', show=False, return_fig=True)
329fig.savefig('figures/my_umap.pdf', dpi=300, bbox_inches='tight')
330```

Manual Saving

import matplotlib.pyplot as plt
fig = sc.pl.umap(adata, color='leiden', show=False, return_fig=True)
fig.savefig('figures/my_umap.pdf', dpi=300, bbox_inches='tight')
Raw lines 324 to 331
332### Batch Export
334Disable autosave here: repeated calls otherwise overwrite the same filename.
336```python
337sc.settings.autosave = False
338genes = ['CD3D', 'CD14', 'MS4A1']
339for gene in genes:
340 fig = sc.pl.umap(adata, color=gene, show=False, return_fig=True)
341 fig.savefig(f'figures/umap_{gene}.pdf', bbox_inches='tight')
342 plt.close(fig)
343```

Batch Export

Disable autosave here: repeated calls otherwise overwrite the same filename.

sc.settings.autosave = False
genes = ['CD3D', 'CD14', 'MS4A1']
for gene in genes:
    fig = sc.pl.umap(adata, color=gene, show=False, return_fig=True)
    fig.savefig(f'figures/umap_{gene}.pdf', bbox_inches='tight')
    plt.close(fig)
Raw lines 332 to 344
345## Common Customization Parameters

Common Customization Parameters

Raw lines 345 to 346
347### Layout Parameters
348- `figsize`: Figure size (width, height)
349- `frameon`: Show frame around plot
350- `title`: Plot title
351- `legend_loc`: 'right margin', 'on data', 'best', or None
352- `legend_fontsize`: Font size for legend
353- `size`: Point size

Layout Parameters

  • figsize: Figure size (width, height)
  • frameon: Show frame around plot
  • title: Plot title
  • legend_loc: 'right margin', 'on data', 'best', or None
  • legend_fontsize: Font size for legend
  • size: Point size
Raw lines 347 to 354
355### Color Parameters
356- `color`: Variable(s) to color by
357- `palette`: Color palette (e.g., 'Set1', 'viridis')
358- `cmap`: Colormap for continuous variables
359- `vmin`, `vmax`: Color scale limits
360- `use_raw`: Read the expression values stored in .raw (not necessarily counts)

Color Parameters

  • color: Variable(s) to color by
  • palette: Color palette (e.g., 'Set1', 'viridis')
  • cmap: Colormap for continuous variables
  • vmin, vmax: Color scale limits
  • use_raw: Read the expression values stored in .raw (not necessarily counts)
Raw lines 355 to 361
362### Saving Parameters
363- `show`: Whether to display plot
364- `dpi`: Resolution for raster formats
365- Use `sc.settings.autosave` + `sc.settings.figdir` instead of deprecated `save=`

Saving Parameters

  • show: Whether to display plot
  • dpi: Resolution for raster formats
  • Use sc.settings.autosave + sc.settings.figdir instead of deprecated save=
Raw lines 362 to 366
367## Tips for Publication Figures
3691. **Use vector formats**: PDF or SVG for scalable graphics
3702. **High DPI**: Set dpi=300 or higher for raster images
3713. **Consistent styling**: Use the same color palette across figures
3724. **Clear labels**: Ensure gene names and cell types are readable
3735. **White background**: Use `facecolor='white'` for publications
3746. **Remove clutter**: Set `frameon=False` for cleaner appearance
3757. **Legend placement**: Use 'on data' for compact figures
3768. **Color blind friendly**: Consider palettes like 'colorblind' or 'Set2'

Tips for Publication Figures

  1. Use vector formats: PDF or SVG for scalable graphics
  2. High DPI: Set dpi=300 or higher for raster images
  3. Consistent styling: Use the same color palette across figures
  4. Clear labels: Ensure gene names and cell types are readable
  5. White background: Use facecolor='white' for publications
  6. Remove clutter: Set frameon=False for cleaner appearance
  7. Legend placement: Use 'on data' for compact figures
  8. Color blind friendly: Consider palettes like 'colorblind' or 'Set2'
Raw lines 367 to 376