Skip to content

Commit 14c2a63

Browse files
feat(seaborn): implement venn-labeled-items (#5373)
## Implementation: `venn-labeled-items` - python/seaborn Implements the **python/seaborn** version of `venn-labeled-items`. **File:** `plots/venn-labeled-items/implementations/python/seaborn.py` **Parent Issue:** #5364 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24923161796)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 9d02e46 commit 14c2a63

2 files changed

Lines changed: 453 additions & 0 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
""" anyplot.ai
2+
venn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items
3+
Library: seaborn 0.13.2 | Python 3.14.4
4+
Quality: 88/100 | Created: 2026-04-25
5+
"""
6+
7+
import os
8+
9+
import matplotlib.pyplot as plt
10+
import pandas as pd
11+
import seaborn as sns
12+
from matplotlib.patches import Circle
13+
14+
15+
# Theme tokens
16+
THEME = os.getenv("ANYPLOT_THEME", "light")
17+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
18+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
19+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
20+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
21+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
22+
23+
OKABE_GREEN = "#009E73"
24+
OKABE_ORANGE = "#D55E00"
25+
OKABE_BLUE = "#0072B2"
26+
27+
sns.set_theme(
28+
style="white",
29+
rc={
30+
"figure.facecolor": PAGE_BG,
31+
"axes.facecolor": PAGE_BG,
32+
"axes.edgecolor": PAGE_BG,
33+
"axes.labelcolor": INK,
34+
"text.color": INK,
35+
"font.family": "serif",
36+
},
37+
)
38+
39+
# Data
40+
circles = [
41+
{"name": "Overhyped", "color": OKABE_GREEN, "center": (0.00, 0.55)},
42+
{"name": "Actually Useful", "color": OKABE_ORANGE, "center": (-0.476, -0.275)},
43+
{"name": "Secretly Loved", "color": OKABE_BLUE, "center": (0.476, -0.275)},
44+
]
45+
46+
zone_items = {
47+
"A": ["NFTs", "Metaverse", "Web3"],
48+
"B": ["Spreadsheets", "Calculators"],
49+
"C": ["Karaoke", "Bob Ross"],
50+
"AB": ["Crypto", "ChatGPT"],
51+
"AC": ["TikTok", "Pumpkin Spice"],
52+
"BC": ["Google Maps", "Dolly Parton", "IKEA Meatballs"],
53+
"ABC": ["Sourdough"],
54+
"outside": ["Jury Duty"],
55+
}
56+
57+
zone_centroids = {
58+
"A": (0.00, 1.05),
59+
"B": (-1.00, -0.62),
60+
"C": (1.00, -0.62),
61+
"AB": (-0.55, 0.32),
62+
"AC": (0.55, 0.32),
63+
"BC": (0.00, -0.65),
64+
"ABC": (0.00, 0.05),
65+
"outside": (-1.95, 1.30),
66+
}
67+
68+
# Build a long-form items dataframe so we can use seaborn for the marker layer.
69+
rows = []
70+
spacing = 0.20
71+
for zone, labels in zone_items.items():
72+
cx, cy = zone_centroids[zone]
73+
n = len(labels)
74+
start_y = cy + (n - 1) * spacing / 2
75+
for i, label in enumerate(labels):
76+
rows.append(
77+
{
78+
"zone": zone,
79+
"label": label,
80+
"x": cx,
81+
"y": start_y - i * spacing,
82+
"is_outside": zone == "outside",
83+
"is_triple": zone == "ABC",
84+
}
85+
)
86+
items_df = pd.DataFrame(rows)
87+
88+
# Plot — square canvas suits the symmetric Venn layout
89+
fig, ax = plt.subplots(figsize=(12, 12), facecolor=PAGE_BG)
90+
ax.set_facecolor(PAGE_BG)
91+
ax.set_aspect("equal")
92+
93+
r = 0.85
94+
for c in circles:
95+
ax.add_patch(Circle(c["center"], radius=r, facecolor=c["color"], alpha=0.20, edgecolor=c["color"], linewidth=2.0))
96+
97+
# Subtle focal-point highlight on the triple-overlap zone for editorial emphasis
98+
ax.add_patch(
99+
Circle(
100+
zone_centroids["ABC"],
101+
radius=0.13,
102+
facecolor=INK,
103+
alpha=0.05,
104+
edgecolor=INK_SOFT,
105+
linewidth=0.8,
106+
linestyle=(0, (2, 2)),
107+
)
108+
)
109+
110+
# Seaborn scatter layer: small dot markers anchor every item.
111+
# Different palette + size for the focal triple-overlap zone (DE-03 storytelling).
112+
regular = items_df[~items_df["is_triple"]]
113+
triple = items_df[items_df["is_triple"]]
114+
sns.scatterplot(data=regular, x="x", y="y", color=INK_SOFT, s=18, alpha=0.55, edgecolor="none", legend=False, ax=ax)
115+
sns.scatterplot(
116+
data=triple, x="x", y="y", color=INK, s=44, alpha=0.85, edgecolor=PAGE_BG, linewidth=1.2, legend=False, ax=ax
117+
)
118+
119+
# Category labels (outside each circle, on the outer side)
120+
ax.text(
121+
0,
122+
0.55 + r + 0.06,
123+
circles[0]["name"],
124+
ha="center",
125+
va="bottom",
126+
fontsize=24,
127+
fontweight="bold",
128+
color=circles[0]["color"],
129+
clip_on=False,
130+
)
131+
ax.text(
132+
-1.40,
133+
-0.275 - 0.80,
134+
circles[1]["name"],
135+
ha="right",
136+
va="top",
137+
fontsize=24,
138+
fontweight="bold",
139+
color=circles[1]["color"],
140+
clip_on=False,
141+
)
142+
ax.text(
143+
1.40,
144+
-0.275 - 0.80,
145+
circles[2]["name"],
146+
ha="left",
147+
va="top",
148+
fontsize=24,
149+
fontweight="bold",
150+
color=circles[2]["color"],
151+
clip_on=False,
152+
)
153+
154+
# Item text labels — slightly above their dot markers
155+
label_offset = 0.055
156+
for _, row in items_df.iterrows():
157+
is_triple = row["is_triple"]
158+
is_outside = row["is_outside"]
159+
ax.text(
160+
row["x"],
161+
row["y"] + label_offset,
162+
row["label"],
163+
ha="center",
164+
va="bottom",
165+
fontsize=20 if is_triple else 17,
166+
fontweight="bold" if is_triple else "normal",
167+
color=INK_MUTED if is_outside else INK,
168+
style="italic" if is_outside else "normal",
169+
clip_on=False,
170+
)
171+
172+
# Hint label for the "outside" stack
173+
ax.text(
174+
-1.95, 1.55, "(outside all)", ha="center", va="bottom", fontsize=14, color=INK_MUTED, style="italic", clip_on=False
175+
)
176+
177+
# Title (mandatory format) + editorial subtitle
178+
fig.suptitle("venn-labeled-items · seaborn · anyplot.ai", fontsize=26, fontweight="medium", color=INK, y=0.965)
179+
fig.text(
180+
0.5,
181+
0.918,
182+
"Tech & Trends — a Chartgeist taxonomy of what we love, use, and overrate",
183+
ha="center",
184+
va="top",
185+
fontsize=18,
186+
color=INK_SOFT,
187+
style="italic",
188+
)
189+
190+
ax.set_xlim(-2.35, 2.35)
191+
ax.set_ylim(-2.05, 1.85)
192+
ax.axis("off")
193+
194+
plt.savefig(f"plot-{THEME}.png", dpi=200, bbox_inches="tight", facecolor=PAGE_BG)

0 commit comments

Comments
 (0)