import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import font_manager
import eunoia
from matplotlib_venn import venn3, venn3_circles
from matplotlib_set_diagrams import EulerDiagram
fp = "Font" if os.path.isdir("Font") else "../Font"
for f in font_manager.findSystemFonts(fontpaths=[fp]):
font_manager.fontManager.addfont(f)
plt.rcParams["font.family"] = "Inter"
RED, BLUE, GREEN, GRY, INK = "#e4573a", "#3a7ca5", "#3c8c57", "#8a8a8a", "#242424"
# venn3 order: A-only, B-only, AB, C-only, AC, BC, ABC ; each set = 82, each pair = 22, centre = 2
d1 = (40, 40, 20, 40, 20, 20, 2)
codes = ["100", "010", "110", "001", "101", "011", "111"]
regmap = {"100": (0,), "010": (1,), "110": (0, 1), "001": (2,), "101": (0, 2), "011": (1, 2), "111": (0, 1, 2)}
counter = [0]
def gene():
counter[0] += 1
return "g%05d" % counter[0]
sets = [set(), set(), set()]
for code, cnt in zip(codes, d1):
members = [gene() for _ in range(cnt)]
for k in regmap[code]:
sets[k].update(members)
excl = {"A": 40, "B": 40, "C": 40, "A&B": 20, "A&C": 20, "B&C": 20, "A&B&C": 2}
fig, ax = plt.subplots(2, 2, figsize=(11, 10))
for a in ax.ravel():
a.set_aspect("equal"); a.axis("off")
# A - matplotlib_venn (circles)
v = venn3(subsets=d1, set_labels=("", "", ""), set_colors=(RED, BLUE, GREEN), alpha=0.45, ax=ax[0, 0])
for t in v.subset_labels:
if t:
t.set_fontsize(12)
# B - matplotlib-set-diagrams Euler (circles)
EulerDiagram.from_sets(sets, set_labels=["", "", ""], set_colors=[RED, BLUE, GREEN], ax=ax[0, 1])
# C - true areas as bubbles: circle area = count, so the centre "2" becomes a tiny dot
a = ax[1, 0]
v2 = venn3(subsets=d1, set_labels=("", "", ""), ax=a)
for code in codes:
p = v2.get_patch_by_id(code)
if p:
p.set_visible(False)
rc = venn3_circles(subsets=d1, ax=a, color="#bbbbbb", lw=1.0)[0].radius
for code, cnt in zip(codes, d1):
lab = v2.get_label_by_id(code)
pos = lab.get_position()
lab.set_text("")
ones = code.count("1")
a.add_patch(Circle(pos, rc * np.sqrt(cnt / 82.0),
facecolor=(RED if ones == 3 else BLUE if ones == 2 else GRY),
alpha=0.85, edgecolor="black", lw=0.6, zorder=5))
a.text(pos[0], pos[1], str(cnt), ha="center", va="center", fontsize=9,
color="white" if ones >= 2 else "black", zorder=6)
a.relim(); a.autoscale_view(); a.margins(0.15)
# D - eunoia ellipses (reshape) with counts; seed fixed for a clean, symmetric layout
fit = eunoia.euler(excl, input="exclusive", shape="ellipse", seed=0, n_restarts=40)
fit.plot(ax=ax[1, 1], quantities=True, labels=False, colors=[RED, BLUE, GREEN])
for a, letter in zip(ax.ravel(), "ABCD"):
a.text(0.03, 0.97, letter, transform=a.transAxes, fontsize=20, fontweight="bold", color=INK, va="top", ha="left")
plt.show()