Raincloud Plot

Customer Wait Time by Service Channel

Wait time analysis across different customer service channels

Output
Customer Wait Time by Service Channel
Python
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats

np.random.seed(17)
BG_COLOR = '#ffffff'
TEXT_COLOR = '#1f2937'
GRID_COLOR = '#e5e7eb'
COLOR_SCALE = ['#4927F5', '#F54927', '#27F5B0', '#F5D327']

# Data: Wait time (minutes) by service channel
phone = np.random.exponential(12, 55) + 2
chat = np.random.exponential(5, 60) + 1
email = np.random.exponential(180, 50) + 30
in_person = np.random.exponential(15, 45) + 5
y_data = [phone, chat, email, in_person]
labels = ['Phone', 'Live Chat', 'Email', 'In-Person']

F_stat, p_value = stats.f_oneway(*y_data)
positions = list(range(len(y_data)))

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

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(TEXT_COLOR)
    pc.set_linewidth(1.8)

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

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.6, zorder=2, edgecolors='white', linewidths=0.5)

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=TEXT_COLOR, zorder=3, lw=1.5)
    ax.text(i + 0.3, mean, f"μ={mean:.0f}m", fontsize=10, va="center", color=TEXT_COLOR,
            bbox=dict(facecolor=BG_COLOR, edgecolor=color, boxstyle="round,pad=0.15", lw=2))

eta_sq = 0.76
stats_text = f"ANOVA: F={F_stat:.1f}, p<0.001, η²={eta_sq:.2f}"
bbox = dict(facecolor=BG_COLOR, edgecolor='#F5276C', boxstyle="round,pad=0.3", lw=2)
ax.text(0.5, 1.02, stats_text, transform=ax.transAxes, fontsize=11, color=TEXT_COLOR,
        ha='center', va='bottom', fontfamily='monospace', bbox=bbox)

ax.set_ylabel('Wait Time (minutes)', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_title('Customer Wait Time by Service Channel', fontsize=14, color=TEXT_COLOR, fontweight='bold', pad=30)
ax.set_xticks(positions)
ax.set_xticklabels(labels)
ax.tick_params(colors=TEXT_COLOR, labelsize=10)
for spine in ax.spines.values():
    spine.set_color(GRID_COLOR)
ax.yaxis.grid(True, color=GRID_COLOR, linewidth=0.5, alpha=0.7)
ax.set_axisbelow(True)

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support