|
1 | 1 | """ anyplot.ai |
2 | 2 | lollipop-basic: Basic Lollipop Chart |
3 | | -Library: plotnine 0.15.3 | Python 3.14.4 |
4 | | -Quality: 85/100 | Updated: 2026-04-26 |
| 3 | +Library: plotnine 0.15.7 | Python 3.13.14 |
| 4 | +Quality: 84/100 | Updated: 2026-07-01 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import os |
| 8 | +import sys |
| 9 | + |
| 10 | + |
| 11 | +# Prevent circular import: remove this script's directory from sys.path so |
| 12 | +# "from plotnine import ..." resolves to the installed library, not this file. |
| 13 | +_here = os.path.dirname(os.path.abspath(__file__)) |
| 14 | +sys.path = [p for p in sys.path if os.path.abspath(p) != _here] |
8 | 15 |
|
9 | 16 | import pandas as pd |
10 | 17 | from plotnine import ( |
11 | 18 | aes, |
| 19 | + element_blank, |
12 | 20 | element_line, |
13 | 21 | element_rect, |
14 | 22 | element_text, |
15 | 23 | geom_point, |
16 | 24 | geom_segment, |
| 25 | + geom_text, |
17 | 26 | ggplot, |
18 | 27 | ggsave, |
19 | 28 | labs, |
| 29 | + scale_size_continuous, |
20 | 30 | theme, |
21 | 31 | theme_minimal, |
22 | 32 | ) |
|
28 | 38 | INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
29 | 39 | BRAND = "#009E73" |
30 | 40 |
|
31 | | -# Data - Product sales by category, sorted by value |
| 41 | +# Product sales by category, sorted ascending for ranking narrative |
32 | 42 | data = { |
33 | 43 | "category": [ |
34 | 44 | "Electronics", |
|
48 | 58 | df = pd.DataFrame(data) |
49 | 59 | df = df.sort_values("value", ascending=True).reset_index(drop=True) |
50 | 60 | df["category"] = pd.Categorical(df["category"], categories=df["category"], ordered=True) |
| 61 | +df["label"] = df["value"].astype(str) + "k" |
51 | 62 |
|
52 | | -# Plot |
53 | 63 | plot = ( |
54 | 64 | ggplot(df, aes(x="category", y="value")) |
55 | | - + geom_segment(aes(x="category", xend="category", y=0, yend="value"), color=BRAND, size=1.5) |
56 | | - + geom_point(color=BRAND, size=6, fill=BRAND) |
57 | | - + labs(x="Product Category", y="Sales (thousands $)", title="lollipop-basic · plotnine · anyplot.ai") |
| 65 | + + geom_segment(aes(x="category", xend="category", y=0, yend="value"), color=BRAND, size=0.6) |
| 66 | + + geom_point(aes(size="value"), color=BRAND, fill=BRAND, show_legend=False) |
| 67 | + + geom_text( |
| 68 | + aes(label="label"), |
| 69 | + color=INK_SOFT, |
| 70 | + size=2.8, # geom_text size is in mm (~2.8mm ≈ 8pt at dpi=400) |
| 71 | + nudge_y=16, |
| 72 | + va="bottom", |
| 73 | + ) |
| 74 | + + scale_size_continuous(range=[2, 7]) |
| 75 | + + labs(x="Product Category", y="Sales (thousands $)", title="lollipop-basic · python · plotnine · anyplot.ai") |
58 | 76 | + theme_minimal() |
59 | 77 | + theme( |
60 | | - figure_size=(16, 9), |
| 78 | + figure_size=(8, 4.5), |
61 | 79 | plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
62 | 80 | panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
63 | | - text=element_text(size=14, color=INK), |
64 | | - axis_title=element_text(size=20, color=INK), |
65 | | - axis_text=element_text(size=16, color=INK_SOFT), |
| 81 | + panel_border=element_blank(), |
| 82 | + text=element_text(size=7, color=INK), |
| 83 | + axis_title=element_text(size=10, color=INK), |
| 84 | + axis_text=element_text(size=8, color=INK_SOFT), |
66 | 85 | axis_text_x=element_text(angle=45, ha="right", color=INK_SOFT), |
67 | 86 | axis_line=element_line(color=INK_SOFT), |
68 | | - plot_title=element_text(size=24, color=INK), |
69 | | - panel_grid_minor=element_line(alpha=0), |
70 | | - panel_grid_major_x=element_line(alpha=0), |
| 87 | + plot_title=element_text(size=12, color=INK), |
| 88 | + panel_grid_minor=element_blank(), |
| 89 | + panel_grid_major_x=element_blank(), |
71 | 90 | panel_grid_major_y=element_line(color=INK, alpha=0.15, size=0.3), |
72 | 91 | ) |
73 | 92 | ) |
74 | 93 |
|
75 | | -ggsave(plot, filename=f"plot-{THEME}.png", dpi=300, width=16, height=9) |
| 94 | +ggsave(plot, filename=f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in") |
0 commit comments