Skip to content

Clustered heatmap

Five nutrients of 19 fruits, each scaled to mean 0 and standard deviation 1. The rows are ordered by hierarchical clustering and the tree is drawn beside the axis (hclust + scale_y_dendro, after R's ggh4x and legendry). A side track shows each fruit's calories with its own fill scale (geom_ysidetile from ggside, and new_scale_fill from ggnewscale).

import polars as pl
from ggrust import *

DATA = "https://raw.githubusercontent.com/datavil/ggrust/main/docs/data/"
fruit = pl.read_csv(DATA + "fruit.csv")
nutrients = ["calories", "sugar_g", "fibre_g", "vitamin_c_mg"]
scaled = fruit.with_columns((pl.col(n) - pl.col(n).mean()) / pl.col(n).std() for n in nutrients)

rows = hclust(scaled.select("fruit", *nutrients), labels="fruit", method="average")
long = scaled.unpivot(index="fruit", on=nutrients, variable_name="nutrient", value_name="z")

plot = (
    ggplot(long, aes("nutrient", "fruit"))
    + geom_tile(aes(fill="z"), color="white")
    + scale_fill_gradient2(low="#2166ac", mid="white", high="#b2182b", name="z-score")
    + scale_y_dendro(rows)
    + new_scale_fill()
    + geom_ysidetile(
        aes(x="calorie", fill="calories"), data=fruit.with_columns(calorie=pl.lit("kcal"))
    )
    + scale_fill_viridis(option="cividis", name="kcal")
    + labs(x="", y="")
    + theme_minimal()
    + ggsize(800, 650)
)
plot

Download the script