3D Bar Chart for Categorical Comparison in Seaborn (Python)

The same plot in 13 other libraries — Python: Altair, Bokeh, lets-plot, Matplotlib, Plotly, plotnine, Pygal; R: ggplot2; Julia: Makie.jl; JavaScript: Chart.js, D3.js, Apache ECharts, MUI X Charts. Compare all 14 side by side: 3D Bar Chart for Categorical Comparison in Python, R, Julia and JavaScript.

A three-dimensional bar chart where bars rise from a 2D categorical grid, with height encoding the measured value. Two categorical axes define the grid position on the base plane while the vertical axis shows magnitude. This visualization extends the bar chart family into 3D space, making it effective for comparing values across two categorical dimensions simultaneously.

3D Bar Chart for Categorical Comparison rendered with Seaborn

Renders

Python source (Seaborn)

""" anyplot.ai
bar-3d-categorical: 3D Bar Chart for Categorical Comparison
Library: seaborn 0.13.2 | Python 3.13.13
Quality: 85/100 | Created: 2026-05-15
"""

import os

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns


# Theme tokens
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 = ["#009E73", "#C475FD", "#4467A3", "#BD8233"]

sns.set_theme(
    style="ticks",
    rc={
        "figure.facecolor": PAGE_BG,
        "axes.facecolor": PAGE_BG,
        "axes.edgecolor": INK_SOFT,
        "axes.labelcolor": INK,
        "text.color": INK,
        "xtick.color": INK_SOFT,
        "ytick.color": INK_SOFT,
        "grid.color": INK,
        "grid.alpha": 0.10,
        "legend.facecolor": ELEVATED_BG,
        "legend.edgecolor": INK_SOFT,
    },
)

# Data — quarterly unit sales (thousands) by product category and region
np.random.seed(42)

products = ["Laptops", "Tablets", "Phones", "Monitors"]
regions = ["North", "South", "East", "West"]
n_prod = len(products)
n_reg = len(regions)

sales_base = np.array(
    [[45.2, 31.8, 38.5, 27.9], [22.1, 18.4, 25.3, 20.1], [68.7, 55.2, 72.1, 60.8], [15.3, 11.9, 18.2, 13.7]]
)
sales = sales_base + np.random.normal(0, 1.2, (n_prod, n_reg))

# Plot
fig = plt.figure(figsize=(16, 9), facecolor=PAGE_BG)
ax = fig.add_subplot(111, projection="3d")

bar_w = 0.55
bar_d = 0.55

for i, (_product, color) in enumerate(zip(products, IMPRINT, strict=False)):
    for j in range(n_reg):
        h = sales[i, j]
        ax.bar3d(i - bar_w / 2, j - bar_d / 2, 0, bar_w, bar_d, h, color=color, alpha=0.85, shade=True)
        ax.text(i, j, h + 1.5, f"{h:.0f}", ha="center", va="bottom", fontsize=11, color=INK, fontweight="medium")

# Style — axis ticks and labels
ax.set_xticks(range(n_prod))
ax.set_xticklabels(products, fontsize=14)
ax.set_yticks(range(n_reg))
ax.set_yticklabels(regions, fontsize=14)
ax.tick_params(axis="x", colors=INK_SOFT, pad=6)
ax.tick_params(axis="y", colors=INK_SOFT, pad=6)
ax.tick_params(axis="z", labelsize=14, colors=INK_SOFT)

ax.set_xlabel("Product Category", fontsize=18, color=INK, labelpad=18)
ax.set_ylabel("Region", fontsize=18, color=INK, labelpad=18)
ax.set_zlabel("Units Sold (thousands)", fontsize=18, color=INK, labelpad=12)

ax.set_title("bar-3d-categorical · seaborn · anyplot.ai", fontsize=22, fontweight="medium", color=INK, pad=22)

ax.view_init(elev=30, azim=45)

# Pane backgrounds — transparent with subtle edges
for pane in (ax.xaxis.pane, ax.yaxis.pane, ax.zaxis.pane):
    pane.fill = False
    pane.set_edgecolor(INK_SOFT)
    pane.set_alpha(0.25)

# Legend
patches = [mpatches.Patch(color=IMPRINT[i], label=p) for i, p in enumerate(products)]
legend = ax.legend(
    handles=patches,
    loc="upper left",
    fontsize=14,
    title="Product",
    title_fontsize=15,
    facecolor=ELEVATED_BG,
    edgecolor=INK_SOFT,
    labelcolor=INK_SOFT,
)
legend.get_title().set_color(INK)

# Save
plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/bar-3d-categorical/seaborn/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "bar-3d-categorical",
  "language": "python",
  "library": "seaborn",
  "page": "https://anyplot.ai/bar-3d-categorical/python/seaborn",
  "hub": "https://anyplot.ai/bar-3d-categorical",
  "code_json": "https://api.anyplot.ai/specs/bar-3d-categorical/seaborn/code",
  "spec_json": "https://api.anyplot.ai/specs/bar-3d-categorical",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/seaborn/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/seaborn/plot-dark.png",
  "quality_score": 85.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of 3D Bar Chart for Categorical Comparison on anyplot.ai.

Other implementations