{"spec_id":"heatmap-periodic-table","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nheatmap-periodic-table: Periodic Table Property Heatmap\nLibrary: matplotlib 3.11.0 | Python 3.13.13\nQuality: 88/100 | Created: 2026-06-15\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nfrom matplotlib.colors import LinearSegmentedColormap, Normalize\n\n\n# Theme\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\n# Imprint sequential colormap for Pauling electronegativity (unipolar)\nimprint_seq = LinearSegmentedColormap.from_list(\"imprint_seq\", [\"#009E73\", \"#4467A3\"])\nGRAY_TILE = \"#C8C7C0\" if THEME == \"light\" else \"#3A3A37\"\n\n# Data — (atomic_number, symbol, group, period, pauling_electronegativity or None)\n# Lanthanides (57-71) and actinides (89-103) are listed separately below.\nELEMENTS = [\n    # Period 1\n    (1, \"H\", 1, 1, 2.20),\n    (2, \"He\", 18, 1, None),\n    # Period 2\n    (3, \"Li\", 1, 2, 0.98),\n    (4, \"Be\", 2, 2, 1.57),\n    (5, \"B\", 13, 2, 2.04),\n    (6, \"C\", 14, 2, 2.55),\n    (7, \"N\", 15, 2, 3.04),\n    (8, \"O\", 16, 2, 3.44),\n    (9, \"F\", 17, 2, 3.98),\n    (10, \"Ne\", 18, 2, None),\n    # Period 3\n    (11, \"Na\", 1, 3, 0.93),\n    (12, \"Mg\", 2, 3, 1.31),\n    (13, \"Al\", 13, 3, 1.61),\n    (14, \"Si\", 14, 3, 1.90),\n    (15, \"P\", 15, 3, 2.19),\n    (16, \"S\", 16, 3, 2.58),\n    (17, \"Cl\", 17, 3, 3.16),\n    (18, \"Ar\", 18, 3, None),\n    # Period 4\n    (19, \"K\", 1, 4, 0.82),\n    (20, \"Ca\", 2, 4, 1.00),\n    (21, \"Sc\", 3, 4, 1.36),\n    (22, \"Ti\", 4, 4, 1.54),\n    (23, \"V\", 5, 4, 1.63),\n    (24, \"Cr\", 6, 4, 1.66),\n    (25, \"Mn\", 7, 4, 1.55),\n    (26, \"Fe\", 8, 4, 1.83),\n    (27, \"Co\", 9, 4, 1.88),\n    (28, \"Ni\", 10, 4, 1.91),\n    (29, \"Cu\", 11, 4, 1.90),\n    (30, \"Zn\", 12, 4, 1.65),\n    (31, \"Ga\", 13, 4, 1.81),\n    (32, \"Ge\", 14, 4, 2.01),\n    (33, \"As\", 15, 4, 2.18),\n    (34, \"Se\", 16, 4, 2.55),\n    (35, \"Br\", 17, 4, 2.96),\n    (36, \"Kr\", 18, 4, 3.00),\n    # Period 5\n    (37, \"Rb\", 1, 5, 0.82),\n    (38, \"Sr\", 2, 5, 0.95),\n    (39, \"Y\", 3, 5, 1.22),\n    (40, \"Zr\", 4, 5, 1.33),\n    (41, \"Nb\", 5, 5, 1.60),\n    (42, \"Mo\", 6, 5, 2.16),\n    (43, \"Tc\", 7, 5, 1.90),\n    (44, \"Ru\", 8, 5, 2.20),\n    (45, \"Rh\", 9, 5, 2.28),\n    (46, \"Pd\", 10, 5, 2.20),\n    (47, \"Ag\", 11, 5, 1.93),\n    (48, \"Cd\", 12, 5, 1.69),\n    (49, \"In\", 13, 5, 1.78),\n    (50, \"Sn\", 14, 5, 1.96),\n    (51, \"Sb\", 15, 5, 2.05),\n    (52, \"Te\", 16, 5, 2.10),\n    (53, \"I\", 17, 5, 2.66),\n    (54, \"Xe\", 18, 5, 2.60),\n    # Period 6 — group 3 is a placeholder for lanthanides (57-71)\n    (55, \"Cs\", 1, 6, 0.79),\n    (56, \"Ba\", 2, 6, 0.89),\n    (72, \"Hf\", 4, 6, 1.30),\n    (73, \"Ta\", 5, 6, 1.50),\n    (74, \"W\", 6, 6, 2.36),\n    (75, \"Re\", 7, 6, 1.90),\n    (76, \"Os\", 8, 6, 2.20),\n    (77, \"Ir\", 9, 6, 2.20),\n    (78, \"Pt\", 10, 6, 2.28),\n    (79, \"Au\", 11, 6, 2.54),\n    (80, \"Hg\", 12, 6, 2.00),\n    (81, \"Tl\", 13, 6, 2.04),\n    (82, \"Pb\", 14, 6, 2.33),\n    (83, \"Bi\", 15, 6, 2.02),\n    (84, \"Po\", 16, 6, 2.00),\n    (85, \"At\", 17, 6, 2.20),\n    (86, \"Rn\", 18, 6, None),\n    # Period 7 — group 3 is a placeholder for actinides (89-103)\n    (87, \"Fr\", 1, 7, 0.70),\n    (88, \"Ra\", 2, 7, 0.90),\n    (104, \"Rf\", 4, 7, None),\n    (105, \"Db\", 5, 7, None),\n    (106, \"Sg\", 6, 7, None),\n    (107, \"Bh\", 7, 7, None),\n    (108, \"Hs\", 8, 7, None),\n    (109, \"Mt\", 9, 7, None),\n    (110, \"Ds\", 10, 7, None),\n    (111, \"Rg\", 11, 7, None),\n    (112, \"Cn\", 12, 7, None),\n    (113, \"Nh\", 13, 7, None),\n    (114, \"Fl\", 14, 7, None),\n    (115, \"Mc\", 15, 7, None),\n    (116, \"Lv\", 16, 7, None),\n    (117, \"Ts\", 17, 7, None),\n    (118, \"Og\", 18, 7, None),\n]\n\n# Lanthanides: La(57) to Lu(71), placed in f-block row at y=1.0, starting x=2\nLANTHANIDES = [\n    (57, \"La\", 1.10),\n    (58, \"Ce\", 1.12),\n    (59, \"Pr\", 1.13),\n    (60, \"Nd\", 1.14),\n    (61, \"Pm\", None),\n    (62, \"Sm\", 1.17),\n    (63, \"Eu\", None),\n    (64, \"Gd\", 1.20),\n    (65, \"Tb\", None),\n    (66, \"Dy\", 1.22),\n    (67, \"Ho\", 1.23),\n    (68, \"Er\", 1.24),\n    (69, \"Tm\", 1.25),\n    (70, \"Yb\", None),\n    (71, \"Lu\", 1.27),\n]\n\n# Actinides: Ac(89) to Lr(103), placed in f-block row at y=0.0, starting x=2\nACTINIDES = [\n    (89, \"Ac\", 1.10),\n    (90, \"Th\", 1.30),\n    (91, \"Pa\", 1.50),\n    (92, \"U\", 1.38),\n    (93, \"Np\", 1.36),\n    (94, \"Pu\", 1.28),\n    (95, \"Am\", 1.30),\n    (96, \"Cm\", 1.30),\n    (97, \"Bk\", 1.30),\n    (98, \"Cf\", 1.30),\n    (99, \"Es\", 1.30),\n    (100, \"Fm\", 1.30),\n    (101, \"Md\", 1.30),\n    (102, \"No\", 1.30),\n    (103, \"Lr\", None),\n]\n\n# Colormap normalization across all defined values\nall_en = [e[4] for e in ELEMENTS if e[4] is not None]\nall_en += [ln[2] for ln in LANTHANIDES if ln[2] is not None]\nall_en += [a[2] for a in ACTINIDES if a[2] is not None]\nvmin, vmax = min(all_en), max(all_en)\nnorm = Normalize(vmin=vmin, vmax=vmax)\n\n# Plot — landscape 3200×1800 (figsize=(8,4.5) × dpi=400)\nfig = plt.figure(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax = fig.add_axes([0.01, 0.11, 0.98, 0.84])\nax.set_facecolor(PAGE_BG)\nax.set_xlim(-0.25, 18.1)\nax.set_ylim(-0.6, 10.2)\nax.set_aspect(\"equal\")\nax.axis(\"off\")\n\nGAP = 0.06\nTILE_SIZE = 1 - GAP\n\n# Period labels (left margin)\nfor p in range(1, 8):\n    ax.text(-0.12, 10 - p + 0.5, str(p), fontsize=2.8, color=INK_MUTED, ha=\"right\", va=\"center\")\n\n# Group labels (top)\nfor g in range(1, 19):\n    ax.text(g - 0.5, 10.0, str(g), fontsize=2.5, color=INK_MUTED, ha=\"center\", va=\"bottom\")\n\n# Build combined element list: (z, sym, en, x, y) — main table, lanthanides, actinides\nall_tiles = []\nfor z, sym, grp, per, en in ELEMENTS:\n    all_tiles.append((z, sym, en, grp - 1, 10 - per))\nfor i, (z, sym, en) in enumerate(LANTHANIDES):\n    all_tiles.append((z, sym, en, 2 + i, 1.0))\nfor i, (z, sym, en) in enumerate(ACTINIDES):\n    all_tiles.append((z, sym, en, 2 + i, 0.0))\n\n# Draw all tiles in a single pass\nfor z, sym, en, x, y in all_tiles:\n    if en is not None:\n        rgba = imprint_seq(norm(en))\n        lum = 0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]\n        tile_color = rgba\n        tc = \"#1A1A17\" if lum > 0.45 else \"#F0EFE8\"\n        tiny_tc = \"#2A2A27\" if lum > 0.45 else \"#E0DFD8\"\n    else:\n        tile_color = GRAY_TILE\n        tc = INK_SOFT\n        tiny_tc = INK_MUTED\n\n    rect = mpatches.Rectangle(\n        (x + GAP / 2, y + GAP / 2), TILE_SIZE, TILE_SIZE, facecolor=tile_color, edgecolor=\"none\", zorder=2\n    )\n    ax.add_patch(rect)\n    ax.text(\n        x + GAP / 2 + 0.07,\n        y + TILE_SIZE + GAP / 2 - 0.07,\n        str(z),\n        fontsize=2.8,\n        color=tiny_tc,\n        ha=\"left\",\n        va=\"top\",\n        zorder=3,\n    )\n    ax.text(x + 0.5, y + 0.52, sym, fontsize=5.5, color=tc, ha=\"center\", va=\"center\", fontweight=\"bold\", zorder=3)\n    if en is not None:\n        ax.text(\n            x + 0.5, y + GAP / 2 + 0.11, f\"{en:.2f}\", fontsize=2.6, color=tiny_tc, ha=\"center\", va=\"bottom\", zorder=3\n        )\n\n# Placeholder tile at group 3, period 6 — marks the lanthanide series\nxp, yp = 2, 4\nrect = mpatches.Rectangle(\n    (xp + GAP / 2, yp + GAP / 2),\n    TILE_SIZE,\n    TILE_SIZE,\n    facecolor=GRAY_TILE,\n    edgecolor=INK_MUTED,\n    linewidth=0.5,\n    linestyle=\"--\",\n    zorder=2,\n)\nax.add_patch(rect)\nax.text(xp + 0.5, yp + 0.58, \"La–Lu\", fontsize=3.2, color=INK_SOFT, ha=\"center\", va=\"center\", zorder=3)\nax.text(xp + 0.5, yp + 0.30, \"57–71\", fontsize=2.5, color=INK_MUTED, ha=\"center\", va=\"center\", zorder=3)\n\n# Placeholder tile at group 3, period 7 — marks the actinide series\nxp, yp = 2, 3\nrect = mpatches.Rectangle(\n    (xp + GAP / 2, yp + GAP / 2),\n    TILE_SIZE,\n    TILE_SIZE,\n    facecolor=GRAY_TILE,\n    edgecolor=INK_MUTED,\n    linewidth=0.5,\n    linestyle=\"--\",\n    zorder=2,\n)\nax.add_patch(rect)\nax.text(xp + 0.5, yp + 0.58, \"Ac–Lr\", fontsize=3.2, color=INK_SOFT, ha=\"center\", va=\"center\", zorder=3)\nax.text(xp + 0.5, yp + 0.30, \"89–103\", fontsize=2.5, color=INK_MUTED, ha=\"center\", va=\"center\", zorder=3)\n\n# F-block row labels\nax.text(1.0, 1.5, \"Lanthanides\", fontsize=2.8, color=INK_MUTED, ha=\"center\", va=\"center\", rotation=90)\nax.text(1.0, 0.5, \"Actinides\", fontsize=2.8, color=INK_MUTED, ha=\"center\", va=\"center\", rotation=90)\n\n# Annotations — highlight the key top-right vs bottom-left electronegativity gradient\n# F (group 17, period 2) is the most electronegative; Fr (group 1, period 7) is the least.\n# Group 17, period 1 is empty — use that space for the \"highest\" label above F.\nax.annotate(\n    \"highest (3.98)\",\n    xy=(16.5, 8.94),\n    xytext=(16.5, 9.60),\n    fontsize=2.4,\n    color=INK_SOFT,\n    ha=\"center\",\n    va=\"bottom\",\n    arrowprops={\"arrowstyle\": \"-\", \"color\": INK_MUTED, \"lw\": 0.5, \"shrinkA\": 0, \"shrinkB\": 2},\n    zorder=5,\n)\n# Gap row (y=2–3) below period 7 is empty — use it for the \"lowest\" label below Fr.\nax.annotate(\n    \"lowest (0.70)\",\n    xy=(0.5, 3.06),\n    xytext=(0.5, 2.50),\n    fontsize=2.4,\n    color=INK_SOFT,\n    ha=\"center\",\n    va=\"top\",\n    arrowprops={\"arrowstyle\": \"-\", \"color\": INK_MUTED, \"lw\": 0.5, \"shrinkA\": 0, \"shrinkB\": 2},\n    zorder=5,\n)\n\n# Style — title with scaled fontsize\ntitle = \"Electronegativity · heatmap-periodic-table · python · matplotlib · anyplot.ai\"\ntitle_len = len(title)\ntitle_fs = max(8, round(12 * 67 / title_len)) if title_len > 67 else 12\nax.set_title(title, fontsize=title_fs, fontweight=\"medium\", color=INK, pad=6)\n\n# Colorbar — raised to y=0.050 so the label clears the canvas bottom\ncbar_ax = fig.add_axes([0.22, 0.050, 0.56, 0.033])\nsm = plt.cm.ScalarMappable(cmap=imprint_seq, norm=norm)\nsm.set_array([])\ncbar = fig.colorbar(sm, cax=cbar_ax, orientation=\"horizontal\")\ncbar.set_label(\"Electronegativity (Pauling scale)\", fontsize=5.5, color=INK, labelpad=3)\ncbar.set_ticks([1.0, 1.5, 2.0, 2.5, 3.0, 3.5])\ncbar.ax.tick_params(labelsize=4.5, colors=INK_SOFT, length=2, width=0.5)\ncbar.outline.set_edgecolor(INK_SOFT)\ncbar.outline.set_linewidth(0.4)\nfor label in cbar.ax.get_xticklabels():\n    label.set_color(INK_SOFT)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}