|
7 | 7 | import pandas as pd |
8 | 8 |
|
9 | 9 |
|
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 | +) |
85 | 14 |
|
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 |
90 | 18 |
|
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"] |
155 | 21 |
|
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