FUNNEL
Excel Usage
=FUNNEL(data, title, color_map, values, legend)
data(list[list], required): Input data (Labels, Values).title(str, optional, default: null): Chart title.color_map(str, optional, default: “viridis”): Color map for stages.values(str, optional, default: “true”): Show numeric values.legend(str, optional, default: “false”): Show legend.
Returns (object): Matplotlib Figure object (standard Python) or base64 encoded PNG string (Pyodide).
Examples
Example 1: Simple funnel chart with 4 stages
Inputs:
| data | |
|---|---|
| Awareness | 1000 |
| Interest | 600 |
| Consideration | 300 |
| Purchase | 100 |
Excel formula:
=FUNNEL({"Awareness",1000;"Interest",600;"Consideration",300;"Purchase",100})
Expected output:
"chart"
Example 2: Funnel chart with title and values
Inputs:
| data | title | values | |
|---|---|---|---|
| Stage 1 | 500 | Sales Funnel | true |
| Stage 2 | 400 | ||
| Stage 3 | 200 |
Excel formula:
=FUNNEL({"Stage 1",500;"Stage 2",400;"Stage 3",200}, "Sales Funnel", "true")
Expected output:
"chart"
Example 3: Funnel with plasma colormap
Inputs:
| data | color_map | |
|---|---|---|
| Top | 800 | plasma |
| Middle | 500 | |
| Bottom | 200 |
Excel formula:
=FUNNEL({"Top",800;"Middle",500;"Bottom",200}, "plasma")
Expected output:
"chart"
Example 4: Funnel chart with legend
Inputs:
| data | legend | values | |
|---|---|---|---|
| Lead | 1200 | true | true |
| Qualified | 800 | ||
| Closed | 300 |
Excel formula:
=FUNNEL({"Lead",1200;"Qualified",800;"Closed",300}, "true", "true")
Expected output:
"chart"
Python Code
import sys
import matplotlib
IS_PYODIDE = sys.platform == "emscripten"
if IS_PYODIDE:
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import io
import base64
import numpy as np
def funnel(data, title=None, color_map='viridis', values='true', legend='false'):
"""
Create a funnel chart for stages in a process.
See: https://matplotlib.org/stable/gallery/lines_bars_and_markers/barh.html
This example function is provided as-is without any representation of accuracy.
Args:
data (list[list]): Input data (Labels, Values).
title (str, optional): Chart title. Default is None.
color_map (str, optional): Color map for stages. Valid options: Viridis, Plasma, Inferno, Magma, Cividis. Default is 'viridis'.
values (str, optional): Show numeric values. Valid options: True, False. Default is 'true'.
legend (str, optional): Show legend. Valid options: True, False. Default is 'false'.
Returns:
object: Matplotlib Figure object (standard Python) or base64 encoded PNG string (Pyodide).
"""
def to2d(x):
return [[x]] if not isinstance(x, list) else x
try:
data = to2d(data)
if not isinstance(data, list) or len(data) < 1:
return "Error: Data must be a non-empty list"
# Extract labels and values
labels = []
vals = []
for row in data:
if not isinstance(row, list) or len(row) < 2:
continue
try:
labels.append(str(row[0]))
vals.append(float(row[1]))
except (ValueError, TypeError):
continue
if len(labels) == 0 or len(vals) == 0:
return "Error: No valid data rows found"
if any(v < 0 for v in vals):
return "Error: Values must be non-negative"
# Create figure
fig, ax = plt.subplots(figsize=(8, 6))
# Create funnel using horizontal bars centered
y_pos = np.arange(len(labels))
colors_list = plt.cm.get_cmap(color_map)(np.linspace(0, 1, len(vals)))
# Center the bars
max_val = max(vals) if vals else 1
left_edges = [(max_val - v) / 2 for v in vals]
bars = ax.barh(y_pos, vals, left=left_edges, color=colors_list, edgecolor='black')
# Add value labels if requested
if values == "true":
for i, (bar, val) in enumerate(zip(bars, vals)):
ax.text(max_val / 2, i, f'{val}', ha='center', va='center', fontweight='bold')
# Set labels
ax.set_yticks(y_pos)
ax.set_yticklabels(labels)
ax.invert_yaxis() # Top to bottom
if title:
ax.set_title(title)
# Handle legend
if legend == "true":
ax.legend(labels, loc="best")
# Remove x-axis for cleaner look
ax.set_xticks([])
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
plt.tight_layout()
if IS_PYODIDE:
buf = io.BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight')
plt.close(fig)
buf.seek(0)
img_base64 = base64.b64encode(buf.read()).decode('utf-8')
return f"data:image/png;base64,{img_base64}"
else:
return fig
except Exception as e:
return f"Error: {str(e)}"