Violin Plot
Urban Commute Analysis
Travel time distribution by mode with reliability scores
Output
Python
import matplotlib.pyplot as plt
import numpy as np
# Commute times (minutes) by transport mode
np.random.seed(42)
modes = ['Car\n(Solo)', 'Carpool', 'Bus', 'Subway', 'Bicycle', 'E-Scooter']
commute_times = [
np.random.gamma(5, 6, 200) + 15, # Car - variable traffic
np.random.gamma(5, 7, 200) + 20, # Carpool - detours
np.random.gamma(6, 5, 200) + 25, # Bus - stops
np.random.normal(35, 8, 200), # Subway - consistent
np.random.gamma(4, 5, 200) + 20, # Bicycle - weather dependent
np.random.gamma(3, 4, 200) + 15, # E-scooter - new option
]
# Mode colors
colors = ['#64748B', '#8B5CF6', '#F59E0B', '#3B82F6', '#22C55E', '#EC4899']
# Create figure
fig, ax = plt.subplots(figsize=(12, 7), facecolor='#F8FAFC')
ax.set_facecolor('#F8FAFC')
vp = ax.violinplot(commute_times, positions=range(len(modes)), widths=0.75,
showmeans=False, showmedians=False, showextrema=False)
for i, body in enumerate(vp['bodies']):
body.set_facecolor(colors[i])
body.set_edgecolor('white')
body.set_linewidth(2)
body.set_alpha(0.75)
# Ideal commute zone
ax.axhspan(20, 35, color='#DCFCE7', alpha=0.4, zorder=0)
ax.text(5.55, 27.5, 'Ideal\nZone', fontsize=9, color='#22C55E',
va='center', fontweight='500')
# Reliability score (inverse of CV)
for i, times in enumerate(commute_times):
median = np.median(times)
cv = np.std(times) / np.mean(times)
reliability = max(0, 100 - cv * 100)
# Median with reliability ring
ring_color = '#22C55E' if reliability > 75 else '#F59E0B' if reliability > 50 else '#EF4444'
ax.scatter(i, median, c='white', s=150, zorder=10,
edgecolor=ring_color, linewidth=3)
ax.scatter(i, median, c=colors[i], s=50, zorder=11)
# Time label
ax.text(i, median - 8, f'{median:.0f}m', ha='center', fontsize=10,
color='#374151', fontweight='600')
# Reliability score
ax.text(i, 75, f'{reliability:.0f}', ha='center', fontsize=11,
color=ring_color, fontweight='bold')
# CO2 indicators (relative)
co2_relative = [100, 60, 40, 20, 0, 5]
for i, co2 in enumerate(co2_relative):
bar_height = co2 / 100 * 8
ax.bar(i, bar_height, bottom=82, width=0.3, color='#9CA3AF', alpha=0.4)
# Labels
ax.text(-0.5, 78, 'Reliability', fontsize=9, color='#6B7280')
ax.text(-0.5, 88, 'CO2', fontsize=9, color='#6B7280')
# Styling
ax.set_xticks(range(len(modes)))
ax.set_xticklabels(modes, fontsize=10, fontweight='500')
ax.set_ylabel('Commute Time (minutes)', fontsize=12, fontweight='500', color='#374151')
ax.set_ylim(0, 95)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_color('#E5E7EB')
ax.spines['bottom'].set_color('#E5E7EB')
ax.tick_params(colors='#6B7280', labelsize=10)
ax.yaxis.grid(True, linestyle='--', alpha=0.3, color='#9CA3AF')
plt.tight_layout()
plt.show()
Library
Matplotlib
Category
Statistical
More Violin Plot examples
☕