{"spec_id":"polar-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\npolar-basic: Basic Polar Chart\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-07-24\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.cm import ScalarMappable\nfrom matplotlib.colors import LinearSegmentedColormap, Normalize\n\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\"\nBRAND = \"#009E73\"\n\n# Imprint sequential colormap (single-polarity magnitude: traffic volume)\nimprint_seq = LinearSegmentedColormap.from_list(\"imprint_seq\", [\"#009E73\", \"#4467A3\"])\n\nsns.set_theme(\n    style=\"ticks\",\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.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data: Hourly website traffic (24-hour cycle)\nnp.random.seed(42)\nhours = np.arange(0, 24)\ntheta = hours * (2 * np.pi / 24)\n\nbase_traffic = 100\nmorning_peak = 80 * np.exp(-0.5 * ((hours - 10) / 2) ** 2)\nevening_peak = 100 * np.exp(-0.5 * ((hours - 20) / 2.5) ** 2)\nnoise = np.random.normal(0, 10, 24)\ntraffic = base_traffic + morning_peak + evening_peak + noise\ntraffic = np.clip(traffic, 20, None)\n\ndf = pd.DataFrame({\"theta\": theta, \"traffic\": traffic})\n\n# Plot (square format for radial symmetry) — canonical 2400x2400 canvas\nfig, ax = plt.subplots(\n    figsize=(6, 6), dpi=400, subplot_kw={\"projection\": \"polar\"}, facecolor=PAGE_BG, layout=\"constrained\"\n)\nax.set_facecolor(PAGE_BG)\n\n# Start at top (12 o'clock), clockwise direction — set before plotting\nax.set_theta_offset(np.pi / 2)\nax.set_theta_direction(-1)\n\n# Scatter points: sns.scatterplot with continuous hue mapped through the Imprint sequential cmap\nsns.scatterplot(\n    data=df,\n    x=\"theta\",\n    y=\"traffic\",\n    hue=\"traffic\",\n    palette=imprint_seq,\n    s=160,\n    alpha=0.9,\n    ax=ax,\n    legend=False,\n    edgecolor=PAGE_BG,\n    linewidth=1.2,\n    zorder=5,\n)\n\n# Connecting line: sns.lineplot on polar axes\ntheta_closed = np.append(theta, theta[0])\ntraffic_closed = np.append(traffic, traffic[0])\ndf_line = pd.DataFrame({\"theta\": theta_closed, \"traffic\": traffic_closed})\nsns.lineplot(\n    data=df_line,\n    x=\"theta\",\n    y=\"traffic\",\n    color=BRAND,\n    linewidth=2.5,\n    alpha=0.85,\n    ax=ax,\n    sort=False,\n    estimator=None,\n    zorder=4,\n)\n\n# Fill under the polygon (no seaborn equivalent for polar fill)\nax.fill(theta_closed, traffic_closed, color=BRAND, alpha=0.12, zorder=3)\nax.set_xlabel(\"\")  # remove \"theta\" label added by sns.lineplot\nax.set_ylabel(\"\")  # remove \"traffic\" label added by sns.lineplot — replaced by custom text below\n\n# Focal-point callout on the evening peak (highest-traffic point)\npeak_idx = int(np.argmax(traffic))\npeak_theta, peak_traffic = theta[peak_idx], traffic[peak_idx]\nax.annotate(\n    f\"Peak: {peak_traffic:.0f}/hr\",\n    xy=(peak_theta, peak_traffic),\n    xytext=(peak_theta + 0.28, peak_traffic + 55),\n    fontsize=10,\n    color=INK,\n    fontweight=\"medium\",\n    ha=\"center\",\n    va=\"center\",\n    arrowprops={\"arrowstyle\": \"-\", \"color\": INK_SOFT, \"linewidth\": 0.9},\n    zorder=6,\n)\n\n# Style — fig.suptitle (centered on the whole figure) rather than ax.set_title\n# (centered on the axes only): the colorbar shifts the polar axes off-centre,\n# and an axes-centered 60-char title would run off the left edge of the canvas.\nfig.suptitle(\n    \"Website Traffic by Hour · polar-basic · python · seaborn · anyplot.ai\",\n    fontsize=11,\n    fontweight=\"medium\",\n    color=INK,\n    y=0.98,\n)\n\n# Angular labels: every 2 hours (12 labels) to reduce perimeter crowding\ntick_hours = np.arange(0, 24, 2)\ntick_theta = tick_hours * (2 * np.pi / 24)\nax.set_xticks(tick_theta)\nax.set_xticklabels([f\"{h:02d}:00\" for h in tick_hours], fontsize=10, color=INK_SOFT)\n\n# Radial range\nrmax = max(traffic) * 1.15\nax.set_ylim(0, rmax)\n\n# Radial ticks + label placed in the low-traffic wedge between the 14:00 and\n# 16:00 angular ticks — far from the title (top) and clear of neighbouring\n# angular tick text, unlike the previous \"right\"-side placement that collided\n# with the 06:00 angular tick label.\nrlabel_theta_deg = 225  # data-space theta; renders at screen angle 225 (bottom-left) via offset+direction\nax.set_rlabel_position(rlabel_theta_deg)\nax.tick_params(axis=\"y\", labelsize=9, colors=INK_SOFT)\nax.text(np.deg2rad(rlabel_theta_deg), rmax * 1.32, \"Visitors/hr\", fontsize=11, color=INK, ha=\"center\", va=\"center\")\n\n# Grid: subtle, theme-adaptive\nax.grid(True, alpha=0.12, linewidth=0.8, color=INK)\nax.spines[\"polar\"].set_color(INK_SOFT)\nax.spines[\"polar\"].set_linewidth(1.0)\n\n# Colorbar via ScalarMappable (seaborn handles point colors; we build the bar explicitly)\nnorm = Normalize(vmin=traffic.min(), vmax=traffic.max())\nsm = ScalarMappable(cmap=imprint_seq, norm=norm)\nsm.set_array([])\ncbar = plt.colorbar(sm, ax=ax, pad=0.13, shrink=0.7)\ncbar.set_label(\"Traffic Volume\", fontsize=11, color=INK)\ncbar.ax.tick_params(labelsize=9, colors=INK_SOFT)\nplt.setp(cbar.ax.yaxis.get_ticklabels(), color=INK_SOFT)\ncbar.outline.set_edgecolor(INK_SOFT)\ncbar.ax.set_facecolor(PAGE_BG)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}