{"spec_id":"network-bipartite","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nnetwork-bipartite: Bipartite Network Graph\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 85/100 | Created: 2026-05-14\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_fill_manual,\n    scale_size,\n    theme,\n    xlim,\n    ylim,\n)\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nSTUDENT_COLOR = \"#009E73\"\nCOURSE_COLOR = \"#C475FD\"\n\n# Data — student-course enrollment network\nstudents = [\"Alice\", \"Bob\", \"Carol\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Iris\", \"James\", \"Kate\", \"Leo\"]\ncourses = [\n    \"Calculus\",\n    \"Linear Algebra\",\n    \"Statistics\",\n    \"Data Structures\",\n    \"Algorithms\",\n    \"Machine Learning\",\n    \"Databases\",\n    \"Networks\",\n    \"Operating Systems\",\n    \"Computer Vision\",\n]\n\nedges_raw = [\n    (\"Alice\", \"Calculus\"),\n    (\"Alice\", \"Statistics\"),\n    (\"Alice\", \"Machine Learning\"),\n    (\"Bob\", \"Data Structures\"),\n    (\"Bob\", \"Algorithms\"),\n    (\"Bob\", \"Databases\"),\n    (\"Carol\", \"Calculus\"),\n    (\"Carol\", \"Linear Algebra\"),\n    (\"Carol\", \"Statistics\"),\n    (\"David\", \"Machine Learning\"),\n    (\"David\", \"Computer Vision\"),\n    (\"David\", \"Networks\"),\n    (\"Emma\", \"Statistics\"),\n    (\"Emma\", \"Machine Learning\"),\n    (\"Emma\", \"Data Structures\"),\n    (\"Frank\", \"Algorithms\"),\n    (\"Frank\", \"Operating Systems\"),\n    (\"Frank\", \"Databases\"),\n    (\"Grace\", \"Calculus\"),\n    (\"Grace\", \"Linear Algebra\"),\n    (\"Henry\", \"Machine Learning\"),\n    (\"Henry\", \"Networks\"),\n    (\"Henry\", \"Computer Vision\"),\n    (\"Iris\", \"Data Structures\"),\n    (\"Iris\", \"Algorithms\"),\n    (\"Iris\", \"Operating Systems\"),\n    (\"James\", \"Calculus\"),\n    (\"James\", \"Statistics\"),\n    (\"Kate\", \"Machine Learning\"),\n    (\"Kate\", \"Statistics\"),\n    (\"Kate\", \"Linear Algebra\"),\n    (\"Leo\", \"Databases\"),\n    (\"Leo\", \"Networks\"),\n    (\"Leo\", \"Operating Systems\"),\n]\n\n# Compute node degrees\nstudent_degree = dict.fromkeys(students, 0)\ncourse_degree = dict.fromkeys(courses, 0)\nfor s, c in edges_raw:\n    student_degree[s] += 1\n    course_degree[c] += 1\n\n# Vertical positions: students at x=0, courses at x=1 (vertically centered)\nn_students = len(students)\nn_courses = len(courses)\ncourse_offset = (n_students - 1 - (n_courses - 1)) / 2.0  # = 1.0\n\nstudent_y = {s: float(i) for i, s in enumerate(students)}\ncourse_y = {c: float(i) + course_offset for i, c in enumerate(courses)}\n\n# Edge dataframe\nedges_df = pd.DataFrame(edges_raw, columns=[\"student\", \"course\"])\nedges_df[\"x\"] = 0.0\nedges_df[\"y\"] = edges_df[\"student\"].map(student_y)\nedges_df[\"xend\"] = 1.0\nedges_df[\"yend\"] = edges_df[\"course\"].map(course_y)\n\n# Node dataframes (separate for label positioning)\nstudent_nodes = pd.DataFrame(\n    {\n        \"name\": students,\n        \"x\": 0.0,\n        \"x_label\": -0.06,\n        \"y\": [student_y[s] for s in students],\n        \"group\": \"Students\",\n        \"degree\": [student_degree[s] for s in students],\n    }\n)\ncourse_nodes = pd.DataFrame(\n    {\n        \"name\": courses,\n        \"x\": 1.0,\n        \"x_label\": 1.06,\n        \"y\": [course_y[c] for c in courses],\n        \"group\": \"Courses\",\n        \"degree\": [course_degree[c] for c in courses],\n    }\n)\nnodes_df = pd.concat([student_nodes, course_nodes], ignore_index=True)\n\n# Theme\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_blank(),\n    panel_grid_minor=element_blank(),\n    axis_title=element_blank(),\n    axis_text=element_blank(),\n    axis_ticks=element_blank(),\n    axis_line=element_blank(),\n    plot_title=element_text(color=INK, size=24),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK, size=16),\n)\n\ntitle = \"network-bipartite · letsplot · anyplot.ai\"\n\n# Plot\nplot = (\n    ggplot()\n    + geom_segment(\n        data=edges_df, mapping=aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\"), color=INK_MUTED, alpha=0.3, size=0.5\n    )\n    + geom_point(data=nodes_df, mapping=aes(x=\"x\", y=\"y\", fill=\"group\", size=\"degree\"), color=PAGE_BG, shape=21)\n    + geom_text(data=student_nodes, mapping=aes(x=\"x_label\", y=\"y\", label=\"name\"), hjust=1, color=INK, size=13)\n    + geom_text(data=course_nodes, mapping=aes(x=\"x_label\", y=\"y\", label=\"name\"), hjust=0, color=INK, size=13)\n    + scale_fill_manual(values={\"Students\": STUDENT_COLOR, \"Courses\": COURSE_COLOR})\n    + scale_size(range=[5, 12], name=\"Connections\")\n    + labs(title=title, fill=\"Group\")\n    + anyplot_theme\n    + ggsize(1600, 900)\n    + xlim(-0.45, 1.45)\n    + ylim(-0.5, float(n_students) - 0.5)\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}