{"spec_id":"line-multi","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nline-multi: Multi-Line Comparison Plot\nLibrary: letsplot 4.11.0 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-08-05\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# Imprint palette (first 3 categorical positions)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data: monthly sales for 3 product lines over 12 months\nnp.random.seed(42)\nmonths = np.arange(1, 13)\nmonth_labels = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\n# Realistic sales trajectories with distinct shapes\nelectronics = 150 + np.cumsum(np.random.randn(12) * 8) + np.linspace(0, 40, 12)\nclothing = 120 + np.cumsum(np.random.randn(12) * 6) + np.sin(np.linspace(0, 2 * np.pi, 12)) * 20\nhome_goods = 90 + np.cumsum(np.random.randn(12) * 5) + np.linspace(0, 25, 12)\n\n# Long-format DataFrame for ggplot grammar\ndf = pd.DataFrame(\n    {\n        \"Month\": np.tile(months, 3),\n        \"MonthLabel\": np.tile(month_labels, 3),\n        \"Sales\": np.concatenate([electronics, clothing, home_goods]),\n        \"Product Line\": np.repeat([\"Electronics\", \"Clothing\", \"Home Goods\"], 12),\n    }\n)\n\n# Split so Electronics (top performer) can be drawn with stronger visual weight\nelectronics_df = df[df[\"Product Line\"] == \"Electronics\"]\nother_df = df[df[\"Product Line\"] != \"Electronics\"]\n\n# Distinctive lets-plot feature: interactive per-point tooltips (custom formatting,\n# not just a ggplot2 port) surfaced in the saved HTML.\nline_tooltips = layer_tooltips().line(\"^color\").line(\"@MonthLabel|@Sales\").format(\"@Sales\", \"{,.0f}k USD\")\n\nplot = (\n    ggplot(df, aes(x=\"Month\", y=\"Sales\", color=\"Product Line\"))\n    + geom_line(data=other_df, size=0.9, alpha=0.55, tooltips=line_tooltips)\n    + geom_point(data=other_df, size=2.5, alpha=0.55, tooltips=line_tooltips)\n    + geom_line(data=electronics_df, size=2.2, alpha=1.0, tooltips=line_tooltips)\n    + geom_point(data=electronics_df, size=4.5, alpha=1.0, tooltips=line_tooltips)\n    + scale_color_manual(values=IMPRINT, breaks=[\"Electronics\", \"Clothing\", \"Home Goods\"])\n    + scale_x_continuous(breaks=months.tolist(), labels=month_labels)\n    + labs(title=\"line-multi · python · letsplot · anyplot.ai\", x=\"Month\", y=\"Sales (thousands USD)\")\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_blank(),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=12, color=INK, face=\"plain\"),\n        axis_text=element_text(size=10, color=INK_SOFT),\n        plot_title=element_text(size=16, color=INK, face=\"plain\"),\n        legend_title=element_text(size=10, color=INK),\n        legend_text=element_text(size=10, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n    )\n    + ggsize(800, 450)\n)\n\n# Save PNG (scale 4x for canonical 3200 x 1800 px)\nggsave(plot, f\"plot-{THEME}.png\", scale=4, path=\".\")\n\n# Save HTML for interactivity (custom tooltips)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}