{"spec_id":"funnel-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nfunnel-basic: Basic Funnel Chart\nLibrary: bokeh 3.9.0 | Python 3.14.4\nQuality: 86/100 | Updated: 2026-04-26\n\"\"\"\n\nfrom bokeh.io import export_png, output_file, save\nfrom bokeh.models import Label, Legend, LegendItem\nfrom bokeh.plotting import figure\n\n\n# Data - Sales funnel stages (from spec example)\nstages = [\"Awareness\", \"Interest\", \"Consideration\", \"Intent\", \"Purchase\"]\nvalues = [1000, 600, 400, 200, 100]\n\n# Colors - distinct for each stage (Python Blue first, then colorblind-safe palette)\ncolors = [\"#306998\", \"#FFD43B\", \"#E74C3C\", \"#9B59B6\", \"#27AE60\"]\n\n# Calculate widths proportional to values (relative to first/largest value)\nmax_value = values[0]\nwidths = [v / max_value for v in values]\n\n# Funnel parameters\nfunnel_height = 2.0  # Total height of funnel\nstage_height = funnel_height / len(stages)\ncenter_x = 0.5  # Center of funnel\n\n# Build funnel segments as patches (trapezoids)\n# Each segment connects the width at its top to the width at its bottom\nsegment_coords = []\nfor i in range(len(stages)):\n    top_width = widths[i]\n    # Bottom width is next stage width, or 50% of current for last stage\n    bottom_width = widths[i + 1] if i < len(stages) - 1 else widths[i] * 0.5\n\n    top_y = funnel_height - i * stage_height\n    bottom_y = top_y - stage_height\n\n    # Trapezoid coordinates (clockwise from top-left)\n    xs = [\n        center_x - top_width / 2,  # top-left\n        center_x + top_width / 2,  # top-right\n        center_x + bottom_width / 2,  # bottom-right\n        center_x - bottom_width / 2,  # bottom-left\n    ]\n    ys = [top_y, top_y, bottom_y, bottom_y]\n    segment_coords.append((xs, ys))\n\n# Create figure (4800 x 2700 px)\np = figure(\n    width=4800,\n    height=2700,\n    title=\"funnel-basic · bokeh · pyplots.ai\",\n    x_range=(-0.3, 1.3),\n    y_range=(-0.3, 2.3),\n    tools=\"\",\n    toolbar_location=None,\n)\n\n# Draw funnel segments and collect renderers for legend\nrenderers = []\nfor (xs, ys), color in zip(segment_coords, colors, strict=True):\n    r = p.patch(xs, ys, fill_color=color, line_color=\"white\", line_width=4, alpha=0.9)\n    renderers.append(r)\n\n# Add labels on each segment (stage name and value/percentage)\nfor i, (stage, value) in enumerate(zip(stages, values, strict=True)):\n    xs, ys = segment_coords[i]\n    # Label position: center of trapezoid\n    center_y = (ys[0] + ys[2]) / 2\n\n    # Calculate percentage relative to first stage\n    percentage = (value / max_value) * 100\n\n    # Stage name label (dark text for yellow, white for others)\n    text_color = \"#333333\" if colors[i] == \"#FFD43B\" else \"white\"\n    stage_label = Label(\n        x=center_x,\n        y=center_y + 0.06,\n        text=stage,\n        text_font_size=\"28pt\",\n        text_font_style=\"bold\",\n        text_color=text_color,\n        text_align=\"center\",\n        text_baseline=\"middle\",\n    )\n    p.add_layout(stage_label)\n\n    # Value label\n    value_label = Label(\n        x=center_x,\n        y=center_y - 0.06,\n        text=f\"{value:,} ({percentage:.0f}%)\",\n        text_font_size=\"22pt\",\n        text_color=text_color,\n        text_align=\"center\",\n        text_baseline=\"middle\",\n    )\n    p.add_layout(value_label)\n\n# Create legend\nlegend_items = [LegendItem(label=f\"{stages[i]}: {values[i]:,}\", renderers=[renderers[i]]) for i in range(len(stages))]\nlegend = Legend(\n    items=legend_items,\n    location=\"center_right\",\n    label_text_font_size=\"24pt\",\n    glyph_width=40,\n    glyph_height=40,\n    spacing=20,\n    padding=30,\n    background_fill_alpha=0.9,\n    border_line_color=\"#cccccc\",\n)\np.add_layout(legend, \"right\")\n\n# Styling\np.title.text_font_size = \"36pt\"\np.title.align = \"center\"\n\n# Hide axes and grid for funnel chart\np.axis.visible = False\np.grid.visible = False\np.outline_line_color = None\n\n# Save outputs\nexport_png(p, filename=\"plot.png\")\n\n# Also save interactive HTML\noutput_file(\"plot.html\")\nsave(p)\n"}