ANOVA Boxplot

Coffee Origin Quality ANOVA

Comparing cupping scores across different coffee growing regions.

Output
Coffee Origin Quality ANOVA
Python
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats

np.random.seed(1515)

# Cupping scores (0-100)
ethiopia = np.random.normal(87, 3, 60)
colombia = np.random.normal(84, 3.5, 60)
brazil = np.random.normal(82, 4, 60)
guatemala = np.random.normal(85, 3.2, 60)
kenya = np.random.normal(88, 2.8, 60)

ethiopia = np.clip(ethiopia, 75, 95)
colombia = np.clip(colombia, 72, 93)
brazil = np.clip(brazil, 70, 92)
guatemala = np.clip(guatemala, 73, 94)
kenya = np.clip(kenya, 78, 96)

F_stat, p_value = stats.f_oneway(ethiopia, colombia, brazil, guatemala, kenya)

fig, ax = plt.subplots(figsize=(13, 7), facecolor='#ffffff')
ax.set_facecolor('#ffffff')

colors = ['#6CF527', '#F5B027', '#C82909', '#27D3F5', '#F5276C']
data = [ethiopia, colombia, brazil, guatemala, kenya]

bp = ax.boxplot(data, positions=[1, 2, 3, 4, 5], widths=0.55, patch_artist=True,
                medianprops={'color': '#1f2937', 'linewidth': 2},
                whiskerprops={'color': '#9ca3af', 'linewidth': 1.5},
                capprops={'color': '#9ca3af', 'linewidth': 1.5},
                flierprops={'marker': 'o', 'markerfacecolor': '#d1d5db', 'markersize': 4})

for patch, color in zip(bp['boxes'], colors):
    patch.set_facecolor(color)
    patch.set_alpha(0.6)
    patch.set_edgecolor(color)
    patch.set_linewidth(2)

labels = ['Ethiopia', 'Colombia', 'Brazil', 'Guatemala', 'Kenya']

# Specialty coffee threshold
ax.axhspan(85, 100, alpha=0.1, color='#22c55e')
ax.axhline(y=85, color='#22c55e', linestyle='--', alpha=0.7, linewidth=1.5)
ax.text(5.45, 85, 'Specialty', fontsize=7, color='#22c55e', va='center')

# Price per kg
prices = ['$28', '$22', '$15', '$24', '$32']
for i, (d, price, color) in enumerate(zip(data, prices, colors)):
    ax.text(i+1, 68, f'μ={d.mean():.1f} | {price}/kg', ha='center', fontsize=8, color=color)

# Stats header
stats_text = f"ANOVA: F={F_stat:.2f}, p={p_value:.4f} | Top Quality: Kenya (μ={kenya.mean():.1f})"
bbox = dict(boxstyle="round,pad=0.3", facecolor='#fef3c7', edgecolor='#F5B027', lw=2)
ax.text(0.5, 1.02, stats_text, transform=ax.transAxes, fontsize=9, color='#1f2937',
        ha='center', va='bottom', fontfamily='monospace', bbox=bbox)

ax.set_xticks([1, 2, 3, 4, 5])
ax.set_xticklabels(labels, fontsize=10, color='#1f2937')
ax.set_ylabel('Cupping Score (SCA)', fontsize=12, color='#1f2937', fontweight='500')
ax.set_title('Single-Origin Coffee Quality\nQ-Grader Evaluation Results', 
             fontsize=14, color='#1f2937', fontweight='bold', pad=25)

ax.tick_params(colors='#374151')
for spine in ax.spines.values():
    spine.set_color('#e5e7eb')
ax.yaxis.grid(True, color='#f3f4f6', linewidth=0.8)
ax.set_axisbelow(True)
ax.set_ylim(65, 98)

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support