{"spec_id":"parliament-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nparliament-basic: Parliament Seat Chart\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-17\n\"\"\"\n# ruff: noqa: F405\n\"\"\"anyplot.ai\nparliament-basic: Parliament Seat Chart\nLibrary: letsplot | Python 3.13\nQuality: pending | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette - first series ALWAYS #009E73\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Data - Board of Directors composition (neutral, non-political)\nparties = [\n    \"Finance Committee\",\n    \"Technology Board\",\n    \"Operations Division\",\n    \"Research Council\",\n    \"Marketing Team\",\n    \"Legal Advisory\",\n]\nseats = [85, 72, 58, 35, 95, 55]\n\ntotal_seats = sum(seats)\nn_rows = 5\n\n# Calculate seat positions in semicircular arrangement\nrow_weights = np.array([i + 1 for i in range(n_rows)])\nseats_per_row = (row_weights / row_weights.sum() * total_seats).astype(int)\nseats_per_row[-1] += total_seats - seats_per_row.sum()  # Adjust for rounding\n\nx_positions = []\ny_positions = []\nparty_labels = []\n\nparty_index = 0\nremaining_in_party = seats[0]\n\nfor row_idx, row_seats in enumerate(seats_per_row):\n    radius = 0.5 + row_idx * 0.12  # Radius increases for outer rows\n    angles = np.linspace(np.pi, 0, row_seats)  # Semicircle from left to right\n\n    for angle in angles:\n        x = radius * np.cos(angle)\n        y = radius * np.sin(angle)\n        x_positions.append(x)\n        y_positions.append(y)\n        party_labels.append(parties[party_index])\n\n        remaining_in_party -= 1\n        if remaining_in_party == 0 and party_index < len(seats) - 1:\n            party_index += 1\n            remaining_in_party = seats[party_index]\n\n# Create DataFrame\ndf = pd.DataFrame({\"x\": x_positions, \"y\": y_positions, \"party\": party_labels})\n\n# Create legend labels with seat counts\nparty_seat_counts = dict(zip(parties, seats, strict=True))\ndf[\"party_label\"] = df[\"party\"].apply(lambda p: f\"{p} ({party_seat_counts[p]})\")\n\n# Create color mapping (Okabe-Ito in order)\ncolor_map = {\n    f\"{p} ({s})\": IMPRINT[i]\n    for i, (p, s) in enumerate(zip(parties, seats, strict=True))\n}\ncolor_values = [color_map[label] for label in df[\"party_label\"].unique()]\n\n# Theme-adaptive styling\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    axis_title=element_text(color=INK),\n    axis_text=element_text(color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=24, hjust=0.5),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT),\n    legend_title=element_text(color=INK),\n    plot_margin=[40, 40, 40, 40],\n)\n\n# Build the plot\nplot = (\n    ggplot(df, aes(x=\"x\", y=\"y\", color=\"party_label\"))\n    + geom_point(size=5, alpha=0.9)\n    + scale_color_manual(values=color_values)\n    + coord_fixed(ratio=1)\n    + theme_void()\n    + anyplot_theme\n    + labs(title=\"parliament-basic · letsplot · anyplot.ai\", color=\"Division (Seats)\")\n    + ggsize(1600, 900)\n)\n\n# Add majority line annotation (horizontal line at y=0)\nplot = plot + geom_hline(yintercept=0, color=INK_SOFT, size=1.5, alpha=0.4)\n\n# Add total seats annotation\nannotation_df = pd.DataFrame(\n    {\"x\": [0], \"y\": [-0.15], \"label\": [f\"Total: {total_seats} seats | Majority: {total_seats // 2 + 1}\"]}\n)\nplot = plot + geom_text(data=annotation_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), size=14, color=INK_SOFT)\n\n# Save as PNG and HTML with theme suffix\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}