Skip to content

Commit e444376

Browse files
MarkusNeusingerclaude[bot]github-actions[bot]
authored
feat: add pie-basic implementation (9 libraries) (#513)
## Summary Adds `pie-basic` plot implementation. ### Libraries - **Merged:** 9 (all libraries) - **Not Feasible:** 0 ### Links - **Spec:** `specs/pie-basic.md` - **Parent Issue:** #206 --- :robot: *Auto-generated by pyplots CI* --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
1 parent b7470b9 commit e444376

File tree

9 files changed

+509
-1427
lines changed

9 files changed

+509
-1427
lines changed

plots/altair/arc/pie-basic/default.py

Lines changed: 48 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -7,152 +7,54 @@
77
import pandas as pd
88

99

10-
# PyPlots.ai default color palette
11-
PYPLOTS_COLORS = ["#306998", "#FFD43B", "#DC2626", "#059669", "#8B5CF6", "#F97316"]
12-
13-
14-
def create_plot(
15-
data: pd.DataFrame,
16-
category: str,
17-
value: str,
18-
*,
19-
title: str | None = None,
20-
colors: list[str] | None = None,
21-
startangle: float = 90,
22-
show_labels: bool = True,
23-
label_format: str = ".1%",
24-
legend: bool = True,
25-
legend_loc: str = "right",
26-
inner_radius: float = 0,
27-
outer_radius: float = 150,
28-
**kwargs,
29-
) -> alt.Chart:
30-
"""
31-
Create a basic pie chart visualizing proportions of categorical data.
32-
33-
A fundamental pie chart where each slice represents a category's share of the whole,
34-
ideal for showing composition and distribution across a small number of categories.
35-
36-
Args:
37-
data: Input DataFrame containing the data to plot.
38-
category: Column name for category labels (slice names).
39-
value: Column name for numeric values (slice sizes).
40-
title: Plot title. Defaults to None.
41-
colors: Custom color palette for slices. Defaults to PyPlots.ai palette.
42-
startangle: Starting angle for first slice in degrees. Defaults to 90.
43-
show_labels: Whether to show percentage labels on slices. Defaults to True.
44-
label_format: Format string for percentage labels. Defaults to ".1%".
45-
legend: Whether to display legend. Defaults to True.
46-
legend_loc: Legend location ('right', 'left', 'top', 'bottom'). Defaults to 'right'.
47-
inner_radius: Inner radius for donut style (0 for solid pie). Defaults to 0.
48-
outer_radius: Outer radius of the pie. Defaults to 150.
49-
**kwargs: Additional parameters.
50-
51-
Returns:
52-
Altair Chart object.
53-
54-
Raises:
55-
ValueError: If data is empty or values contain negative numbers.
56-
KeyError: If required columns are not found in data.
57-
58-
Example:
59-
>>> data = pd.DataFrame({
60-
... 'category': ['Product A', 'Product B', 'Product C'],
61-
... 'value': [35, 25, 40]
62-
... })
63-
>>> chart = create_plot(data, 'category', 'value', title='Market Share')
64-
"""
65-
# Input validation
66-
if data.empty:
67-
raise ValueError("Data cannot be empty")
68-
69-
for col in [category, value]:
70-
if col not in data.columns:
71-
available = ", ".join(data.columns)
72-
raise KeyError(f"Column '{col}' not found. Available: {available}")
73-
74-
# Validate non-negative values
75-
if (data[value] < 0).any():
76-
raise ValueError("Pie chart values must be non-negative")
77-
78-
# Handle case where all values are zero
79-
total = data[value].sum()
80-
if total == 0:
81-
raise ValueError("Sum of values cannot be zero")
82-
83-
# Use custom colors or default palette
84-
color_palette = colors if colors is not None else PYPLOTS_COLORS
10+
# Data
11+
data = pd.DataFrame(
12+
{"category": ["Product A", "Product B", "Product C", "Product D", "Other"], "value": [35, 25, 20, 15, 5]}
13+
)
8514

86-
# Calculate the starting angle in radians (Altair uses radians, offset from 12 o'clock)
87-
# Altair's theta starts from 3 o'clock (0 degrees), so we need to adjust
88-
# To start from 12 o'clock (90 degrees from 3 o'clock), we use theta2Offset
89-
start_offset = (startangle - 90) * 3.14159 / 180
15+
# Calculate percentages for labels
16+
total = data["value"].sum()
17+
data["percentage"] = data["value"] / total
9018

91-
# Create base chart with arc mark
92-
base = alt.Chart(data).encode(
93-
theta=alt.Theta(f"{value}:Q", stack=True),
94-
color=alt.Color(
95-
f"{category}:N",
96-
scale=alt.Scale(range=color_palette),
97-
legend=alt.Legend(title=category, orient=legend_loc, labelFontSize=16, titleFontSize=16)
98-
if legend
99-
else None,
100-
),
101-
tooltip=[alt.Tooltip(f"{category}:N", title="Category"), alt.Tooltip(f"{value}:Q", title="Value")],
102-
)
103-
104-
# Create the pie/arc chart
105-
pie = base.mark_arc(
106-
innerRadius=inner_radius,
107-
outerRadius=outer_radius,
108-
stroke="#ffffff",
109-
strokeWidth=2,
110-
theta2Offset=start_offset,
111-
thetaOffset=start_offset,
112-
)
113-
114-
# Add percentage labels if requested
115-
if show_labels:
116-
# Calculate percentage for labels
117-
data_with_pct = data.copy()
118-
data_with_pct["_percentage"] = data_with_pct[value] / total
119-
120-
# Create text labels positioned at the middle of each arc
121-
text = (
122-
alt.Chart(data_with_pct)
123-
.mark_text(radius=outer_radius * 0.7, fontSize=14, fontWeight="bold", color="#FFFFFF")
124-
.encode(theta=alt.Theta(f"{value}:Q", stack=True), text=alt.Text("_percentage:Q", format=label_format))
125-
.transform_calculate(theta2Offset=str(start_offset), thetaOffset=str(start_offset))
126-
)
127-
128-
# Layer pie and text
129-
chart = alt.layer(pie, text)
130-
else:
131-
chart = pie
132-
133-
# Set chart dimensions and title
134-
chart = chart.properties(width=400, height=400)
135-
136-
if title is not None:
137-
chart = chart.properties(title=alt.TitleParams(text=title, fontSize=20, anchor="middle", fontWeight=600))
138-
139-
# Configure chart appearance
140-
chart = chart.configure_view(strokeWidth=0).configure_legend(
141-
labelFontSize=16, titleFontSize=16, symbolSize=200, padding=10
142-
)
143-
144-
return chart
145-
146-
147-
if __name__ == "__main__":
148-
# Sample data for testing
149-
sample_data = pd.DataFrame(
150-
{"category": ["Product A", "Product B", "Product C", "Product D", "Other"], "value": [35, 25, 20, 15, 5]}
151-
)
152-
153-
# Create plot
154-
fig = create_plot(sample_data, "category", "value", title="Market Share Distribution")
19+
# PyPlots.ai default color palette
20+
PYPLOTS_COLORS = ["#306998", "#FFD43B", "#DC2626", "#059669", "#8B5CF6", "#F97316"]
15521

156-
# Save
157-
fig.save("plot.png", scale_factor=2.0)
158-
print("Plot saved to plot.png")
22+
# Create pie chart using arc mark
23+
# Altair uses theta encoding for pie/arc charts
24+
base = alt.Chart(data).encode(
25+
theta=alt.Theta("value:Q", stack=True),
26+
color=alt.Color(
27+
"category:N",
28+
scale=alt.Scale(range=PYPLOTS_COLORS),
29+
legend=alt.Legend(title="Category", orient="right", labelFontSize=16, titleFontSize=16),
30+
),
31+
tooltip=[
32+
alt.Tooltip("category:N", title="Category"),
33+
alt.Tooltip("value:Q", title="Value"),
34+
alt.Tooltip("percentage:Q", title="Share", format=".1%"),
35+
],
36+
)
37+
38+
# Create the pie with arc mark
39+
pie = base.mark_arc(innerRadius=0, outerRadius=300, stroke="#ffffff", strokeWidth=2)
40+
41+
# Add percentage labels on slices
42+
text = base.mark_text(radius=200, fontSize=20, fontWeight="bold", color="#FFFFFF").encode(
43+
text=alt.Text("percentage:Q", format=".1%")
44+
)
45+
46+
# Combine pie and labels
47+
chart = alt.layer(pie, text).properties(
48+
width=800,
49+
height=800,
50+
title=alt.TitleParams(text="Market Share Distribution", fontSize=20, anchor="middle", fontWeight="bold"),
51+
)
52+
53+
# Configure chart appearance
54+
chart = chart.configure_view(strokeWidth=0).configure_legend(
55+
labelFontSize=16, titleFontSize=16, symbolSize=200, padding=20
56+
)
57+
58+
# Save - scale_factor=3 gives 2400x2400 from 800x800 base
59+
# For pie charts, square aspect ratio is more appropriate
60+
chart.save("plot.png", scale_factor=3.0)

0 commit comments

Comments
 (0)