Raincloud Plot

REST API Endpoint Latency Raincloud

Distribution of API response times across different endpoint types.

Output
REST API Endpoint Latency Raincloud
Python
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats

np.random.seed(1010)

# Response time in milliseconds
get_simple = np.random.lognormal(2.5, 0.4, 150)
get_complex = np.random.lognormal(3.5, 0.5, 140)
post_create = np.random.lognormal(3.2, 0.45, 130)
post_update = np.random.lognormal(3.0, 0.4, 135)

get_simple = np.clip(get_simple, 5, 50)
get_complex = np.clip(get_complex, 15, 150)
post_create = np.clip(post_create, 10, 100)
post_update = np.clip(post_update, 8, 80)

F_stat, p_value = stats.f_oneway(get_simple, get_complex, post_create, post_update)

BG_COLOR = "#0a0a0f"
COLOR_SCALE = ["#6CF527", "#F5276C", "#27D3F5", "#F5B027"]

fig, ax = plt.subplots(figsize=(10, 6), facecolor=BG_COLOR)
ax.set_facecolor(BG_COLOR)

y_data = [get_simple, get_complex, post_create, post_update]
positions = [0, 1, 2, 3]
labels = ["GET /simple", "GET /complex", "POST /create", "PUT /update"]

for h in [25, 50, 100]:
    ax.axhline(h, color='#333333', ls=(0, (5, 5)), alpha=0.5, zorder=0)

# SLA thresholds
ax.axhline(y=50, color='#22c55e', ls='--', alpha=0.7, lw=2)
ax.axhline(y=100, color='#ef4444', ls='--', alpha=0.7, lw=2)
ax.text(3.55, 50, "P95 SLA", color='#22c55e', fontsize=9, va='center')
ax.text(3.55, 100, "P99 SLA", color='#ef4444', fontsize=9, va='center')

violins = ax.violinplot(y_data, positions=positions, widths=0.5, 
                         bw_method="silverman", showmeans=False, 
                         showmedians=False, showextrema=False)
for pc in violins["bodies"]:
    pc.set_facecolor("none")
    pc.set_edgecolor("white")
    pc.set_linewidth(1.8)

bp = ax.boxplot(y_data, positions=positions, showfliers=False, showcaps=False,
                medianprops=dict(linewidth=3, color='#F5D327'),
                whiskerprops=dict(linewidth=2, color='#555555'),
                boxprops=dict(linewidth=2, color='#555555'))

for i, (y, color) in enumerate(zip(y_data, COLOR_SCALE)):
    x_jitter = np.array([i] * len(y)) + stats.t(df=6, scale=0.04).rvs(len(y))
    ax.scatter(x_jitter, y, s=50, color=color, alpha=0.5, zorder=2)

means = [y.mean() for y in y_data]
for i, (mean, color) in enumerate(zip(means, COLOR_SCALE)):
    ax.scatter(i, mean, s=180, color='#C82909', zorder=5, edgecolors='white', linewidths=2)
    ax.plot([i, i + 0.28], [mean, mean], ls="dashdot", color="white", zorder=3, lw=1.5)
    ax.text(i + 0.3, mean, f"μ={mean:.0f}ms", fontsize=10, va="center", color='white',
            bbox=dict(facecolor=BG_COLOR, edgecolor=color, boxstyle="round,pad=0.15", lw=2))

# P99 percentiles
p99s = [np.percentile(y, 99) for y in y_data]
for i, (p99, color) in enumerate(zip(p99s, COLOR_SCALE)):
    ax.text(i, -12, f"P99: {p99:.0f}ms", ha='center', fontsize=9, color=color)

ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.spines["left"].set_color('#444444')
ax.spines["bottom"].set_color('#444444')
ax.tick_params(colors='#888888', length=0)

xlabels = [f"{l}\n(n={len(y_data[i])})" for i, l in enumerate(labels)]
ax.set_xticks(positions)
ax.set_xticklabels(xlabels, size=10, color='white')
ax.set_ylabel("Response Time (ms)", size=14, color='white', fontweight='bold')

ax.set_title("REST API Endpoint Latency Analysis", fontsize=14, color="white", fontweight="bold", pad=20)


ax.set_ylim(-20, 160)
plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support