Mirror Chart
iOS vs Android User Engagement
Mirror density comparing daily app usage with retention metrics
Output
Python
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
np.random.seed(1980)
BG_COLOR = '#ffffff'
TEXT_COLOR = '#1f2937'
ios = np.random.gamma(3, 15, 900)
android = np.random.gamma(2.5, 18, 1100)
ios = ios[ios < 120]
android = android[android < 120]
fig, ax = plt.subplots(figsize=(12, 7), facecolor=BG_COLOR)
ax.set_facecolor(BG_COLOR)
x = np.linspace(0, 120, 400)
kde_i = gaussian_kde(ios)
y_i = kde_i(x)
ax.fill_between(x, y_i, alpha=0.3, color='#4927F5')
ax.plot(x, y_i, color='#4927F5', linewidth=2, label='iOS (avg: %.1f min)' % np.mean(ios))
kde_a = gaussian_kde(android)
y_a = kde_a(x) * -1
ax.fill_between(x, y_a, alpha=0.3, color='#6CF527')
ax.plot(x, y_a, color='#6CF527', linewidth=2, label='Android (avg: %.1f min)' % np.mean(android))
ax.axhline(0, color=TEXT_COLOR, linewidth=1.5)
avg_ios = np.mean(ios)
avg_and = np.mean(android)
ax.axvline(avg_ios, color='#4927F5', linestyle='--', linewidth=1.5, alpha=0.7)
ax.axvline(avg_and, color='#6CF527', linestyle='--', linewidth=1.5, alpha=0.7)
pct_diff = (avg_ios - avg_and) / avg_and * 100
label_text = 'iOS +%.1f%% engagement' % pct_diff if pct_diff > 0 else 'Android +%.1f%% engagement' % abs(pct_diff)
ax.text(0.98, 0.98, label_text, transform=ax.transAxes, fontsize=11,
color='#4927F5' if pct_diff > 0 else '#6CF527', ha='right', va='top', fontweight='bold')
stats_text = 'Retention Proxy:\niOS: %.1f min/day\nAndroid: %.1f min/day' % (avg_ios, avg_and)
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.set_xlabel('Daily Session Time (minutes)', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_ylabel('Density', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_title('Mobile App Engagement: iOS vs Android', 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, 120)
plt.tight_layout()
plt.show()
Library
Matplotlib
Category
Statistical
☕