Basic Pie Chart — Altair

A pie chart showing proportions of categorical data as slices of a circle. Each slice represents a category's contribution to the whole, with the angle proportional to its value. Essential for visualizing parts-to-whole relationships in a simple, intuitive format.

Basic Pie Chart rendered with Altair

Python source (Altair)

""" anyplot.ai
pie-basic: Basic Pie Chart
Library: altair 6.1.0 | Python 3.13.13
Quality: 90/100 | Updated: 2026-05-28
"""

import os
import sys


# This file is named altair.py — same as the library. Remove the script
# directory from sys.path so Python finds the real altair package in the venv.
_here = os.path.normcase(os.path.abspath(os.path.dirname(__file__)))
sys.path[:] = [p for p in sys.path if os.path.normcase(os.path.abspath(p if p else os.getcwd())) != _here]

import altair as alt
import pandas as pd
from PIL import Image


# 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_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"]

# Data — cloud infrastructure market share
data = pd.DataFrame(
    {"category": ["AWS", "Azure", "Google Cloud", "Alibaba", "Oracle", "Others"], "value": [31, 24, 11, 4, 3, 27]}
)

total = data["value"].sum()
data["percentage"] = data["value"] / total * 100
data["label"] = data["percentage"].apply(lambda x: f"{x:.0f}%")
data["order"] = range(len(data))

domain = data["category"].tolist()
color_scale = alt.Scale(domain=domain, range=IMPRINT_PALETTE)

title_str = "pie-basic · python · altair · anyplot.ai"

# Shared base encodings
base = alt.Chart(data).encode(
    theta=alt.Theta("value:Q", stack=True),
    order=alt.Order("order:O"),
    color=alt.Color(
        "category:N",
        scale=color_scale,
        legend=alt.Legend(
            title="Provider",
            titleFontSize=12,
            labelFontSize=11,
            symbolSize=180,
            orient="bottom",
            direction="horizontal",
            columns=6,
            titleAnchor="middle",
        ),
    ),
)

# Non-AWS pie slices
pie = (
    base.transform_filter(alt.datum.category != "AWS")
    .mark_arc(outerRadius=185, innerRadius=0, stroke=PAGE_BG, strokeWidth=2, padAngle=0.02, cornerRadius=3)
    .encode(tooltip=[alt.Tooltip("category:N", title="Provider"), alt.Tooltip("value:Q", title="Market Share (%)")])
)

# Exploded AWS slice — emphasises the market leader
exploded_aws = (
    base.transform_filter(alt.datum.category == "AWS")
    .mark_arc(
        outerRadius=185, innerRadius=0, radiusOffset=25, stroke=PAGE_BG, strokeWidth=2.5, padAngle=0.04, cornerRadius=3
    )
    .encode(tooltip=[alt.Tooltip("category:N", title="Provider"), alt.Tooltip("value:Q", title="Market Share (%)")])
)

# Percentage labels for slices ≥5% at standard radius
text_main = (
    base.transform_filter((alt.datum.category != "AWS") & (alt.datum.percentage >= 5))
    .mark_text(radius=228, fontSize=16, fontWeight="bold", color=INK)
    .encode(text="label:N")
)

# Small slices (<5%) pushed further out to reduce crowding between 3% and 4% labels
text_small = (
    base.transform_filter((alt.datum.category != "AWS") & (alt.datum.percentage < 5))
    .mark_text(radius=250, fontSize=13, fontWeight="bold", color=INK_SOFT)
    .encode(text="label:N")
)

# AWS label with matching radiusOffset
text_aws = (
    base.transform_filter(alt.datum.category == "AWS")
    .mark_text(radius=228, radiusOffset=25, fontSize=16, fontWeight="bold", color=INK)
    .encode(text="label:N")
)

chart = (
    alt.layer(pie, exploded_aws, text_main, text_small, text_aws)
    .properties(
        width=500,
        height=460,
        background=PAGE_BG,
        title=alt.Title(
            text=title_str,
            subtitle="Global Cloud Infrastructure Market Share",
            fontSize=16,
            subtitleFontSize=12,
            subtitleColor=INK_SOFT,
            anchor="middle",
            color=INK,
        ),
    )
    .configure_view(fill=PAGE_BG, strokeWidth=0)
    .configure_legend(
        fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK, padding=12, offset=8
    )
)

# Save PNG targeting 2400×2400 (square — pie chart)
chart.save(f"plot-{THEME}.png", scale_factor=4.0)

# PAD-only-to-target (2400×2400) — DO NOT crop
TW, TH = 2400, 2400
PAGE_BG_RGB = tuple(int(PAGE_BG.lstrip("#")[i : i + 2], 16) for i in (0, 2, 4))
_img = Image.open(f"plot-{THEME}.png").convert("RGB")
_w, _h = _img.size
if _w > TW or _h > TH:
    raise SystemExit(
        f"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. "
        f"Shrink chart .properties(width=, height=) values and re-render."
    )
if _w < TW or _h < TH:
    _canvas = Image.new("RGB", (TW, TH), PAGE_BG_RGB)
    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))
    _canvas.save(f"plot-{THEME}.png")

chart.save(f"plot-{THEME}.html")

Part of Basic Pie Chart on anyplot.ai.

Other implementations