|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | waterfall-basic: Basic Waterfall Chart |
3 | | -Library: letsplot 4.8.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-24 |
| 3 | +Library: letsplot 4.9.0 | Python 3.13.13 |
| 4 | +Quality: 94/100 | Updated: 2026-05-06 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import pandas as pd |
8 | 10 | from lets_plot import ( |
9 | 11 | LetsPlot, |
10 | 12 | aes, |
11 | 13 | element_blank, |
| 14 | + element_rect, |
12 | 15 | element_text, |
13 | 16 | geom_rect, |
14 | 17 | geom_segment, |
|
27 | 30 |
|
28 | 31 | LetsPlot.setup_html() |
29 | 32 |
|
| 33 | +# Theme tokens |
| 34 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 35 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 36 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 37 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 38 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 39 | + |
| 40 | +# Okabe-Ito palette |
| 41 | +BRAND = "#009E73" # Position 1 - green |
| 42 | +ACCENT_1 = "#D55E00" # Position 2 - orange |
| 43 | +ACCENT_2 = "#0072B2" # Position 3 - blue |
| 44 | + |
30 | 45 | # Data - Quarterly financial breakdown from revenue to net income |
31 | 46 | categories = [ |
32 | 47 | "Starting Balance", |
|
37 | 52 | "Taxes", |
38 | 53 | "Net Profit", |
39 | 54 | ] |
40 | | -values = [50000, 35000, 18000, -22000, -8000, -12000, 0] # Last is placeholder for total |
| 55 | +values = [50000, 35000, 18000, -22000, -8000, -12000, 0] |
41 | 56 |
|
42 | 57 | # Calculate waterfall positions |
43 | 58 | running_total = 0 |
|
109 | 124 | + geom_rect( |
110 | 125 | data=df, |
111 | 126 | mapping=aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="color_type"), |
112 | | - color="white", |
113 | | - size=1, |
| 127 | + color=INK_SOFT, |
| 128 | + size=0.8, |
114 | 129 | ) |
115 | 130 | # Connector lines between bars |
116 | 131 | + geom_segment( |
117 | 132 | data=connector_df, |
118 | 133 | mapping=aes(x="x_start", xend="x_end", y="y", yend="y"), |
119 | | - color="#555555", |
120 | | - size=1, |
| 134 | + color=INK_SOFT, |
| 135 | + size=0.6, |
121 | 136 | linetype="dashed", |
122 | 137 | ) |
123 | 138 | # Value labels on bars |
124 | | - + geom_text(data=df, mapping=aes(x="x_pos", y="label_y", label="label"), color="white", size=12, fontface="bold") |
125 | | - # Custom colors: green for positive, red for negative, Python Blue for totals |
| 139 | + + geom_text(data=df, mapping=aes(x="x_pos", y="label_y", label="label"), color=INK, size=12, fontface="bold") |
| 140 | + # Colors: Okabe-Ito palette |
126 | 141 | + scale_fill_manual( |
127 | | - values={"positive": "#22C55E", "negative": "#EF4444", "total": "#306998"}, |
| 142 | + values={"positive": BRAND, "negative": ACCENT_1, "total": ACCENT_2}, |
128 | 143 | name="Change Type", |
129 | 144 | labels={"positive": "Increase", "negative": "Decrease", "total": "Total"}, |
130 | 145 | ) |
|
133 | 148 | # Y axis |
134 | 149 | + scale_y_continuous(format="${,.0f}") |
135 | 150 | # Labels |
136 | | - + labs(title="waterfall-basic · letsplot · pyplots.ai", x="", y="Amount ($)") |
| 151 | + + labs(title="waterfall-basic · letsplot · anyplot.ai", x="", y="Amount ($)") |
137 | 152 | # Theme |
138 | 153 | + theme_minimal() |
139 | 154 | + theme( |
140 | | - plot_title=element_text(size=24, hjust=0.5), |
141 | | - axis_title=element_text(size=20), |
142 | | - axis_text_x=element_text(size=14, angle=30), |
143 | | - axis_text_y=element_text(size=16), |
144 | | - legend_title=element_text(size=18), |
145 | | - legend_text=element_text(size=16), |
146 | | - legend_position="right", |
| 155 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 156 | + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 157 | + panel_grid_major_y=element_rect(color=INK_SOFT, size=0.4, linetype="solid"), |
147 | 158 | panel_grid_major_x=element_blank(), |
| 159 | + panel_grid_minor=element_blank(), |
| 160 | + plot_title=element_text(size=24, color=INK), |
| 161 | + axis_title=element_text(size=20, color=INK), |
| 162 | + axis_text_x=element_text(size=14, color=INK_SOFT, angle=30), |
| 163 | + axis_text_y=element_text(size=16, color=INK_SOFT), |
| 164 | + legend_title=element_text(size=18, color=INK), |
| 165 | + legend_text=element_text(size=16, color=INK_SOFT), |
| 166 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), |
| 167 | + legend_position="right", |
148 | 168 | ) |
149 | 169 | + ggsize(1600, 900) |
150 | 170 | ) |
151 | 171 |
|
152 | 172 | # Save as PNG (scale 3x for 4800x2700) |
153 | | -ggsave(plot, "plot.png", path=".", scale=3) |
| 173 | +ggsave(plot, f"plot-{THEME}.png", path=".", scale=3) |
154 | 174 |
|
155 | 175 | # Save as HTML for interactivity |
156 | | -ggsave(plot, "plot.html", path=".") |
| 176 | +ggsave(plot, f"plot-{THEME}.html", path=".") |
0 commit comments