Mirror Chart
Free vs Premium User Engagement
Mirror density comparing user activity with LTV analysis
Output
Python
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
np.random.seed(1984)
BG_COLOR = '#ffffff'
TEXT_COLOR = '#1f2937'
free = np.random.exponential(8, 2000)
premium = np.random.gamma(4, 10, 800)
free = free[(free > 0) & (free < 60)]
premium = premium[(premium > 5) & (premium < 80)]
fig, ax = plt.subplots(figsize=(12, 7), facecolor=BG_COLOR)
ax.set_facecolor(BG_COLOR)
x = np.linspace(0, 80, 400)
kde_p = gaussian_kde(premium)
y_p = kde_p(x)
ax.fill_between(x, y_p, alpha=0.3, color='#F5B027')
ax.plot(x, y_p, color='#F5B027', linewidth=2, label='Premium Users (n=%d)' % len(premium))
kde_f = gaussian_kde(free)
y_f = kde_f(x) * -1
ax.fill_between(x, y_f, alpha=0.3, color='#4927F5')
ax.plot(x, y_f, color='#4927F5', linewidth=2, label='Free Users (n=%d)' % len(free))
ax.axhline(0, color=TEXT_COLOR, linewidth=1.5)
avg_p = np.mean(premium)
avg_f = np.mean(free)
ax.axvline(avg_p, color='#F5B027', linestyle='--', linewidth=1.5, alpha=0.7)
ax.axvline(avg_f, color='#4927F5', linestyle='--', linewidth=1.5, alpha=0.7)
engagement_lift = (avg_p - avg_f) / avg_f * 100
arpu_premium = avg_p * 0.50
arpu_free = avg_f * 0.05
stats_text = 'ARPU Estimate:\nPremium: $%.2f/mo\nFree: $%.2f/mo\nLift: %.0fx' % (arpu_premium, arpu_free, arpu_premium/arpu_free)
ax.text(0.02, 0.98, stats_text.replace('\n', chr(10)), transform=ax.transAxes, fontsize=10,
color=TEXT_COLOR, verticalalignment='top', fontfamily='monospace',
bbox=dict(boxstyle='round,pad=0.5', facecolor='#f8fafc', edgecolor='#e5e7eb', alpha=0.9))
ax.text(0.98, 0.98, 'Premium: +%.0f%% engagement' % engagement_lift,
transform=ax.transAxes, fontsize=11, color='#F5B027', ha='right', va='top', fontweight='bold')
ax.set_xlabel('Monthly Session Time (minutes)', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_ylabel('Density', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_title('SaaS User Engagement: Free vs Premium', fontsize=14,
color=TEXT_COLOR, fontweight='bold', pad=15)
ax.tick_params(colors='#374151', labelsize=10)
for spine in ax.spines.values():
spine.set_color('#e5e7eb')
ax.legend(loc='upper right', facecolor=BG_COLOR, edgecolor='#e5e7eb',
labelcolor=TEXT_COLOR, fontsize=10)
ax.set_xlim(0, 80)
plt.tight_layout()
plt.show()
Library
Matplotlib
Category
Statistical
☕