Violin Plot

Ridgeline Violin Plot

Elegant overlapping distributions with gradient colors and trend line

Output
Ridgeline Violin Plot
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

# Data
np.random.seed(42)
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August']
data = [np.random.normal(5 + i * 0.5, 1.2 - i * 0.05, 500) for i in range(len(months))]

# Custom gradient colormap (coral to cyan)
colors_hex = ['#FF6B6B', '#FF8E72', '#FFA07A', '#FFB347', 
              '#98D8C8', '#7FC8A9', '#52B788', '#40916C']

# Create figure with subtle gradient background
fig, ax = plt.subplots(figsize=(12, 9), facecolor='#FAFBFC')
ax.set_facecolor('#FAFBFC')

spacing = 1.0
overlap = 0.4

for i, (d, month) in enumerate(zip(data, months)):
    pos = i * spacing
    
    # Create violin
    vp = ax.violinplot([d], positions=[pos], widths=spacing * 2.2,
                       showmeans=False, showmedians=False, showextrema=False,
                       vert=False)
    
    for body in vp['bodies']:
        vertices = body.get_paths()[0].vertices.copy()
        
        # Clip to create ridgeline effect
        vertices[:, 1] = np.clip(vertices[:, 1], pos - 0.05, np.inf)
        body.get_paths()[0].vertices = vertices
        
        # Main fill with gradient effect
        body.set_facecolor(colors_hex[i])
        body.set_edgecolor('none')
        body.set_alpha(0.9)
        
        # Add subtle shadow
        shadow_vertices = vertices.copy()
        shadow_vertices[:, 1] -= 0.08
        shadow_vertices[:, 0] += 0.05
        ax.fill(shadow_vertices[:, 0], shadow_vertices[:, 1], 
                color='#000000', alpha=0.03, zorder=i)
        
        # Add white edge glow
        ax.plot(vertices[:, 0], vertices[:, 1], 
                color='white', linewidth=2.5, alpha=0.8, zorder=i+10)
        ax.plot(vertices[:, 0], vertices[:, 1], 
                color=colors_hex[i], linewidth=1, alpha=0.9, zorder=i+11)
    
    # Median marker with glow
    median = np.median(d)
    ax.scatter(median, pos + 0.15, s=80, c='white', zorder=100,
               edgecolor=colors_hex[i], linewidth=2)
    
    # Value annotation
    ax.text(median, pos + 0.35, f'{median:.1f}', fontsize=9, 
            ha='center', va='bottom', color='#374151', fontweight='500')

# Month labels with custom styling
for i, month in enumerate(months):
    ax.text(-0.8, i * spacing + 0.1, month, fontsize=11, fontweight='600',
            color='#1F2937', ha='right', va='center')

# Remove default ticks
ax.set_yticks([])
ax.set_yticklabels([])

# X-axis styling
ax.set_xlabel('Temperature Distribution (°C)', fontsize=13, fontweight='500', 
              color='#374151', labelpad=15)
ax.set_xlim(-1.5, 12)
ax.set_ylim(-0.5, len(months) * spacing + 0.5)

# Minimal spines
for spine in ax.spines.values():
    spine.set_visible(False)

# Subtle vertical grid
for x in range(0, 12, 2):
    ax.axvline(x, color='#E5E7EB', linewidth=0.8, alpha=0.5, zorder=0)

# X-axis ticks
ax.set_xticks(range(0, 12, 2))
ax.tick_params(axis='x', colors='#9CA3AF', labelsize=10, length=0, pad=8)

# Add subtle trend line
medians = [np.median(d) for d in data]
positions = [i * spacing + 0.15 for i in range(len(months))]
ax.plot(medians, positions, color='#6366F1', linewidth=2, alpha=0.4,
        linestyle='--', zorder=50)

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support