{"spec_id":"scatter-hr-diagram","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nscatter-hr-diagram: Hertzsprung-Russell Diagram\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent this file from shadowing the plotnine library when run by name\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nif _this_dir in sys.path:\n    sys.path.remove(_this_dir)\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_smooth,\n    geom_text,\n    ggplot,\n    guide_legend,\n    guides,\n    labs,\n    scale_color_manual,\n    scale_x_reverse,\n    scale_y_log10,\n    theme,\n    theme_minimal,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\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\n# Data — Imprint palette not used here: spectral type colors follow domain convention\n# (blue for O/B, white/cyan for A, yellow-white for F, golden for G, orange K, red M)\nnp.random.seed(42)\n\n# Main sequence: L ~ T^3.5 (approx), with scatter\nn_main = 250\nmain_log_temp = np.random.uniform(np.log10(2800), np.log10(38000), n_main)\nmain_temp = 10**main_log_temp\nmain_log_lum = 3.5 * (main_log_temp - np.log10(5778)) + np.random.normal(0, 0.15, n_main)\nmain_lum = 10**main_log_lum\n\n# Red giants (cool but luminous)\nn_giants = 60\ngiant_temp = np.random.uniform(3200, 5200, n_giants)\ngiant_lum = 10 ** np.random.uniform(1.0, 3.0, n_giants)\n\n# Supergiants (very luminous, broad temp range)\nn_super = 25\nsuper_temp = np.random.uniform(3500, 28000, n_super)\nsuper_lum = 10 ** np.random.uniform(3.5, 5.5, n_super)\n\n# White dwarfs (hot but very dim)\nn_wd = 40\nwd_temp = np.random.uniform(5000, 30000, n_wd)\nwd_lum = 10 ** np.random.uniform(-4, -1.5, n_wd)\n\n# Conventional spectral-type palette — all 7 types visually distinct on both themes\nspectral_colors = {\n    \"O\": \"#3949AB\",  # medium indigo (> 30 000 K) — visible on dark bg\n    \"B\": \"#1E88E5\",  # medium blue (10 000 – 30 000 K)\n    \"A\": \"#80DEEA\",  # light cyan (7 500 – 10 000 K) — clearly different from B\n    \"F\": \"#FFD600\",  # bright yellow (6 000 – 7 500 K) — visible on cream, different from G\n    \"G\": \"#FF8F00\",  # deep amber (5 200 – 6 000 K) — clearly different from F\n    \"K\": \"#E65100\",  # deep orange (3 700 – 5 200 K)\n    \"M\": \"#C62828\",  # crimson red (< 3 700 K)\n}\n\ntemperatures = np.concatenate([main_temp, giant_temp, super_temp, wd_temp])\nluminosities = np.concatenate([main_lum, giant_lum, super_lum, wd_lum])\nregions = [\"Main Sequence\"] * n_main + [\"Red Giants\"] * n_giants + [\"Supergiants\"] * n_super + [\"White Dwarfs\"] * n_wd\n\nspectral_bins = [0, 3700, 5200, 6000, 7500, 10000, 30000, np.inf]\nspectral_labels = [\"M\", \"K\", \"G\", \"F\", \"A\", \"B\", \"O\"]\nspectral = pd.cut(temperatures, bins=spectral_bins, labels=spectral_labels, ordered=False)\n\ndf = pd.DataFrame(\n    {\n        \"temperature\": temperatures,\n        \"luminosity\": luminosities,\n        \"region\": regions,\n        \"spectral_type\": pd.Categorical(spectral, categories=[\"O\", \"B\", \"A\", \"F\", \"G\", \"K\", \"M\"]),\n    }\n)\n\n# Sun reference\nsun = pd.DataFrame({\"temperature\": [5778], \"luminosity\": [1.0], \"label\": [\"Sun\"]})\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"temperature\", y=\"luminosity\", color=\"spectral_type\"))\n    + geom_point(size=2.5, alpha=0.65, stroke=0.3)\n    + geom_smooth(\n        data=df[df[\"region\"] == \"Main Sequence\"],\n        mapping=aes(x=\"temperature\", y=\"luminosity\"),\n        method=\"lm\",\n        color=INK_SOFT,\n        fill=INK_SOFT,\n        alpha=0.12,\n        size=0.7,\n        inherit_aes=False,\n    )\n    + geom_point(\n        data=sun,\n        mapping=aes(x=\"temperature\", y=\"luminosity\"),\n        color=INK,\n        fill=\"#FFD700\",\n        size=6,\n        shape=\"D\",\n        stroke=1.2,\n        inherit_aes=False,\n    )\n    + geom_text(\n        data=sun,\n        mapping=aes(x=\"temperature\", y=\"luminosity\", label=\"label\"),\n        color=INK,\n        size=3,\n        nudge_x=3000,\n        nudge_y=0.5,\n        inherit_aes=False,\n        fontweight=\"bold\",\n    )\n    + annotate(\n        \"text\",\n        x=9000,\n        y=0.12,\n        label=\"Main Sequence\",\n        color=INK_MUTED,\n        size=3,\n        fontstyle=\"italic\",\n        fontweight=\"bold\",\n        alpha=0.9,\n    )\n    + annotate(\n        \"text\",\n        x=3100,\n        y=2000,\n        label=\"Red Giants\",\n        color=INK_MUTED,\n        size=3,\n        fontstyle=\"italic\",\n        fontweight=\"bold\",\n        alpha=0.9,\n    )\n    + annotate(\n        \"text\",\n        x=14000,\n        y=250000,\n        label=\"Supergiants\",\n        color=INK_MUTED,\n        size=3,\n        fontstyle=\"italic\",\n        fontweight=\"bold\",\n        alpha=0.9,\n    )\n    + annotate(\n        \"text\",\n        x=22000,\n        y=0.0005,\n        label=\"White Dwarfs\",\n        color=INK_MUTED,\n        size=3,\n        fontstyle=\"italic\",\n        fontweight=\"bold\",\n        alpha=0.9,\n    )\n    + scale_x_reverse()\n    + scale_y_log10()\n    + scale_color_manual(values=spectral_colors, name=\"Spectral Type\")\n    + labs(\n        x=\"Surface Temperature (K)\", y=\"Luminosity (L☉)\", title=\"scatter-hr-diagram · python · plotnine · anyplot.ai\"\n    )\n    + guides(color=guide_legend(override_aes={\"size\": 4, \"alpha\": 1}))\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_title=element_text(size=12, weight=\"bold\", color=INK, margin={\"b\": 8}),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        legend_title=element_text(size=9, weight=\"bold\", color=INK),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_position=\"right\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT, size=0.4),\n        legend_key=element_rect(fill=PAGE_BG, color=\"none\"),\n        panel_grid_minor=element_blank(),\n        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_background=element_rect(fill=PAGE_BG, color=\"none\"),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_border=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        plot_margin=0.04,\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}