Mirror Chart
Organic vs Paid Traffic Sessions
Mirror histogram comparing user engagement with bounce rate annotations
Output
Python
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(111)
BG_COLOR = '#0a0a0f'
# Session duration (seconds)
organic = np.random.exponential(200, 1000) + 20
paid = np.random.exponential(90, 1000) + 15
organic = organic[organic < 800]
paid = paid[paid < 800]
fig, ax = plt.subplots(figsize=(12, 7), facecolor=BG_COLOR)
ax.set_facecolor(BG_COLOR)
bins = np.linspace(0, 600, 40)
bin_width = bins[1] - bins[0]
centers = (bins[:-1] + bins[1:]) / 2
# Organic (top) - green
h1, _ = np.histogram(organic, bins=bins, density=True)
ax.bar(centers, h1, width=bin_width*0.85, color='#6CF527', alpha=0.7,
edgecolor='#6CF527', linewidth=1, label=f'Organic (μ={np.mean(organic):.0f}s)')
# Paid (bottom) - pink
h2, _ = np.histogram(paid, bins=bins, density=True)
ax.bar(centers, -h2, width=bin_width*0.85, color='#F5276C', alpha=0.7,
edgecolor='#F5276C', linewidth=1, label=f'Paid (μ={np.mean(paid):.0f}s)')
ax.axhline(0, color='#555555', linewidth=2)
# Bounce rate threshold (< 30 seconds)
bounce_threshold = 30
ax.axvline(bounce_threshold, color='#ef4444', linestyle='--', linewidth=2, alpha=0.7)
ax.fill_betweenx([min(-h2), max(h1)], 0, bounce_threshold, alpha=0.1, color='#ef4444')
# Bounce rates
organic_bounce = (organic < bounce_threshold).mean() * 100
paid_bounce = (paid < bounce_threshold).mean() * 100
# Engagement score
organic_engaged = (organic > 120).mean() * 100
paid_engaged = (paid > 120).mean() * 100
# Stats annotations
ax.text(bounce_threshold + 10, max(h1)*0.9, f'Bounce: {organic_bounce:.0f}%',
color='#6CF527', fontsize=10, fontweight='bold')
ax.text(bounce_threshold + 10, min(-h2)*0.9, f'Bounce: {paid_bounce:.0f}%',
color='#F5276C', fontsize=10, fontweight='bold')
# Engagement quality
quality_diff = organic_engaged - paid_engaged
stats = f"Engaged Users (>2min): Organic {organic_engaged:.0f}% vs Paid {paid_engaged:.0f}% | Δ = +{quality_diff:.0f}%"
ax.text(0.5, 0.98, stats, transform=ax.transAxes, ha='center', va='top',
fontsize=10, color='#6CF527', fontfamily='monospace',
bbox=dict(boxstyle='round,pad=0.4', facecolor=BG_COLOR, edgecolor='#6CF527', lw=2))
ax.set_xlabel('Session Duration (seconds)', fontsize=12, color='white', fontweight='500')
ax.set_title('Traffic Quality: Organic vs Paid', fontsize=16, color='white', fontweight='bold', pad=25)
ax.tick_params(colors='#888888', labelsize=10)
ax.set_yticks([])
for spine in ['top', 'right']:
ax.spines[spine].set_visible(False)
for spine in ['bottom', 'left']:
ax.spines[spine].set_color('#333333')
ax.legend(loc='upper right', facecolor=BG_COLOR, edgecolor='#333333', labelcolor='white', fontsize=10)
plt.tight_layout()
plt.show()
Library
Matplotlib
Category
Statistical
☕