{"spec_id":"network-weighted","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nnetwork-weighted: Weighted Network Graph with Edge Thickness\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    guide_legend,\n    guides,\n    labs,\n    scale_size_continuous,\n    theme,\n    theme_void,\n)\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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# Data - Trade network between countries (billions USD annual trade volume)\nnp.random.seed(42)\n\n# Define nodes (countries)\nnodes = pd.DataFrame(\n    {\n        \"id\": [\n            \"USA\",\n            \"China\",\n            \"Germany\",\n            \"Japan\",\n            \"UK\",\n            \"France\",\n            \"Canada\",\n            \"Mexico\",\n            \"S.Korea\",\n            \"India\",\n            \"Brazil\",\n            \"Australia\",\n        ],\n        \"group\": [\n            \"Americas\",\n            \"Asia\",\n            \"Europe\",\n            \"Asia\",\n            \"Europe\",\n            \"Europe\",\n            \"Americas\",\n            \"Americas\",\n            \"Asia\",\n            \"Asia\",\n            \"Americas\",\n            \"Oceania\",\n        ],\n    }\n)\n\n# Define edges (trade relationships with weights in billions USD)\nedges_data = [\n    (\"USA\", \"China\", 580),\n    (\"USA\", \"Canada\", 620),\n    (\"USA\", \"Mexico\", 550),\n    (\"USA\", \"Japan\", 180),\n    (\"USA\", \"Germany\", 160),\n    (\"USA\", \"UK\", 130),\n    (\"USA\", \"S.Korea\", 140),\n    (\"China\", \"Japan\", 280),\n    (\"China\", \"S.Korea\", 240),\n    (\"China\", \"Germany\", 170),\n    (\"China\", \"Australia\", 150),\n    (\"China\", \"India\", 90),\n    (\"Germany\", \"France\", 180),\n    (\"Germany\", \"UK\", 140),\n    (\"Germany\", \"Japan\", 45),\n    (\"France\", \"UK\", 80),\n    (\"Japan\", \"S.Korea\", 70),\n    (\"Canada\", \"Mexico\", 35),\n    (\"India\", \"UK\", 30),\n    (\"Brazil\", \"USA\", 75),\n    (\"Brazil\", \"China\", 95),\n    (\"Australia\", \"Japan\", 55),\n]\n\nedges = pd.DataFrame(edges_data, columns=[\"source\", \"target\", \"weight\"])\n\n# Create node positions using circular layout\nn_nodes = len(nodes)\nangles = np.linspace(0, 2 * np.pi, n_nodes, endpoint=False)\n\n# Arrange nodes in circular pattern with slight variation\nnode_positions = {}\nfor i, node_id in enumerate(nodes[\"id\"]):\n    radius = 4.0 + 0.3 * np.sin(i * 1.5)\n    node_positions[node_id] = (radius * np.cos(angles[i]), radius * np.sin(angles[i]))\n\n# Calculate weighted degree for node sizing (sum of connected edge weights)\nweighted_degree = {}\nfor node_id in nodes[\"id\"]:\n    total_weight = edges[(edges[\"source\"] == node_id) | (edges[\"target\"] == node_id)][\"weight\"].sum()\n    weighted_degree[node_id] = total_weight\n\n# Add positions and weighted degree to nodes DataFrame\nnodes[\"x\"] = nodes[\"id\"].map(lambda n: node_positions[n][0])\nnodes[\"y\"] = nodes[\"id\"].map(lambda n: node_positions[n][1])\nnodes[\"weighted_degree\"] = nodes[\"id\"].map(weighted_degree)\n\n# Create edges DataFrame with coordinates\nedges[\"x\"] = edges[\"source\"].map(lambda n: node_positions[n][0])\nedges[\"y\"] = edges[\"source\"].map(lambda n: node_positions[n][1])\nedges[\"xend\"] = edges[\"target\"].map(lambda n: node_positions[n][0])\nedges[\"yend\"] = edges[\"target\"].map(lambda n: node_positions[n][1])\n\n# Scale edge thickness for better visibility (1-6 range)\nweight_min, weight_max = edges[\"weight\"].min(), edges[\"weight\"].max()\nedges[\"thickness\"] = 1.0 + (edges[\"weight\"] - weight_min) / (weight_max - weight_min) * 5\n\n# Scale node size based on weighted degree (5-16 range for better visibility)\ndegree_min = nodes[\"weighted_degree\"].min()\ndegree_max = nodes[\"weighted_degree\"].max()\nnodes[\"node_size\"] = 5 + (nodes[\"weighted_degree\"] - degree_min) / (degree_max - degree_min) * 11\n\n# Create plot\nplot = (\n    ggplot()\n    # Draw edges with thickness mapped to trade weight\n    + geom_segment(\n        data=edges, mapping=aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\", size=\"weight\"), color=\"#4467A3\", alpha=0.55\n    )\n    # Draw nodes with size mapped to weighted degree - larger for better visibility\n    + geom_point(\n        data=nodes,\n        mapping=aes(x=\"x\", y=\"y\", size=\"weighted_degree\"),\n        color=INK,\n        stroke=1.5,\n        fill=\"#009E73\",\n        show_legend=False,\n    )\n    # Add node labels with offset - larger size for better legibility\n    + geom_text(data=nodes, mapping=aes(x=\"x\", y=\"y\", label=\"id\"), size=14, color=INK, fontweight=\"bold\", nudge_y=0.65)\n    # Scale edge thickness\n    + scale_size_continuous(range=(0.8, 6), name=\"Trade Volume\\n(Billions USD)\", breaks=[100, 300, 500])\n    # Labels and title - format: {spec-id} · {library} · anyplot.ai\n    + labs(\n        title=\"network-weighted · plotnine · anyplot.ai\",\n        subtitle=\"Edge thickness represents bilateral trade volume between countries\",\n    )\n    # Clean theme with no axes\n    + theme_void()\n    + theme(\n        figure_size=(16, 9),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_title=element_text(size=26, ha=\"center\", weight=\"bold\", color=INK),\n        plot_subtitle=element_text(size=18, ha=\"center\", color=INK_SOFT),\n        legend_title=element_text(size=16, color=INK),\n        legend_text=element_text(size=14, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n        plot_margin=0.05,\n    )\n    + guides(size=guide_legend(override_aes={\"alpha\": 0.8}))\n)\n\n# Save plot\nplot.save(f\"plot-{THEME}.png\", dpi=300, width=16, height=9, verbose=False)\n"}