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.
# Base plots shared for discrete palette demos
p1 = example_scatterplot()
p2 = example_barplot()
NPG
LAST = (p1 + scale_color_npg()) | (p2 + scale_fill_npg())
AAAS
LAST = (p1 + scale_color_aaas()) | (p2 + scale_fill_aaas())
NEJM
LAST = (p1 + scale_color_nejm()) | (p2 + scale_fill_nejm())
Lancet
LAST = (p1 + scale_color_lancet()) | (p2 + scale_fill_lancet())
JAMA
LAST = (p1 + scale_color_jama()) | (p2 + scale_fill_jama())
BMJ
LAST = (p1 + scale_color_bmj()) | (p2 + scale_fill_bmj())
JCO
LAST = (p1 + scale_color_jco()) | (p2 + scale_fill_jco())
UCSCGB
LAST = (p1 + scale_color_ucscgb()) | (p2 + scale_fill_ucscgb())
D3 (category10)
LAST = (p1 + scale_color_d3("category10")) | (p2 + scale_fill_d3("category10"))
Observable 10
LAST = (p1 + scale_color_observable()) | (p2 + scale_fill_observable())
Primer
LAST = (p1 + scale_color_primer()) | (p2 + scale_fill_primer())
Atlassian
LAST = (p1 + scale_color_atlassian()) | (p2 + scale_fill_atlassian())
iTerm
LAST = (p1 + scale_color_iterm("Rose Pine")) | (p2 + scale_fill_iterm("Rose Pine"))
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
LAST = (p1 + scale_color_locuszoom()) | (p2 + scale_fill_locuszoom())
IGV (chromosomes)
LAST = (p1 + scale_color_igv()) | (p2 + scale_fill_igv())
COSMIC
LAST = (p1 + scale_color_cosmic("hallmarks_light")) | (p2 + scale_fill_cosmic("hallmarks_light"))
LAST = (p1 + scale_color_cosmic("hallmarks_dark")) | (p2 + scale_fill_cosmic("hallmarks_dark"))
LAST = (p1 + scale_color_cosmic("signature_substitutions")) | (p2 + scale_fill_cosmic("signature_substitutions"))
UChicago
LAST = (p1 + scale_color_uchicago()) | (p2 + scale_fill_uchicago())
Star Trek
LAST = (p1 + scale_color_startrek()) | (p2 + scale_fill_startrek())
Tron Legacy (use with dark theme)
LAST = (p1 + theme_dark() + scale_color_tron()) | (p2 + theme_dark() + scale_fill_tron())
Futurama
LAST = (p1 + scale_color_futurama()) | (p2 + scale_fill_futurama())
Rick and Morty
LAST = (p1 + scale_color_rickandmorty()) | (p2 + scale_fill_rickandmorty())
The Simpsons
LAST = (p1 + scale_color_simpsons()) | (p2 + scale_fill_simpsons())
Flat UI
LAST = (p1 + scale_color_flatui()) | (p2 + scale_fill_flatui())
Frontiers
LAST = (p1 + scale_color_frontiers()) | (p2 + scale_fill_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)
LAST = (p3 + scale_fill_gsea()) | (p3 + scale_fill_gsea(reverse=True))
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.