GAUGE
Excel Usage
=GAUGE(value, title, min_val, max_val, color_ranges)
value(float, required): Current reading.title(str, optional, default: null): Gauge title.min_val(float, optional, default: 0): Minimum value.max_val(float, optional, default: 100): Maximum value.color_ranges(list[list], optional, default: null): Range color zones.
Returns (object): Matplotlib Figure object (standard Python) or base64 encoded PNG string (Pyodide).
Examples
Example 1: Gauge showing mid-range value
Inputs:
| value |
|---|
| 50 |
Excel formula:
=GAUGE(50)
Expected output:
"chart"
Example 2: Gauge with custom title
Inputs:
| value | title |
|---|---|
| 75 | Speed (mph) |
Excel formula:
=GAUGE(75, "Speed (mph)")
Expected output:
"chart"
Example 3: Custom min/max range
Inputs:
| value | min_val | max_val |
|---|---|---|
| 150 | 0 | 200 |
Excel formula:
=GAUGE(150, 0, 200)
Expected output:
"chart"
Example 4: Gauge with color zones
Inputs:
| value | color_ranges | ||
|---|---|---|---|
| 85 | 0 | 50 | green |
| 50 | 75 | yellow | |
| 75 | 100 | red |
Excel formula:
=GAUGE(85, {0,50,"green";50,75,"yellow";75,100,"red"})
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
import matplotlib.patches as mpatches
def gauge(value, title=None, min_val=0, max_val=100, color_ranges=None):
"""
Create a speedometer/gauge style chart.
See: https://matplotlib.org/stable/gallery/pie_and_polar_charts/pie_and_donut_labels.html
This example function is provided as-is without any representation of accuracy.
Args:
value (float): Current reading.
title (str, optional): Gauge title. Default is None.
min_val (float, optional): Minimum value. Default is 0.
max_val (float, optional): Maximum value. Default is 100.
color_ranges (list[list], optional): Range color zones. Default is None.
Returns:
object: Matplotlib Figure object (standard Python) or base64 encoded PNG string (Pyodide).
"""
try:
# Validate value
try:
val = float(value)
except (TypeError, ValueError):
return "Error: Value must be a number"
# Validate min/max
if min_val >= max_val:
return "Error: min_val must be less than max_val"
# Clamp value to range
val = max(min_val, min(max_val, val))
# Create the figure
fig, ax = plt.subplots(figsize=(8, 5), subplot_kw={'projection': 'polar'})
# Set up the gauge (semicircle from pi to 2*pi, or 180 to 360 degrees)
ax.set_theta_zero_location('S')
ax.set_theta_direction(1)
ax.set_thetamin(0)
ax.set_thetamax(180)
# Remove radial labels
ax.set_yticks([])
ax.set_xticks([])
# Parse color ranges if provided
if color_ranges:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
ranges = to2d(color_ranges)
# Draw colored zones
for range_def in ranges:
if not isinstance(range_def, list) or len(range_def) < 3:
continue
try:
r_min = float(range_def[0])
r_max = float(range_def[1])
r_color = str(range_def[2])
# Convert value range to angle (0 to 180 degrees = 0 to pi radians)
theta_min = np.pi * (r_min - min_val) / (max_val - min_val)
theta_max = np.pi * (r_max - min_val) / (max_val - min_val)
theta = np.linspace(theta_min, theta_max, 100)
r = np.ones_like(theta)
ax.fill_between(theta, 0, r, color=r_color, alpha=0.3)
except (TypeError, ValueError):
continue
else:
# Default gradient from green to yellow to red
n_segments = 100
for i in range(n_segments):
theta_start = np.pi * i / n_segments
theta_end = np.pi * (i + 1) / n_segments
# Color gradient: green -> yellow -> red
ratio = i / n_segments
if ratio < 0.5:
color = (ratio * 2, 1, 0) # green to yellow
else:
color = (1, 2 * (1 - ratio), 0) # yellow to red
theta = np.linspace(theta_start, theta_end, 10)
r = np.ones_like(theta)
ax.fill_between(theta, 0, r, color=color, alpha=0.3)
# Draw the needle
needle_angle = np.pi * (val - min_val) / (max_val - min_val)
ax.plot([needle_angle, needle_angle], [0, 0.9], color='black', linewidth=3)
# Add value text
ax.text(np.pi / 2, -0.2, f'{val:.1f}',
ha='center', va='center', fontsize=20, weight='bold',
transform=ax.transData)
# Add min/max labels
ax.text(0, 1.1, f'{min_val:.0f}', ha='right', va='center', fontsize=10)
ax.text(np.pi, 1.1, f'{max_val:.0f}', ha='left', va='center', fontsize=10)
if title:
plt.title(str(title), pad=20, fontsize=14, weight='bold')
plt.tight_layout()
if IS_PYODIDE:
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
buf.seek(0)
img_base64 = base64.b64encode(buf.read()).decode('utf-8')
plt.close(fig)
return f"data:image/png;base64,{img_base64}"
else:
return fig
except Exception as e:
return f"Error: {str(e)}"