{"spec_id":"wordcloud-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nwordcloud-basic: Basic Word Cloud\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-08-04\n\"\"\"\n\nimport os\n\nimport plotly.graph_objects as go\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"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\"\n\n# Imprint categorical palette (colorblind-safe, canonical order) — one color per tool category\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#2ABCCD\"]\n\n# Data - data-science tool ecosystem, grouped into categories (encoded via color)\n# word: (frequency, category index, x, y) — positions solved by a bounding-box\n# nearest-to-center packing pass so no two words overlap on the 800x450 canvas\nword_data = {\n    \"NumPy\": (95, 0, 0.500, 0.498),\n    \"Pandas\": (92, 0, 0.500, 0.677),\n    \"SQL\": (92, 0, 0.500, 0.320),\n    \"Git\": (88, 3, 0.393, 0.326),\n    \"TensorFlow\": (88, 1, 0.500, 0.843),\n    \"PyTorch\": (85, 1, 0.500, 0.154),\n    \"Scikit-learn\": (82, 1, 0.728, 0.333),\n    \"Matplotlib\": (80, 2, 0.267, 0.498),\n    \"AWS\": (80, 4, 0.638, 0.498),\n    \"Jupyter\": (78, 2, 0.298, 0.646),\n    \"SciPy\": (78, 0, 0.674, 0.646),\n    \"Plotly\": (75, 2, 0.688, 0.191),\n    \"XGBoost\": (75, 1, 0.298, 0.179),\n    \"Google Cloud\": (75, 4, 0.185, 0.357),\n    \"Apache Spark\": (72, 3, 0.736, 0.056),\n    \"Keras\": (70, 1, 0.739, 0.498),\n    \"Docker\": (70, 3, 0.275, 0.781),\n    \"Tableau\": (68, 2, 0.730, 0.775),\n    \"Azure\": (68, 4, 0.300, 0.050),\n    \"Power BI\": (65, 2, 0.736, 0.892),\n    \"Kubernetes\": (62, 3, 0.253, 0.898),\n    \"OpenCV\": (60, 4, 0.500, 0.025),\n    \"Airflow\": (58, 3, 0.528, 0.972),\n    \"Hugging Face\": (55, 1, 0.832, 0.609),\n    \"Dask\": (52, 0, 0.435, 0.965),\n    \"MLflow\": (50, 3, 0.618, 0.965),\n    \"NLTK\": (48, 4, 0.402, 0.038),\n    \"Statsmodels\": (45, 0, 0.326, 0.990),\n}\ncategories = [\n    \"Data Wrangling\",\n    \"ML & Deep Learning\",\n    \"Visualization & BI\",\n    \"Data Engineering & Ops\",\n    \"Cloud & AI Toolkits\",\n]\n\n# Scale font sizes for the 800x450 logical canvas (scale=4 -> 3200x1800 source px)\nmin_size, max_size = 16, 40\nfreqs_all = [v[0] for v in word_data.values()]\nmin_freq, max_freq = min(freqs_all), max(freqs_all)\n\n# Create figure — one trace per category so the legend groups words by theme\n# and doubles as a click-to-toggle filter (Plotly's native legend interactivity)\nfig = go.Figure()\n\nfor cat_idx, cat_name in enumerate(categories):\n    entries = [(word, freq, x, y) for word, (freq, c, x, y) in word_data.items() if c == cat_idx]\n    words = [e[0] for e in entries]\n    freqs = [e[1] for e in entries]\n    xs = [e[2] for e in entries]\n    ys = [e[3] for e in entries]\n    sizes = [min_size + (f - min_freq) / (max_freq - min_freq) * (max_size - min_size) for f in freqs]\n    # Heavier weight for the most frequent tools adds a hierarchy cue beyond size alone\n    families = [\"Arial Black\" if f >= 75 else \"Arial\" for f in freqs]\n\n    fig.add_trace(\n        go.Scatter(\n            x=xs,\n            y=ys,\n            mode=\"text\",\n            text=words,\n            textfont=dict(size=sizes, family=families, color=IMPRINT[cat_idx]),\n            customdata=freqs,\n            hovertemplate=\"<b>%{text}</b><br>Frequency: %{customdata}<extra></extra>\",\n            name=cat_name,\n        )\n    )\n\n# Style — theme-adaptive chrome; axes hidden since position fills space, not a data scale\nfig.update_layout(\n    autosize=False,\n    width=800,\n    height=450,\n    title=dict(\n        text=\"wordcloud-basic · python · plotly · anyplot.ai\", font=dict(size=18, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    xaxis=dict(showgrid=False, showticklabels=False, zeroline=False, range=[-0.02, 1.02]),\n    yaxis=dict(showgrid=False, showticklabels=False, zeroline=False, range=[-0.03, 1.03]),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    margin=dict(l=30, r=30, t=80, b=25),\n    legend=dict(\n        orientation=\"h\",\n        y=-0.06,\n        x=0.5,\n        xanchor=\"center\",\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n        font=dict(color=INK_SOFT, size=11),\n    ),\n    hoverlabel=dict(bgcolor=ELEVATED_BG, font=dict(color=INK_SOFT)),\n)\n\n# Save outputs\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}