{"spec_id":"scatter-complex-plane","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nscatter-complex-plane: Complex Plane Visualization (Argand Diagram)\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\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 palette — first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\nsns.set_theme(\n    style=\"white\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data — 3rd roots of unity, their sum, and arbitrary points across all quadrants\nroots_of_unity = [np.exp(2j * np.pi * k / 3) for k in range(3)]\nroot_sum = sum(roots_of_unity)\n\narbitrary_points = [2.5 + 1.5j, -1.8 + 2.2j, 1.0 - 2.0j, -2.5 - 1.0j, 0.5 + 2.8j, -1.2 - 2.5j, 3.0 + 0.0j]\n\nall_points = roots_of_unity + [root_sum] + arbitrary_points\n\nlabels = (\n    [f\"$\\\\omega_{k}$\" for k in range(3)]\n    + [\"$\\\\Sigma\\\\omega_k$\"]\n    + [f\"$z_{{{i + 1}}}$\" for i in range(len(arbitrary_points))]\n)\n\ncategories = [\"Roots of Unity\"] * 3 + [\"Sum of Roots\"] * 1 + [\"Arbitrary Points\"] * len(arbitrary_points)\nmagnitudes = [abs(z) for z in all_points]\n\ndf = pd.DataFrame(\n    {\n        \"real\": [z.real for z in all_points],\n        \"imaginary\": [z.imag for z in all_points],\n        \"label\": labels,\n        \"category\": categories,\n        \"magnitude\": magnitudes,\n    }\n)\n\ncat_colors = {\n    \"Roots of Unity\": IMPRINT_PALETTE[0],  # #009E73 green — first series\n    \"Sum of Roots\": IMPRINT_PALETTE[1],  # #C475FD lavender\n    \"Arbitrary Points\": IMPRINT_PALETTE[2],  # #4467A3 blue\n}\nmarkers = {\"Roots of Unity\": \"D\", \"Sum of Roots\": \"X\", \"Arbitrary Points\": \"o\"}\n\n# JointGrid — square canvas (2400×2400 px) suits the equal-aspect complex plane\ng = sns.JointGrid(data=df, x=\"real\", y=\"imaginary\", height=6, ratio=6, space=0.15)\ng.figure.set_dpi(400)\ng.figure.patch.set_facecolor(PAGE_BG)\n\n# Main scatter with hue, style, and size encoding\nsns.scatterplot(\n    data=df,\n    x=\"real\",\n    y=\"imaginary\",\n    hue=\"category\",\n    style=\"category\",\n    size=\"magnitude\",\n    sizes=(150, 450),\n    markers=markers,\n    palette=cat_colors,\n    edgecolor=PAGE_BG,\n    linewidth=1.5,\n    zorder=5,\n    ax=g.ax_joint,\n    legend=\"full\",\n)\n\n# Marginal KDE plots — distinctively seaborn, adds statistical context\nfor cat, color in cat_colors.items():\n    subset = df[df[\"category\"] == cat]\n    sns.kdeplot(\n        data=subset, x=\"real\", color=color, fill=True, alpha=0.3, linewidth=1.5, ax=g.ax_marg_x, warn_singular=False\n    )\n    sns.kdeplot(\n        data=subset,\n        y=\"imaginary\",\n        color=color,\n        fill=True,\n        alpha=0.3,\n        linewidth=1.5,\n        ax=g.ax_marg_y,\n        warn_singular=False,\n    )\n\n# Marginal axes styling\ng.ax_marg_x.set_xlabel(\"\")\ng.ax_marg_x.set_ylabel(\"\")\ng.ax_marg_y.set_xlabel(\"\")\ng.ax_marg_y.set_ylabel(\"\")\ng.ax_marg_x.tick_params(left=False, labelleft=False)\ng.ax_marg_y.tick_params(bottom=False, labelbottom=False)\ng.ax_marg_x.set_facecolor(PAGE_BG)\ng.ax_marg_y.set_facecolor(PAGE_BG)\n\nax = g.ax_joint\nax.set_facecolor(PAGE_BG)\n\n# Vectors from origin to each point\nfor _, row in df.iterrows():\n    color = cat_colors[row[\"category\"]]\n    ax.annotate(\n        \"\",\n        xy=(row[\"real\"], row[\"imaginary\"]),\n        xytext=(0, 0),\n        arrowprops={\"arrowstyle\": \"->\", \"color\": color, \"lw\": 1.8, \"alpha\": 0.5},\n    )\n\n# Annotations with rectangular form — offsets in points from the data point\noffsets = {\n    \"$\\\\omega_0$\": (20, -28),\n    \"$\\\\omega_1$\": (-95, -35),  # lowered to clear the upper-left legend\n    \"$\\\\omega_2$\": (-95, -26),\n    \"$\\\\Sigma\\\\omega_k$\": (15, -55),  # lower-right of origin, away from z3\n    \"$z_{2}$\": (16, -30),  # below data point to avoid upper-left legend\n    \"$z_{3}$\": (45, 16),  # shifted right to separate from Σωₖ annotation\n    \"$z_{4}$\": (-85, 16),  # above-left to avoid ω₂ annotation in lower-left\n    \"$z_{7}$\": (-90, 16),  # rightmost point — move label left to avoid right marginal\n}\n\nfor _, row in df.iterrows():\n    r = row[\"real\"]\n    i_val = row[\"imaginary\"]\n    r_str = \"0\" if abs(r) < 0.01 else f\"{r:.1f}\"\n    if abs(i_val) < 0.01:\n        rect_form = f\"{r_str}+0.0i\"\n    elif i_val >= 0:\n        rect_form = f\"{r_str}+{i_val:.1f}i\"\n    else:\n        rect_form = f\"{r_str}{i_val:.1f}i\"\n\n    offset = offsets.get(row[\"label\"], (16, 16))\n    ax.annotate(\n        f\"{row['label']}\\n{rect_form}\",\n        xy=(r, i_val),\n        xytext=offset,\n        textcoords=\"offset points\",\n        fontsize=9,\n        color=INK,\n        fontweight=\"medium\",\n        bbox={\n            \"boxstyle\": \"round,pad=0.3\",\n            \"facecolor\": ELEVATED_BG,\n            \"edgecolor\": INK_SOFT,\n            \"alpha\": 0.9,\n            \"linewidth\": 0.5,\n        },\n        zorder=6,\n    )\n\n# Equilateral triangle connecting roots of unity\nroot_reals = [z.real for z in roots_of_unity] + [roots_of_unity[0].real]\nroot_imags = [z.imag for z in roots_of_unity] + [roots_of_unity[0].imag]\nax.plot(root_reals, root_imags, ls=\"-\", color=IMPRINT_PALETTE[0], lw=2.0, alpha=0.4, zorder=3)\n\n# Unit circle — dashed reference at r=1\ntheta = np.linspace(0, 2 * np.pi, 200)\nax.plot(np.cos(theta), np.sin(theta), ls=\"--\", color=INK_MUTED, lw=1.8, alpha=0.8, label=\"Unit Circle\")\n\n# Style\nax.set_aspect(\"equal\")\n\ntitle = \"scatter-complex-plane · python · seaborn · anyplot.ai\"\nn = len(title)\ntitle_fontsize = max(8, round(12 * (67 / n if n > 67 else 1.0)))\n# Reserve top margin so suptitle sits above the top marginal without clipping\ng.figure.subplots_adjust(top=0.90)\ng.figure.suptitle(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, y=0.97)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\n\nax.axhline(0, color=INK_SOFT, lw=1.0, zorder=0)\nax.axvline(0, color=INK_SOFT, lw=1.0, zorder=0)\n\nsns.despine(ax=ax, left=True, bottom=True)\nsns.despine(ax=g.ax_marg_x, left=True, bottom=True)\nsns.despine(ax=g.ax_marg_y, left=True, bottom=True)\n\nax.xaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\n\nlimit = 3.8\nax.set_xlim(-limit, limit)\nax.set_ylim(-limit, limit)\n\nax.set_xlabel(\"Re(z)\", fontsize=10, labelpad=8, color=INK)\nax.set_ylabel(\"Im(z)\", fontsize=10, labelpad=8, color=INK)\n\n# Legend — keep category + unit circle entries, remove magnitude size entries\nhandles, leg_labels = ax.get_legend_handles_labels()\nkeep = []\nfor handle, lbl in zip(handles, leg_labels, strict=False):\n    if lbl not in {\"magnitude\", \"\", \"category\"}:\n        try:\n            float(lbl)\n        except ValueError:\n            keep.append((handle, lbl))\nfiltered_handles, filtered_labels = zip(*keep, strict=False) if keep else ([], [])\nax.legend(\n    filtered_handles,\n    filtered_labels,\n    fontsize=8,\n    loc=\"upper left\",\n    framealpha=0.95,\n    edgecolor=INK_SOFT,\n    fancybox=True,\n    labelcolor=INK,\n)\n\n# Save — bbox_inches must stay default (None) to preserve exact 2400×2400 canvas\nplt.savefig(f\"plot-{THEME}.png\", dpi=400)\n"}