Raincloud Plot

Restaurant Rating by Cuisine Type

Customer satisfaction ratings across different cuisine categories

Output
Restaurant Rating by Cuisine Type
Python
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats

np.random.seed(19)
BG_COLOR = '#ffffff'
TEXT_COLOR = '#1f2937'
GRID_COLOR = '#e5e7eb'
COLOR_SCALE = ['#C82909', '#27D3F5', '#F5D327', '#6CF527']

# Data: Restaurant ratings (1-5) by cuisine
italian = np.random.normal(4.2, 0.5, 65)
japanese = np.random.normal(4.4, 0.4, 58)
mexican = np.random.normal(4.0, 0.6, 62)
indian = np.random.normal(4.1, 0.55, 55)
y_data = [italian, japanese, mexican, indian]
labels = ['Italian', 'Japanese', 'Mexican', 'Indian']

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='#5314E6'),
                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='#5314E6', 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:.2f}★", fontsize=10, va="center", color=TEXT_COLOR,
            bbox=dict(facecolor=BG_COLOR, edgecolor=color, boxstyle="round,pad=0.15", lw=2))

eta_sq = 0.09
stats_text = f"ANOVA: F={F_stat:.1f}, p=0.002, η²={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('Customer Rating (1-5 Stars)', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_title('Restaurant Rating by Cuisine Type', 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)
ax.set_ylim(2.5, 5.5)

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support