Pictogram Chart (Isotype Visualization) — Seaborn

A pictogram chart represents quantities using repeated icons or symbols, where each icon stands for a fixed number of units. Inspired by Otto Neurath's ISOTYPE system, this visualization makes numerical comparisons more intuitive and engaging than plain bar charts. It is especially effective for public-facing data communication and infographics where visual appeal and immediate comprehension are important.

Pictogram Chart (Isotype Visualization) rendered with Seaborn

Python source (Seaborn)

""" anyplot.ai
pictogram-basic: Pictogram Chart (Isotype Visualization)
Library: seaborn 0.13.2 | Python 3.13.13
Quality: 87/100 | Updated: 2026-06-03
"""

import os
import sys


# Remove this file's directory from sys.path so "import seaborn" resolves to the library
_this_dir = os.path.dirname(os.path.abspath(__file__))
sys.path = [p for p in sys.path if p != _this_dir]

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns


THEME = os.getenv("ANYPLOT_THEME", "light")

PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"

# Imprint palette — canonical order, first series always #009E73
IMPRINT_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"]

# Data — Developer survey: most-used programming languages (% of respondents)
categories = ["Python", "JavaScript", "Java", "C++", "Go"]
values = [67, 55, 43, 32, 25]
unit_value = 10  # each icon = 10% of respondents

# Category colors from Imprint palette in canonical order
cat_colors = {cat: IMPRINT_PALETTE[i] for i, cat in enumerate(categories)}

# Build icon DataFrame — each row is one icon dot
rows = []
for cat, val in zip(categories, values, strict=True):
    full_icons = val // unit_value
    partial = (val % unit_value) / unit_value
    for j in range(full_icons):
        rows.append({"category": cat, "x": j, "icon_type": "full"})
    if partial > 0:
        rows.append({"category": cat, "x": full_icons, "icon_type": "partial"})

df = pd.DataFrame(rows)

# Theme-aware seaborn setup
sns.set_theme(
    style="white",
    rc={
        "figure.facecolor": PAGE_BG,
        "axes.facecolor": PAGE_BG,
        "axes.labelcolor": INK_SOFT,
        "text.color": INK,
        "xtick.color": INK_SOFT,
        "ytick.color": INK,
        "legend.facecolor": ELEVATED_BG,
        "legend.edgecolor": INK_SOFT,
    },
)

fig, ax = plt.subplots(figsize=(8, 4.5), dpi=400)
fig.set_facecolor(PAGE_BG)
ax.set_facecolor(PAGE_BG)

# Full icons via seaborn stripplot — discrete dot placement along y-axis categories
df_full = df[df["icon_type"] == "full"]
if not df_full.empty:
    sns.stripplot(
        data=df_full,
        x="x",
        y="category",
        hue="category",
        order=categories,
        hue_order=categories,
        palette=cat_colors,
        size=14,
        marker="o",
        edgecolor=PAGE_BG,
        linewidth=0.8,
        jitter=False,
        dodge=False,
        legend=False,
        zorder=3,
        ax=ax,
    )

# Partial icons — faded to signal fractional remainder
df_partial = df[df["icon_type"] == "partial"]
for cat in categories:
    cat_partial = df_partial[df_partial["category"] == cat]
    if not cat_partial.empty:
        sns.stripplot(
            data=cat_partial,
            x="x",
            y="category",
            order=categories,
            color=cat_colors[cat],
            alpha=0.35,
            size=14,
            marker="o",
            edgecolor=cat_colors[cat],
            linewidth=0.8,
            jitter=False,
            dodge=False,
            legend=False,
            zorder=3,
            ax=ax,
        )

# Subtle highlight band for the top category row
ax.axhspan(-0.4, 0.4, color=IMPRINT_PALETTE[0], alpha=0.07, zorder=0)

# Value annotations to the right of each row
for idx, (_cat, val) in enumerate(zip(categories, values, strict=True)):
    total_icons = val // unit_value + (1 if val % unit_value > 0 else 0)
    ax.text(
        total_icons + 0.3,
        idx,
        f"{val}%",
        fontsize=8,
        va="center",
        ha="left",
        color=INK,
        fontweight="bold" if idx == 0 else "normal",
    )

# Axis styling
ax.set_xlabel(f"Icons (each = {unit_value}% of respondents)", fontsize=10, color=INK_SOFT, labelpad=8)
ax.set_ylabel("")
ax.tick_params(axis="y", length=0, pad=8, labelsize=8, colors=INK)
ax.tick_params(axis="x", which="both", bottom=False, labelbottom=False)

max_x_pos = max(v // unit_value + (1 if v % unit_value > 0 else 0) for v in values)
ax.set_xlim(-0.7, max_x_pos + 1.5)

ax.set_title("pictogram-basic · python · seaborn · anyplot.ai", fontsize=12, fontweight="medium", pad=14, color=INK)

sns.despine(left=True, bottom=True, ax=ax)

# Unit legend annotation — tertiary text
ax.annotate(
    f"● = {unit_value}% of respondents   (lighter = partial unit)",
    xy=(0.5, -0.10),
    xycoords="axes fraction",
    fontsize=8,
    ha="center",
    va="top",
    color=INK_MUTED,
)

plt.savefig(f"plot-{THEME}.png", dpi=400, facecolor=PAGE_BG)
plt.close()

Part of Pictogram Chart (Isotype Visualization) on anyplot.ai.

Other implementations