Violin Plot

Production Line Efficiency

Cycle time distribution showing automation impact

Output
Production Line Efficiency
Python
import matplotlib.pyplot as plt
import numpy as np

# Cycle time data (minutes) by production line
np.random.seed(42)
lines = ['Line A\n(Legacy)', 'Line B\n(Upgraded)', 'Line C\n(New)', 'Line D\n(Automated)', 'Line E\n(AI-Optimized)']
cycle_times = [
    np.random.gamma(8, 2, 200) + 10,      # Legacy - slow, variable
    np.random.gamma(6, 1.8, 200) + 8,     # Upgraded
    np.random.gamma(5, 1.5, 200) + 6,     # New
    np.random.gamma(4, 1.2, 200) + 4,     # Automated
    np.random.gamma(3, 1, 200) + 3,       # AI-optimized
]

# Efficiency gradient colors
colors = ['#EF4444', '#F97316', '#EAB308', '#22C55E', '#10B981']

# Create industrial theme
fig, ax = plt.subplots(figsize=(12, 7), facecolor='#F8FAFC')
ax.set_facecolor('#F8FAFC')

vp = ax.violinplot(cycle_times, positions=range(len(lines)), widths=0.7,
                   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)

# Target cycle time
target = 12
ax.axhline(target, color='#3B82F6', linewidth=2, linestyle='--', alpha=0.7)
ax.fill_between([-0.5, 4.5], 0, target, color='#DBEAFE', alpha=0.3, zorder=0)
ax.text(4.55, target - 0.5, 'Target', fontsize=10, color='#3B82F6', 
        fontweight='500', va='top')

# Metrics
for i, times in enumerate(cycle_times):
    median = np.median(times)
    cv = np.std(times) / np.mean(times) * 100  # Coefficient of variation
    on_target = (times <= target).mean() * 100
    
    # Median
    ax.scatter(i, median, c='white', s=100, zorder=10,
               edgecolor=colors[i], linewidth=2.5)
    
    # Variability indicator (CV)
    cv_color = '#10B981' if cv < 25 else '#F59E0B' if cv < 40 else '#EF4444'
    ax.errorbar(i, median, yerr=np.std(times), color=colors[i],
                capsize=6, capthick=2, linewidth=0, elinewidth=2, alpha=0.5)
    
    # On-target percentage
    ax.text(i, 2, f'{on_target:.0f}%', ha='center', fontsize=11,
            color='#10B981' if on_target > 80 else '#F97316',
            fontweight='bold')

# Improvement arrows
for i in range(1, len(lines)):
    prev_med = np.median(cycle_times[i-1])
    curr_med = np.median(cycle_times[i])
    improvement = ((prev_med - curr_med) / prev_med) * 100
    if improvement > 0:
        ax.annotate('', xy=(i - 0.1, curr_med), xytext=(i - 0.9, prev_med),
                    arrowprops=dict(arrowstyle='->', color='#10B981', lw=1.5, alpha=0.5))

# Label
ax.text(-0.5, 3.5, 'On Target', fontsize=9, color='#6B7280')

# Styling
ax.set_xticks(range(len(lines)))
ax.set_xticklabels(lines, fontsize=10, fontweight='500')
ax.set_ylabel('Cycle Time (minutes)', fontsize=12, fontweight='500', color='#374151')
ax.set_ylim(0, 40)

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

Did this help you?

Support PyLucid to keep it free & growing

Support