{"spec_id":"venn-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nvenn-basic: Venn Diagram\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 83/100 | Updated: 2026-05-11\n\"\"\"\n\nimport math\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_polygon,\n    geom_text,\n    ggplot,\n    labs,\n    scale_fill_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette - first three colors for three-set Venn\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Three overlapping sets representing skills in a tech team\n# Set A: Python developers (100 people)\n# Set B: Data Scientists (80 people)\n# Set C: ML Engineers (60 people)\n# Overlaps: A∩B=30, A∩C=20, B∩C=25, A∩B∩C=10\n\nset_labels = [\"Python\\nDevelopers\", \"Data\\nScientists\", \"ML\\nEngineers\"]\n# Region counts (exclusive to each region)\n# A only: 100 - 30 - 20 + 10 = 60\n# B only: 80 - 30 - 25 + 10 = 35\n# C only: 60 - 20 - 25 + 10 = 25\n# A∩B only: 30 - 10 = 20\n# A∩C only: 20 - 10 = 10\n# B∩C only: 25 - 10 = 15\n# A∩B∩C: 10\n\nregion_counts = {\"A_only\": 60, \"B_only\": 35, \"C_only\": 25, \"AB_only\": 20, \"AC_only\": 10, \"BC_only\": 15, \"ABC\": 10}\n\n# Circle positions for 3-set Venn diagram\n# Circles arranged in a triangle formation with significant overlap\nradius = 5\noffset = radius * 0.6  # Controls overlap amount\nn_points = 100\n\n# Circle centers\ncenters = {\n    \"A\": (-offset, offset * 0.5),  # Top-left\n    \"B\": (offset, offset * 0.5),  # Top-right\n    \"C\": (0, -offset * 0.9),  # Bottom\n}\n\n# Create circle polygons\ncircle_rows = []\ncircle_id = 0\nfor label, (cx, cy) in centers.items():\n    angles = np.linspace(0, 2 * math.pi, n_points + 1)\n    x_coords = cx + radius * np.cos(angles)\n    y_coords = cy + radius * np.sin(angles)\n    for i in range(len(x_coords)):\n        circle_rows.append({\"x\": x_coords[i], \"y\": y_coords[i], \"circle_id\": circle_id, \"set\": label})\n    circle_id += 1\n\ncircle_df = pd.DataFrame(circle_rows)\n\n# Calculate positions for region labels\n# A only - left side of circle A\nlabel_A_only = {\n    \"x\": centers[\"A\"][0] - radius * 0.45,\n    \"y\": centers[\"A\"][1] + radius * 0.2,\n    \"label\": str(region_counts[\"A_only\"]),\n}\n\n# B only - right side of circle B\nlabel_B_only = {\n    \"x\": centers[\"B\"][0] + radius * 0.45,\n    \"y\": centers[\"B\"][1] + radius * 0.2,\n    \"label\": str(region_counts[\"B_only\"]),\n}\n\n# C only - bottom of circle C\nlabel_C_only = {\"x\": centers[\"C\"][0], \"y\": centers[\"C\"][1] - radius * 0.5, \"label\": str(region_counts[\"C_only\"])}\n\n# AB intersection (top center)\nlabel_AB = {\"x\": 0, \"y\": centers[\"A\"][1] + radius * 0.35, \"label\": str(region_counts[\"AB_only\"])}\n\n# AC intersection (bottom-left)\nlabel_AC = {\n    \"x\": centers[\"A\"][0] + radius * 0.35,\n    \"y\": (centers[\"A\"][1] + centers[\"C\"][1]) / 2 - radius * 0.1,\n    \"label\": str(region_counts[\"AC_only\"]),\n}\n\n# BC intersection (bottom-right)\nlabel_BC = {\n    \"x\": centers[\"B\"][0] - radius * 0.35,\n    \"y\": (centers[\"B\"][1] + centers[\"C\"][1]) / 2 - radius * 0.1,\n    \"label\": str(region_counts[\"BC_only\"]),\n}\n\n# ABC intersection (center)\ncentroid_x = (centers[\"A\"][0] + centers[\"B\"][0] + centers[\"C\"][0]) / 3\ncentroid_y = (centers[\"A\"][1] + centers[\"B\"][1] + centers[\"C\"][1]) / 3\nlabel_ABC = {\"x\": centroid_x, \"y\": centroid_y, \"label\": str(region_counts[\"ABC\"])}\n\n# Create label dataframes\ncount_labels_df = pd.DataFrame([label_A_only, label_B_only, label_C_only, label_AB, label_AC, label_BC, label_ABC])\n\n# Set name labels - positioned outside circles\nset_name_labels = [\n    {\"x\": centers[\"A\"][0] - radius * 0.8, \"y\": centers[\"A\"][1] + radius * 0.9, \"label\": set_labels[0]},\n    {\"x\": centers[\"B\"][0] + radius * 0.8, \"y\": centers[\"B\"][1] + radius * 0.9, \"label\": set_labels[1]},\n    {\"x\": centers[\"C\"][0], \"y\": centers[\"C\"][1] - radius * 1.1, \"label\": set_labels[2]},\n]\nset_name_df = pd.DataFrame(set_name_labels)\n\n# Create color mapping dict for sets\ncolor_map = {\"A\": IMPRINT[0], \"B\": IMPRINT[1], \"C\": IMPRINT[2]}\n\n# Plot\nplot = (\n    ggplot()\n    # Draw circles with transparency for overlap visibility\n    + geom_polygon(\n        aes(x=\"x\", y=\"y\", fill=\"set\", group=\"circle_id\"), data=circle_df, alpha=0.45, color=INK_SOFT, size=1.5\n    )\n    # Region count labels (larger, bold)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=count_labels_df, size=18, fontweight=\"bold\", color=INK)\n    # Set name labels (outside circles)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=set_name_df, size=14, fontweight=\"bold\", color=INK_SOFT)\n    # Colors using Okabe-Ito palette\n    + scale_fill_manual(values=color_map)\n    # Axis scaling for proper aspect ratio\n    + scale_x_continuous(limits=(-12, 12))\n    + scale_y_continuous(limits=(-12, 10))\n    # Title\n    + labs(title=\"venn-basic · plotnine · anyplot.ai\")\n    # Theme with proper sizing and colors\n    + theme(\n        figure_size=(12, 12),\n        plot_title=element_text(size=24, ha=\"center\", color=INK, fontweight=\"medium\"),\n        axis_title=element_blank(),\n        axis_text=element_blank(),\n        axis_ticks=element_blank(),\n        axis_line=element_blank(),\n        panel_grid_major=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_background=element_rect(fill=PAGE_BG, color=None),\n        plot_background=element_rect(fill=PAGE_BG, color=None),\n        legend_position=\"none\",\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300)\n"}