{"spec_id":"network-hierarchical","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nnetwork-hierarchical: Hierarchical Network Graph with Tree Layout\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_size_identity,\n    theme,\n    xlim,\n    ylim,\n)\n\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\"]\n\n# Data: Software company organizational hierarchy (22 employees, 4 levels)\nnodes = [\n    # Level 0 - CEO\n    {\"id\": 0, \"label\": \"CEO\", \"level\": 0},\n    # Level 1 - VPs (3 people)\n    {\"id\": 1, \"label\": \"VP Engineering\", \"level\": 1},\n    {\"id\": 2, \"label\": \"VP Product\", \"level\": 1},\n    {\"id\": 3, \"label\": \"VP Operations\", \"level\": 1},\n    # Level 2 - Directors/Managers (6 people)\n    {\"id\": 4, \"label\": \"Frontend Dir\", \"level\": 2},\n    {\"id\": 5, \"label\": \"Backend Dir\", \"level\": 2},\n    {\"id\": 6, \"label\": \"PM Lead\", \"level\": 2},\n    {\"id\": 7, \"label\": \"UX Lead\", \"level\": 2},\n    {\"id\": 8, \"label\": \"IT Manager\", \"level\": 2},\n    {\"id\": 9, \"label\": \"HR Manager\", \"level\": 2},\n    # Level 3 - Team Members (12 people)\n    {\"id\": 10, \"label\": \"FE Dev 1\", \"level\": 3},\n    {\"id\": 11, \"label\": \"FE Dev 2\", \"level\": 3},\n    {\"id\": 12, \"label\": \"BE Dev 1\", \"level\": 3},\n    {\"id\": 13, \"label\": \"BE Dev 2\", \"level\": 3},\n    {\"id\": 14, \"label\": \"PM 1\", \"level\": 3},\n    {\"id\": 15, \"label\": \"Designer\", \"level\": 3},\n    {\"id\": 16, \"label\": \"UX Rsrch\", \"level\": 3},\n    {\"id\": 17, \"label\": \"SysAdmin\", \"level\": 3},\n    {\"id\": 18, \"label\": \"DevOps\", \"level\": 3},\n    {\"id\": 19, \"label\": \"Recruiter\", \"level\": 3},\n    {\"id\": 20, \"label\": \"Payroll\", \"level\": 3},\n    {\"id\": 21, \"label\": \"Benefits\", \"level\": 3},\n]\n\nedges = [\n    # CEO to VPs\n    (0, 1),\n    (0, 2),\n    (0, 3),\n    # VP Engineering to Directors\n    (1, 4),\n    (1, 5),\n    # VP Product to Leads\n    (2, 6),\n    (2, 7),\n    # VP Operations to Managers\n    (3, 8),\n    (3, 9),\n    # Directors to Team Members\n    (4, 10),\n    (4, 11),\n    (5, 12),\n    (5, 13),\n    (6, 14),\n    (7, 15),\n    (7, 16),\n    (8, 17),\n    (8, 18),\n    (9, 19),\n    (9, 20),\n    (9, 21),\n]\n\n# Compute hierarchical layout positions\nlevels = {}\nfor node in nodes:\n    lvl = node[\"level\"]\n    if lvl not in levels:\n        levels[lvl] = []\n    levels[lvl].append(node)\n\npositions = {}\ny_spacing = 0.22\nfor lvl in sorted(levels.keys()):\n    nodes_at_level = levels[lvl]\n    n = len(nodes_at_level)\n    if n > 1:\n        x_positions = [0.05 + i * (0.90 / (n - 1)) for i in range(n)]\n    else:\n        x_positions = [0.5]\n    y_pos = 0.90 - lvl * y_spacing\n    for i, node in enumerate(nodes_at_level):\n        positions[node[\"id\"]] = (x_positions[i], y_pos)\n\n# Level names for legend\nlevel_names = {0: \"Level 0: CEO\", 1: \"Level 1: VPs\", 2: \"Level 2: Directors\", 3: \"Level 3: Team\"}\n\n# Map level names to Okabe-Ito colors\nlevel_colors = {\n    \"Level 0: CEO\": IMPRINT[0],\n    \"Level 1: VPs\": IMPRINT[1],\n    \"Level 2: Directors\": IMPRINT[2],\n    \"Level 3: Team\": IMPRINT[3],\n}\n\n# Node sizes by level\nsize_map = {0: 16, 1: 12, 2: 9, 3: 6}\n\n# Create node dataframe\nnode_df = pd.DataFrame(\n    {\n        \"x\": [positions[node[\"id\"]][0] for node in nodes],\n        \"y\": [positions[node[\"id\"]][1] for node in nodes],\n        \"label\": [node[\"label\"] for node in nodes],\n        \"level\": [level_names[node[\"level\"]] for node in nodes],\n        \"size\": [size_map[node[\"level\"]] for node in nodes],\n    }\n)\n\n# Create edge dataframe\nedge_data = []\nfor parent, child in edges:\n    edge_data.append(\n        {\"x\": positions[parent][0], \"y\": positions[parent][1], \"xend\": positions[child][0], \"yend\": positions[child][1]}\n    )\nedge_df = pd.DataFrame(edge_data)\n\n# Create the plot\nplot = (\n    ggplot()\n    # Draw edges first (underneath nodes)\n    + geom_segment(\n        data=edge_df, mapping=aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\"), color=INK_SOFT, size=1.2, alpha=0.7\n    )\n    # Draw nodes colored by level using Okabe-Ito palette\n    + geom_point(data=node_df, mapping=aes(x=\"x\", y=\"y\", color=\"level\", size=\"size\"), alpha=0.95, stroke=0.5)\n    # Add node labels with offset\n    + geom_text(data=node_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), size=9, va=\"bottom\", nudge_y=0.025, color=INK)\n    + scale_color_manual(values=level_colors)\n    + scale_size_identity()\n    + labs(title=\"network-hierarchical · plotnine · anyplot.ai\", color=\"Hierarchy Level\")\n    + xlim(-0.02, 1.02)\n    + ylim(0.18, 1.0)\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=24, ha=\"center\", color=INK),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_position=\"bottom\",\n        legend_box_margin=10,\n        legend_margin=5,\n        legend_key=element_rect(fill=ELEVATED_BG),\n        # Remove axis elements for network graph\n        axis_title=element_blank(),\n        axis_text=element_blank(),\n        axis_ticks=element_blank(),\n        panel_grid=element_blank(),\n    )\n)\n\n# Save the plot\nplot.save(f\"plot-{THEME}.png\", dpi=300)\n"}