Funnel Analysis
Overview
Funnel analysis tracks user progression through sequential steps, identifying where users drop off and optimizing each stage for better conversion.
When to Use
- When optimizing user conversion paths and improving conversion rates
- When identifying bottlenecks and drop-off points in user flows
- When comparing performance across different segments or traffic sources
- When measuring product feature adoption or onboarding effectiveness
- When improving customer journey efficiency and user experience
- When A/B testing different funnel configurations or designs
Funnel Structure
- Stage 1: Initial entry (landing page, app open)
- Stage 2-N: Intermediate steps (signup, selection, payment)
- Final Stage: Goal completion (purchase, subscription, sign-up)
- Drop-off: Users not progressing to next stage
- Conversion Rate: % progressing to next step
Key Metrics
- Drop-off Rate: % leaving at each stage
- Conversion Rate: % progressing per stage
- Funnel Efficiency: Overall conversion (Stage 1 to Final)
- Friction Score: Identifying problem areas
Implementation with Python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Create sample funnel data
np.random.seed(42)
funnel_stages = ['Landing Page', 'Sign Up', 'Product Selection', 'Add to Cart', 'Checkout', 'Payment', 'Confirmation']
# Simulate user journey (progressive drop-off)
data = []
users_at_stage = 100000
for i, stage in enumerate(funnel_stages):
# Progressively lower retention
drop_off_rate = 0.15 + (i * 0.05) # Increasing drop-off
users_at_stage = int(users_at_stage * (1 - drop_off_rate))
for _ in range(users_at_stage):
data.append({
'user_id': f'user_{np.random.randint(0, 1000000)}',
'stage': stage,
'timestamp': np.random.randint(0, 365),
})
df = pd.DataFrame(data)
# 1. Funnel Counts
funnel_counts = df['stage'].value_counts().reindex(funnel_stages)
print("Funnel Counts by Stage:")
print(funnel_counts)
# 2. Funnel Metrics
funnel_metrics = pd.DataFrame({
'Stage': funnel_stages,
'Users': funnel_counts.values,
})
funnel_metrics['Drop-off'] = funnel_metrics['Users'].shift(1) - funnel_metrics['Users']
funnel_metrics['Drop-off %'] = (funnel_metrics['Drop-off'] / funnel_metrics['Users'].shift(1) * 100).round(2)
funnel_metrics['Conversion %'] = (funnel_metrics['Users'] / funnel_metrics['Users'].iloc[0] * 100).round(2)
print("\nFunnel Metrics:")
print(funnel_metrics)
# 3. Visualization - Funnel Chart
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# Traditional funnel visualization
ax = axes[0]
colors = plt.cm.RdYlGn_r(np.linspace(0.3, 0.7, len(funnel_metrics)))
for idx, (stage, users) in enumerate(zip(funnel_metrics['Stage'], funnel_metrics['Users'])):
# Create trapezoid-like bars
width = users / funnel_metrics['Users'].max()
y_pos = len(funnel_metrics) - idx - 1
ax.barh(y_pos, width, left=(1 - width) / 2, height=0.6, color=colors[idx], edgecolor='black')
ax.text(-0.05, y_pos, stage, ha='right', va='center', fontsize=10)
ax.text(0.5, y_pos, f"{users:,}", ha='center', va='center', fontsize=9, fontweight='bold')
ax.set_xlim(0, 1)
ax.set_ylim(-0.5, len(funnel_metrics) - 0.5)
ax.set_xticks([])
ax.set_yticks([])
ax.set_title('Conversion Funnel')
# Step-by-step conversion
ax2 = axes[1]
x_pos = np.arange(len(funnel_stages))
colors2 = plt.cm.Spectral(np.linspace(0, 1, len(funnel_stages)))
bars = ax2.bar(x_pos, funnel_metrics['Users'], color=colors2, edgecolor='black', alpha=0.7)
# Add value labels
for i, (bar, users, conv) in enumerate(zip(bars, funnel_metrics['Users'], funnel_metrics['Conversion %'])):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width() / 2., height,
f'{int(users):,}\n({conv:.1f}%)',
ha='center', va='bottom', fontsize=9)
ax2.set_ylabel('User Count')
ax2.set_title('Users by Stage')
ax2.set_xticks(x_pos)
ax2.set_xticklabels(funnel_stages, rotation=45, ha='right')
ax2.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 4. Drop-off Analysis
fig, ax = plt.subplots(figsize=(12, 6))
# Filter out first stage (no drop-off from before)
drop_off_data = funnel_metrics[1:].copy()
drop_off_data = drop_off_data[drop_off_data['Drop-off'] > 0]
colors_drop = ['#d62728' if x > drop_off_data['Drop-off'].median() else '#2ca02c'
for x in drop_off_data['Drop-off']]
bars = ax.barh(drop_off_data['Stage'], drop_off_data['Drop-off %'], color=colors_drop, edgecolor='black')
# Add value labels
for i, (bar, drop_pct) in enumerate(zip(bars, drop_off_data['Drop-off %'])):
width = bar.get_width()
ax.text(width, bar.get_y() + bar.get_height() / 2.,
f'{drop_pct:.1f}%',
ha='left', va='center', fontsize=10, fontweight='bold')
ax.set_xlabel('Drop-off Rate (%)')
ax.set_title('Drop-off Rates by Stage')
ax.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
# 5. Funnel Efficiency Matrix
efficiency_matrix = funnel_metrics[['Stage', 'Conversion %']].copy()
print("\nFunnel Efficiency (% of Initial Users):")
print(efficiency_matrix)
# 6. Stage-to-stage conversion
fig, ax = plt.subplots(figsize=(12, 6))
stage_conversion = []
for i in range(len(funnel_metrics) - 1):
conversion = (funnel_metrics.iloc[i + 1]['Users'] / funnel_metrics.iloc[i]['Users'] * 100)
stage_conversion.append({
'Transition': f"{funnel_metrics.iloc[i]['Stage']}\n→ {funnel_metrics.iloc[i+1]['Stage']}",
'Conversion %': conversion
})
stage_conv_df = pd.DataFrame(stage_conversion)
colors_stage = ['#2ca02c' if x > 80 else '#ff7f0e' if x > 60 else '#d62728'
for x in stage_conv_df['Conversion %']]
bars = ax.bar(range(len(stage_conv_df)), stage_conv_df['Conversion %'], color=colors_stage, edgecolor='black')
# Add value labels
for bar, conv in zip(bars, stage_conv_df['Conversion %']):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2., height,
f'{conv:.1f}%',
ha='center', va='bottom', fontsize=10, fontweight='bold')
ax.set_ylabel('Conversion Rate (%)')
ax.set_title('Stage-to-Stage Conversion Rates')
ax.set_xticks(range(len(stage_conv_df)))
ax.set_xticklabels(stage_conv_df['Transition'], fontsize=9)
ax.set_ylim([0, 105])
ax.axhline(y=80, color='green', linestyle='--', alpha=0.5, label='Good (80%+)')
ax.axhline(y=60, color='orange', linestyle='--', alpha=0.5, label='Acceptable (60%+)')
ax.legend()
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 7. Funnel by Segment (e.g., traffic source)
np.random.seed(42)
df['traffic_source'] = np.random.choice(['Organic', 'Paid', 'Direct'], len(df))
# Create funnel for each segment
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
for idx, source in enumerate(['Organic', 'Paid', 'Direct']):
df_segment = df[df['traffic_source'] == source]
segment_counts = df_segment['stage'].value_counts().reindex(funnel_stages)
segment_metrics = pd.DataFrame({
'Stage': funnel_stages,
'Users': segment_counts.values,
})
segment_metrics['Conversion %'] = (segment_metrics['Users'] / segment_metrics['Users'].iloc[0] * 100).round(2)
ax = axes[idx]
x_pos = np.arange(len(funnel_stages))
bars = ax.bar(x_pos, segment_metrics['Users'], color='steelblue', edgecolor='black', alpha=0.7)
for bar, conv in zip(bars, segment_metrics['Conversion %']):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2., height,
f'{conv:.1f}%',
ha='center', va='bottom', fontsize=8)
ax.set_title(f'Funnel: {source}')
ax.set_ylabel('Users')
ax.set_xticks(x_pos)
ax.set_xticklabels(funnel_stages, rotation=45, ha='right', fontsize=8)
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 8. Comparison table of segments
print("\nFunnel Comparison by Traffic Source:")
comparison_data = []
for source in ['Organic', 'Paid', 'Direct']:
df_segment = df[df['traffic_source'] == source]
segment_counts = df_segment['stage'].value_counts().reindex(funnel_stages)
comparison_data.append({
'Traffic Source': source,
'Landing': segment_counts.iloc[0],
'Sign Up': segment_counts.iloc[1],
'Product': segment_counts.iloc[2],
'Cart': segment_counts.iloc[3],
'Final Conv %': (segment_counts.iloc[-1] / segment_counts.iloc[0] * 100),
})
comparison_df = pd.DataFrame(comparison_data)
print(comparison_df.round(2))
# 9. Sankey diagram representation (text-based)
print("\nFunnel Flow Summary:")
print("="*60)
for i in range(len(funnel_metrics) - 1):
current = funnel_metrics.iloc[i]
next_stage = funnel_metrics.iloc[i + 1]
drop = current['Users'] - next_stage['Users']
conv_pct = (next_stage['Users'] / current['Users'] * 100)
print(f"{current['Stage']}")
print(f" ├─ Continue: {next_stage['Users']:>7,} ({conv_pct:>5.1f}%)")
print(f" └─ Drop-off: {drop:>7,} ({100-conv_pct:>5.1f}%)")
print(f"\n{funnel_metrics.iloc[-1]['Stage']}")
print(" └─ Completed: {0:,}".format(int(funnel_metrics.iloc[-1]['Users'])))
# 10. Key insights visualization
fig, ax = plt.subplots(figsize=(10, 6))
ax.axis('off')
insights = f"""
FUNNEL ANALYSIS SUMMARY
Total Users: {int(funnel_metrics['Users'].iloc[0]):,}
Conversions: {int(funnel_metrics['Users'].iloc[-1]):,}
Overall Conversion Rate: {funnel_metrics['Conversion %'].iloc[-1]:.2f}%
BOTTLENECKS (Highest Drop-off):
1. {funnel_metrics[funnel_metrics['Drop-off %'].idxmax()]['Stage']} - {funnel_metrics['Drop-off %'].max():.1f}%
2. {funnel_metrics[funnel_metrics['Drop-off %'].nlargest(2).index[1]]['Stage']}
BEST PERFORMERS (Highest Conversion):
1. {stage_conv_df.nlargest(2, 'Conversion %').iloc[0]['Transition'].split(chr(10))[1][2:]} - {stage_conv_df['Conversion %'].nlargest(2).iloc[0]:.1f}%
2. {stage_conv_df.nlargest(2, 'Conversion %').iloc[1]['Transition'].split(chr(10))[1][2:]} - {stage_conv_df['Conversion %'].nlargest(2).iloc[1]:.1f}%
RECOMMENDATIONS:
• Focus optimization on highest drop-off stages
• Benchmark against industry standards
• A/B test improvements at each stage
• Monitor segment performance separately
"""
ax.text(0.05, 0.95, insights, transform=ax.transAxes, fontfamily='monospace',
fontsize=11, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
plt.tight_layout()
plt.show()
Funnel Analysis Steps
- Define all stages in customer journey
- Count users at each stage
- Calculate drop-off and conversion rates
- Identify biggest bottlenecks
- Analyze by segments (traffic source, device, etc.)
- Benchmark against goals
- Prioritize optimization efforts
Common Drop-off Points
- Complex signup forms
- Unexpected fees
- Confusing navigation
- Payment issues
- Technical errors
Deliverables
- Funnel visualization chart
- Drop-off analysis table
- Stage-to-stage conversion rates
- Segmented funnel analysis
- Bottleneck identification
- Actionable optimization recommendations
- Benchmark comparison report
1---2name: funnel-analysis3description: Analyze user conversion funnels, identify drop-off points, and optimize conversion rates for conversion optimization and user flow analysis4---5
6# Funnel Analysis
7
8## Overview
9
10Funnel analysis tracks user progression through sequential steps, identifying where users drop off and optimizing each stage for better conversion.
11
12## When to Use
13
14- When optimizing user conversion paths and improving conversion rates
15- When identifying bottlenecks and drop-off points in user flows
16- When comparing performance across different segments or traffic sources
17- When measuring product feature adoption or onboarding effectiveness
18- When improving customer journey efficiency and user experience
19- When A/B testing different funnel configurations or designs
20
21## Funnel Structure
22
23- **Stage 1**: Initial entry (landing page, app open)
24- **Stage 2-N**: Intermediate steps (signup, selection, payment)
25- **Final Stage**: Goal completion (purchase, subscription, sign-up)
26- **Drop-off**: Users not progressing to next stage
27- **Conversion Rate**: % progressing to next step
28
29## Key Metrics
30
31- **Drop-off Rate**: % leaving at each stage
32- **Conversion Rate**: % progressing per stage
33- **Funnel Efficiency**: Overall conversion (Stage 1 to Final)
34- **Friction Score**: Identifying problem areas
35
36## Implementation with Python
37
38```python
39import pandas as pd
40import numpy as np
41import matplotlib.pyplot as plt
42import seaborn as sns
43
44# Create sample funnel data
45np.random.seed(42)
46
47funnel_stages = ['Landing Page', 'Sign Up', 'Product Selection', 'Add to Cart', 'Checkout', 'Payment', 'Confirmation']
48
49# Simulate user journey (progressive drop-off)
50data = []
51users_at_stage = 100000
52for i, stage in enumerate(funnel_stages):
53 # Progressively lower retention
54 drop_off_rate = 0.15 + (i * 0.05) # Increasing drop-off
55 users_at_stage = int(users_at_stage * (1 - drop_off_rate))
56
57 for _ in range(users_at_stage):
58 data.append({
59 'user_id': f'user_{np.random.randint(0, 1000000)}',
60 'stage': stage,
61 'timestamp': np.random.randint(0, 365),
62 })
63
64df = pd.DataFrame(data)
65
66# 1. Funnel Counts
67funnel_counts = df['stage'].value_counts().reindex(funnel_stages)
68print("Funnel Counts by Stage:")
69print(funnel_counts)
70
71# 2. Funnel Metrics
72funnel_metrics = pd.DataFrame({
73 'Stage': funnel_stages,
74 'Users': funnel_counts.values,
75})
76
77funnel_metrics['Drop-off'] = funnel_metrics['Users'].shift(1) - funnel_metrics['Users']
78funnel_metrics['Drop-off %'] = (funnel_metrics['Drop-off'] / funnel_metrics['Users'].shift(1) * 100).round(2)
79funnel_metrics['Conversion %'] = (funnel_metrics['Users'] / funnel_metrics['Users'].iloc[0] * 100).round(2)
80
81print("\nFunnel Metrics:")
82print(funnel_metrics)
83
84# 3. Visualization - Funnel Chart
85fig, axes = plt.subplots(1, 2, figsize=(14, 6))
86
87# Traditional funnel visualization
88ax = axes[0]
89colors = plt.cm.RdYlGn_r(np.linspace(0.3, 0.7, len(funnel_metrics)))
90
91for idx, (stage, users) in enumerate(zip(funnel_metrics['Stage'], funnel_metrics['Users'])):
92 # Create trapezoid-like bars
93 width = users / funnel_metrics['Users'].max()
94 y_pos = len(funnel_metrics) - idx - 1
95 ax.barh(y_pos, width, left=(1 - width) / 2, height=0.6, color=colors[idx], edgecolor='black')
96 ax.text(-0.05, y_pos, stage, ha='right', va='center', fontsize=10)
97 ax.text(0.5, y_pos, f"{users:,}", ha='center', va='center', fontsize=9, fontweight='bold')
98
99ax.set_xlim(0, 1)
100ax.set_ylim(-0.5, len(funnel_metrics) - 0.5)
101ax.set_xticks([])
102ax.set_yticks([])
103ax.set_title('Conversion Funnel')
104
105# Step-by-step conversion
106ax2 = axes[1]
107x_pos = np.arange(len(funnel_stages))
108colors2 = plt.cm.Spectral(np.linspace(0, 1, len(funnel_stages)))
109
110bars = ax2.bar(x_pos, funnel_metrics['Users'], color=colors2, edgecolor='black', alpha=0.7)
111
112# Add value labels
113for i, (bar, users, conv) in enumerate(zip(bars, funnel_metrics['Users'], funnel_metrics['Conversion %'])):
114 height = bar.get_height()
115 ax2.text(bar.get_x() + bar.get_width() / 2., height,
116 f'{int(users):,}\n({conv:.1f}%)',
117 ha='center', va='bottom', fontsize=9)
118
119ax2.set_ylabel('User Count')
120ax2.set_title('Users by Stage')
121ax2.set_xticks(x_pos)
122ax2.set_xticklabels(funnel_stages, rotation=45, ha='right')
123ax2.grid(True, alpha=0.3, axis='y')
124
125plt.tight_layout()
126plt.show()
127
128# 4. Drop-off Analysis
129fig, ax = plt.subplots(figsize=(12, 6))
130
131# Filter out first stage (no drop-off from before)
132drop_off_data = funnel_metrics[1:].copy()
133drop_off_data = drop_off_data[drop_off_data['Drop-off'] > 0]
134
135colors_drop = ['#d62728' if x > drop_off_data['Drop-off'].median() else '#2ca02c'
136 for x in drop_off_data['Drop-off']]
137
138bars = ax.barh(drop_off_data['Stage'], drop_off_data['Drop-off %'], color=colors_drop, edgecolor='black')
139
140# Add value labels
141for i, (bar, drop_pct) in enumerate(zip(bars, drop_off_data['Drop-off %'])):
142 width = bar.get_width()
143 ax.text(width, bar.get_y() + bar.get_height() / 2.,
144 f'{drop_pct:.1f}%',
145 ha='left', va='center', fontsize=10, fontweight='bold')
146
147ax.set_xlabel('Drop-off Rate (%)')
148ax.set_title('Drop-off Rates by Stage')
149ax.grid(True, alpha=0.3, axis='x')
150
151plt.tight_layout()
152plt.show()
153
154# 5. Funnel Efficiency Matrix
155efficiency_matrix = funnel_metrics[['Stage', 'Conversion %']].copy()
156print("\nFunnel Efficiency (% of Initial Users):")
157print(efficiency_matrix)
158
159# 6. Stage-to-stage conversion
160fig, ax = plt.subplots(figsize=(12, 6))
161
162stage_conversion = []
163for i in range(len(funnel_metrics) - 1):
164 conversion = (funnel_metrics.iloc[i + 1]['Users'] / funnel_metrics.iloc[i]['Users'] * 100)
165 stage_conversion.append({
166 'Transition': f"{funnel_metrics.iloc[i]['Stage']}\n→ {funnel_metrics.iloc[i+1]['Stage']}",
167 'Conversion %': conversion
168 })
169
170stage_conv_df = pd.DataFrame(stage_conversion)
171colors_stage = ['#2ca02c' if x > 80 else '#ff7f0e' if x > 60 else '#d62728'
172 for x in stage_conv_df['Conversion %']]
173
174bars = ax.bar(range(len(stage_conv_df)), stage_conv_df['Conversion %'], color=colors_stage, edgecolor='black')
175
176# Add value labels
177for bar, conv in zip(bars, stage_conv_df['Conversion %']):
178 height = bar.get_height()
179 ax.text(bar.get_x() + bar.get_width() / 2., height,
180 f'{conv:.1f}%',
181 ha='center', va='bottom', fontsize=10, fontweight='bold')
182
183ax.set_ylabel('Conversion Rate (%)')
184ax.set_title('Stage-to-Stage Conversion Rates')
185ax.set_xticks(range(len(stage_conv_df)))
186ax.set_xticklabels(stage_conv_df['Transition'], fontsize=9)
187ax.set_ylim([0, 105])
188ax.axhline(y=80, color='green', linestyle='--', alpha=0.5, label='Good (80%+)')
189ax.axhline(y=60, color='orange', linestyle='--', alpha=0.5, label='Acceptable (60%+)')
190ax.legend()
191ax.grid(True, alpha=0.3, axis='y')
192
193plt.tight_layout()
194plt.show()
195
196# 7. Funnel by Segment (e.g., traffic source)
197np.random.seed(42)
198df['traffic_source'] = np.random.choice(['Organic', 'Paid', 'Direct'], len(df))
199
200# Create funnel for each segment
201fig, axes = plt.subplots(1, 3, figsize=(15, 6))
202
203for idx, source in enumerate(['Organic', 'Paid', 'Direct']):
204 df_segment = df[df['traffic_source'] == source]
205 segment_counts = df_segment['stage'].value_counts().reindex(funnel_stages)
206
207 segment_metrics = pd.DataFrame({
208 'Stage': funnel_stages,
209 'Users': segment_counts.values,
210 })
211 segment_metrics['Conversion %'] = (segment_metrics['Users'] / segment_metrics['Users'].iloc[0] * 100).round(2)
212
213 ax = axes[idx]
214 x_pos = np.arange(len(funnel_stages))
215 bars = ax.bar(x_pos, segment_metrics['Users'], color='steelblue', edgecolor='black', alpha=0.7)
216
217 for bar, conv in zip(bars, segment_metrics['Conversion %']):
218 height = bar.get_height()
219 ax.text(bar.get_x() + bar.get_width() / 2., height,
220 f'{conv:.1f}%',
221 ha='center', va='bottom', fontsize=8)
222
223 ax.set_title(f'Funnel: {source}')
224 ax.set_ylabel('Users')
225 ax.set_xticks(x_pos)
226 ax.set_xticklabels(funnel_stages, rotation=45, ha='right', fontsize=8)
227 ax.grid(True, alpha=0.3, axis='y')
228
229plt.tight_layout()
230plt.show()
231
232# 8. Comparison table of segments
233print("\nFunnel Comparison by Traffic Source:")
234comparison_data = []
235for source in ['Organic', 'Paid', 'Direct']:
236 df_segment = df[df['traffic_source'] == source]
237 segment_counts = df_segment['stage'].value_counts().reindex(funnel_stages)
238 comparison_data.append({
239 'Traffic Source': source,
240 'Landing': segment_counts.iloc[0],
241 'Sign Up': segment_counts.iloc[1],
242 'Product': segment_counts.iloc[2],
243 'Cart': segment_counts.iloc[3],
244 'Final Conv %': (segment_counts.iloc[-1] / segment_counts.iloc[0] * 100),
245 })
246
247comparison_df = pd.DataFrame(comparison_data)
248print(comparison_df.round(2))
249
250# 9. Sankey diagram representation (text-based)
251print("\nFunnel Flow Summary:")
252print("="*60)
253for i in range(len(funnel_metrics) - 1):
254 current = funnel_metrics.iloc[i]
255 next_stage = funnel_metrics.iloc[i + 1]
256 drop = current['Users'] - next_stage['Users']
257 conv_pct = (next_stage['Users'] / current['Users'] * 100)
258
259 print(f"{current['Stage']}")
260 print(f" ├─ Continue: {next_stage['Users']:>7,} ({conv_pct:>5.1f}%)")
261 print(f" └─ Drop-off: {drop:>7,} ({100-conv_pct:>5.1f}%)")
262print(f"\n{funnel_metrics.iloc[-1]['Stage']}")
263print(" └─ Completed: {0:,}".format(int(funnel_metrics.iloc[-1]['Users'])))
264
265# 10. Key insights visualization
266fig, ax = plt.subplots(figsize=(10, 6))
267ax.axis('off')
268
269insights = f"""
270FUNNEL ANALYSIS SUMMARY
271
272Total Users: {int(funnel_metrics['Users'].iloc[0]):,}
273Conversions: {int(funnel_metrics['Users'].iloc[-1]):,}
274Overall Conversion Rate: {funnel_metrics['Conversion %'].iloc[-1]:.2f}%
275
276BOTTLENECKS (Highest Drop-off):
2771. {funnel_metrics[funnel_metrics['Drop-off %'].idxmax()]['Stage']} - {funnel_metrics['Drop-off %'].max():.1f}%
2782. {funnel_metrics[funnel_metrics['Drop-off %'].nlargest(2).index[1]]['Stage']}
279
280BEST PERFORMERS (Highest Conversion):
2811. {stage_conv_df.nlargest(2, 'Conversion %').iloc[0]['Transition'].split(chr(10))[1][2:]} - {stage_conv_df['Conversion %'].nlargest(2).iloc[0]:.1f}%
2822. {stage_conv_df.nlargest(2, 'Conversion %').iloc[1]['Transition'].split(chr(10))[1][2:]} - {stage_conv_df['Conversion %'].nlargest(2).iloc[1]:.1f}%
283
284RECOMMENDATIONS:
285• Focus optimization on highest drop-off stages
286• Benchmark against industry standards
287• A/B test improvements at each stage
288• Monitor segment performance separately
289"""
290
291ax.text(0.05, 0.95, insights, transform=ax.transAxes, fontfamily='monospace',
292 fontsize=11, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
293
294plt.tight_layout()
295plt.show()
296```
297
298## Funnel Analysis Steps
299
3001. Define all stages in customer journey
3012. Count users at each stage
3023. Calculate drop-off and conversion rates
3034. Identify biggest bottlenecks
3045. Analyze by segments (traffic source, device, etc.)
3056. Benchmark against goals
3067. Prioritize optimization efforts
307
308## Common Drop-off Points
309
310- Complex signup forms
311- Unexpected fees
312- Confusing navigation
313- Payment issues
314- Technical errors
315
316## Deliverables
317
318- Funnel visualization chart
319- Drop-off analysis table
320- Stage-to-stage conversion rates
321- Segmented funnel analysis
322- Bottleneck identification
323- Actionable optimization recommendations
324- Benchmark comparison report