Get started¶
ggsci for Python offers a collection of plotnine color palettes inspired by scientific journals, data visualization libraries, science fiction movies, and TV shows. Palettes are exposed as plotnine scales:
scale_color_palname()
/scale_colour_palname()
scale_fill_palname()
This article mirrors the original R package vignette using plotnine.
import numpy as np
import pandas as pd
from plotnine import *
from plotnine.data import diamonds, mtcars
from ggsci import *
LAST = None
Discrete color palettes¶
We will use reusable helpers that construct a scatter plot with a smoothed
curve and a side-by-side bar plot to demonstrate discrete palettes. The
examples below use the diamonds
dataset and apply each palette to color
and fill
scales respectively.
NPG¶
AAAS¶
NEJM¶
Lancet¶
JAMA¶
BMJ¶
JCO¶
UCSCGB¶
D3 (category10)¶
Observable 10¶
Primer¶
Atlassian¶
iTerm¶
You can preview these color palettes in ggsci on a dedicated microsite: https://nanx.me/ggsci-iterm/. It renders example plots for all palettes on a single page for fast visual comparison.
LocusZoom¶
IGV (chromosomes)¶
COSMIC¶
LAST = (p1 + scale_color_cosmic("signature_substitutions")) | (p2 + scale_fill_cosmic("signature_substitutions"))
UChicago¶
Star Trek¶
Tron Legacy (use with dark theme)¶
Futurama¶
Rick and Morty¶
The Simpsons¶
Flat UI¶
Frontiers¶
Continuous color palettes¶
There are two types of continuous palettes: diverging and sequential. We demonstrate with a correlation heatmap and a small random matrix.
# Correlation matrix for diverging palettes (numeric columns only)
cor = mtcars.select_dtypes(include=[np.number]).corr()
cor_melt = (
cor.stack()
.reset_index()
.rename(columns={"level_0": "Var1", "level_1": "Var2", 0: "value"})
)
p3 = (
ggplot(cor_melt, aes("Var1", "Var2", fill="value"))
+ geom_tile(color="black", size=0.3)
+ theme_void()
)
# Random upper-triangular matrix for sequential palettes
np.random.seed(42)
k = 6
x = np.eye(k)
iu = np.triu_indices(k, 1)
x[iu] = np.random.uniform(0, 1, size=len(iu[0]))
x_melt = (
pd.DataFrame(x)
.stack()
.reset_index()
.rename(columns={"level_0": "Var1", "level_1": "Var2", 0: "value"})
)
p4 = (
ggplot(x_melt, aes("Var1", "Var2", fill="value"))
+ geom_tile(color="black", size=0.3)
+ scale_x_continuous(expand=(0, 0))
+ scale_y_continuous(expand=(0, 0))
+ theme_bw()
+ theme(
legend_position="none",
plot_background=element_rect(fill="white"),
panel_background=element_rect(fill="white"),
axis_title_x=element_blank(),
axis_title_y=element_blank(),
axis_text_x=element_blank(),
axis_text_y=element_blank(),
axis_ticks=element_blank(),
axis_line=element_blank(),
panel_border=element_blank(),
panel_grid_major=element_blank(),
panel_grid_minor=element_blank(),
)
)
# Placeholder panel to pad compositions to equal column counts
def blank_panel():
# Build a grid matching p4's tile layout, but with white tiles (no fill mapping)
try:
df = x_melt[["Var1", "Var2"]].copy()
except NameError:
# Fallback to a 6x6 grid if x_melt is not yet defined
k = 6
df = pd.DataFrame(
[(i, j) for i in range(k) for j in range(k)], columns=["Var1", "Var2"]
)
return (
ggplot(df, aes("Var1", "Var2"))
+ geom_tile(fill="white", color="black", size=0.3)
+ scale_x_continuous(expand=(0, 0))
+ scale_y_continuous(expand=(0, 0))
+ theme_bw()
+ theme(
legend_position="none",
plot_background=element_rect(fill="white"),
panel_background=element_rect(fill="white"),
axis_title_x=element_blank(),
axis_title_y=element_blank(),
axis_text_x=element_blank(),
axis_text_y=element_blank(),
axis_ticks=element_blank(),
axis_line=element_blank(),
panel_border=element_blank(),
panel_grid_major=element_blank(),
panel_grid_minor=element_blank(),
)
)
GSEA (diverging)¶
Bootstrap 5 (sequential)¶
row1 = (
(p4 + scale_fill_bs5("blue"))
| (p4 + scale_fill_bs5("indigo"))
| (p4 + scale_fill_bs5("purple"))
| (p4 + scale_fill_bs5("pink"))
| (p4 + scale_fill_bs5("red"))
| (p4 + scale_fill_bs5("orange"))
| (p4 + scale_fill_bs5("yellow"))
| (p4 + scale_fill_bs5("green"))
)
row2 = (
(p4 + scale_fill_bs5("teal"))
| (p4 + scale_fill_bs5("cyan"))
| (p4 + scale_fill_bs5("gray"))
| blank_panel()
| blank_panel()
| blank_panel()
| blank_panel()
| blank_panel()
)
LAST = row1 / row2
Material Design (sequential)¶
row1 = (
(p4 + scale_fill_material("red"))
| (p4 + scale_fill_material("pink"))
| (p4 + scale_fill_material("purple"))
| (p4 + scale_fill_material("deep-purple"))
| (p4 + scale_fill_material("indigo"))
| (p4 + scale_fill_material("blue"))
| (p4 + scale_fill_material("light-blue"))
| (p4 + scale_fill_material("cyan"))
)
row2 = (
(p4 + scale_fill_material("teal"))
| (p4 + scale_fill_material("green"))
| (p4 + scale_fill_material("light-green"))
| (p4 + scale_fill_material("lime"))
| (p4 + scale_fill_material("yellow"))
| (p4 + scale_fill_material("amber"))
| (p4 + scale_fill_material("orange"))
| (p4 + scale_fill_material("deep-orange"))
)
row3 = (
(p4 + scale_fill_material("brown"))
| (p4 + scale_fill_material("grey"))
| (p4 + scale_fill_material("blue-grey"))
| blank_panel()
| blank_panel()
| blank_panel()
| blank_panel()
| blank_panel()
)
LAST = row1 / row2 / row3
Tailwind CSS 3 (sequential)¶
row1 = (
(p4 + scale_fill_tw3("slate"))
| (p4 + scale_fill_tw3("gray"))
| (p4 + scale_fill_tw3("zinc"))
| (p4 + scale_fill_tw3("neutral"))
| (p4 + scale_fill_tw3("stone"))
| (p4 + scale_fill_tw3("red"))
| (p4 + scale_fill_tw3("orange"))
| (p4 + scale_fill_tw3("amber"))
)
row2 = (
(p4 + scale_fill_tw3("yellow"))
| (p4 + scale_fill_tw3("lime"))
| (p4 + scale_fill_tw3("green"))
| (p4 + scale_fill_tw3("emerald"))
| (p4 + scale_fill_tw3("teal"))
| (p4 + scale_fill_tw3("cyan"))
| (p4 + scale_fill_tw3("sky"))
| (p4 + scale_fill_tw3("blue"))
)
row3 = (
(p4 + scale_fill_tw3("indigo"))
| (p4 + scale_fill_tw3("violet"))
| (p4 + scale_fill_tw3("purple"))
| (p4 + scale_fill_tw3("fuchsia"))
| (p4 + scale_fill_tw3("pink"))
| (p4 + scale_fill_tw3("rose"))
| blank_panel()
| blank_panel()
)
LAST = row1 / row2 / row3
Use palette functions outside plotnine¶
You can use palette generator functions to get hex color codes directly for other plotting systems.
mypal = pal_observable("observable10", alpha=0.7)(10)
df_cols = pd.DataFrame({"x": range(len(mypal)), "fill": mypal})
LAST = (
ggplot(df_cols, aes("x", 1, fill="fill"))
+ geom_tile(width=1, height=1)
+ scale_fill_identity(guide=None)
+ theme_void()
)
Discussion¶
Some palettes may not be optimal for specific needs such as color-blind safety, photocopy safety, or print-friendliness. Consider alternatives like ColorBrewer or viridis when these constraints apply. Palettes in ggsci are for research and demonstration purposes only.