Introduction: Why NPS Became Our Most Critical AI API Metric
I manage the AI infrastructure for a mid-sized e-commerce platform handling 50,000+ daily customer inquiries. When we launched our AI customer service system last year, we tracked response times, token costs, and accuracy—but completely ignored the human satisfaction dimension. Six months in, our system was technically "working," yet our support tickets increased by 23%. Users weren't complaining about AI errors—they were frustrated by inconsistent experiences that made them lose trust in our brand. That's when I discovered AI API NPS tracking, and it transformed how we evaluate and optimize our LLM integrations.
Net Promoter Score measures user loyalty on a simple 0-10 scale: Promoters (9-10) are loyal enthusiasts, Passives (7-8) are satisfied but vulnerable to competitors, and Detractors (0-6) are unhappy customers who may damage your reputation. For AI APIs specifically, NPS captures whether users trust the AI's responses, find interactions helpful, and would recommend your service to colleagues. HolySheep AI has emerged as a cost-effective alternative with sub-50ms latency and support for WeChat and Alipay payments, making it an attractive option for teams prioritizing both performance and user satisfaction metrics.
Understanding AI API NPS vs Traditional SaaS NPS
Traditional SaaS NPS measures long-term account health, but AI API NPS operates in real-time at the conversation level. When a user asks your RAG system a question and receives a response, they form an immediate opinion about quality, speed, and relevance. Capturing this feedback immediately—while the interaction is fresh—provides actionable data that monthly surveys simply cannot.
Key differences in AI API contexts:
- Temporal granularity: Traditional NPS surveys quarterly; AI API NPS tracks per-query satisfaction
- Failure mode analysis: AI NPS captures hallucination detection, irrelevant retrieval, and timeout failures specifically
- Model comparison: Real-time NPS allows A/B testing of GPT-4.1 ($8/MTok) vs Claude Sonnet 4.5 ($15/MTok) vs Gemini 2.5 Flash ($2.50/MTok) vs DeepSeek V3.2 ($0.42/MTok)
- Cost-satisfaction correlation: Calculate whether premium models justify their 35x cost difference through improved user satisfaction
Implementation Architecture
System Design Overview
Our implementation uses a lightweight feedback collection layer between the user interface and the HolySheep AI API. This middleware captures request metadata, response quality signals, and explicit NPS ratings without introducing measurable latency overhead. The architecture supports both synchronous feedback (thumbs up/down after each response) and delayed surveys (email follow-up for enterprise accounts).
#!/usr/bin/env python3
"""
AI API NPS Collection System
Integrates with HolySheep AI for cost-effective LLM inference
Rate: ¥1=$1 (saves 85%+ vs ¥7.3 competitors)
Latency: <50ms p99
"""
import json
import time
import sqlite3
from datetime import datetime, timedelta
from typing import Optional, Dict, List
from dataclasses import dataclass, asdict
from flask import Flask, request, jsonify
import requests
@dataclass
class NPSRecord:
"""Structured NPS feedback record"""
feedback_id: str
timestamp: str
user_id: str
session_id: str
model: str
nps_score: int # 0-10
feedback_type: str # 'explicit' | 'implicit' | 'delayed_survey'
response_latency_ms: float
tokens_used: int
cost_usd: float
query_preview: str # First 100 chars
response_preview: str # First 200 chars
was_helpful: bool
error_occurred: bool
error_type: Optional[str] = None
class HolySheepNPSCollector:
"""HolySheep AI API integration with built-in NPS tracking"""
def __init__(self, api_key: str, db_path: str = "nps_data.db"):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
self.db_path = db_path
self._init_database()
def _init_database(self):
"""Initialize SQLite database for NPS storage"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS nps_feedback (
feedback_id TEXT PRIMARY KEY,
timestamp TEXT NOT NULL,
user_id TEXT NOT NULL,
session_id TEXT NOT NULL,
model TEXT NOT NULL,
nps_score INTEGER NOT NULL,
feedback_type TEXT NOT NULL,
response_latency_ms REAL NOT NULL,
tokens_used INTEGER NOT NULL,
cost_usd REAL NOT NULL,
query_preview TEXT NOT NULL,
response_preview TEXT NOT NULL,
was_helpful INTEGER NOT NULL,
error_occurred INTEGER NOT NULL,
error_type TEXT,
INDEX idx_timestamp (timestamp),
INDEX idx_model (model),
INDEX idx_user (user_id)
)
''')
conn.commit()
conn.close()
def calculate_nps(self,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
model: Optional[str] = None) -> Dict:
"""
Calculate Net Promoter Score from collected feedback
NPS = % Promoters - % Detractors
Range: -100 to +100
"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
query = "SELECT nps_score FROM nps_feedback WHERE nps_score IS NOT NULL"
params = []
if start_date:
query += " AND timestamp >= ?"
params.append(start_date)
if end_date:
query += " AND timestamp <= ?"
params.append(end_date)
if model:
query += " AND model = ?"
params.append(model)
cursor.execute(query, params)
scores = [row[0] for row in cursor.fetchall()]
conn.close()
if not scores:
return {"nps": 0, "total_responses": 0, "breakdown": {}}
promoters = sum(1 for s in scores if s >= 9)
passives = sum(1 for s in scores if 7 <= s <= 8)
detractors = sum(1 for s in scores if s <= 6)
total = len(scores)
nps = ((promoters - detractors) / total) * 100
return {
"nps": round(nps, 2),
"total_responses": total,
"breakdown": {
"promoters": {"count": promoters, "percentage": round(promoters/total*100, 1)},
"passives": {"count": passives, "percentage": round(passives/total*100, 1)},
"detractors": {"count": detractors, "percentage": round(detractors/total*100, 1)}
},
"average_score": round(sum(scores)/len(scores), 2)
}
app = Flask(__name__)
Initialize HolySheep NPS Collector
nps_collector = HolySheepNPSCollector(
api_key="YOUR_HOLYSHEEP_API_KEY",
db_path="production_nps.db"
)
Model pricing for cost tracking (2026 rates)
MODEL_PRICING = {
"gpt-4.1": {"input": 0.002, "output": 0.008}, # $2/MTok input, $8/MTok output
"claude-sonnet-4.5": {"input": 0.003, "output": 0.015}, # $3/$15
"gemini-2.5-flash": {"input": 0.00035, "output": 0.0025}, # $0.35/$2.50
"deepseek-v3.2": {"input": 0.00007, "output": 0.00042} # $0.07/$0.42
}
@app.route('/api/chat', methods=['POST'])
def chat_with_nps_tracking():
"""AI chat endpoint with automatic NPS metadata collection"""
start_time = time.time()
data = request.json
user_id = data.get('user_id', 'anonymous')
session_id = data.get('session_id', 'unknown')
model = data.get('model', 'deepseek-v3.2') # Default to cheapest
messages = data.get('messages', [])
headers = {
"Authorization": f"Bearer {nps_collector.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": messages,
"temperature": 0.7,
"max_tokens": 2000
}
try:
# Call HolySheep AI API
response = requests.post(
f"{nps_collector.base_url}/chat/completions",
headers=headers,
json=payload,
timeout=30
)
response_data = response.json()
latency_ms = (time.time() - start_time) * 1000
# Extract usage for cost calculation
usage = response_data.get('usage', {})
input_tokens = usage.get('prompt_tokens', 0)
output_tokens = usage.get('completion_tokens', 0)
pricing = MODEL_PRICING.get(model, MODEL_PRICING['deepseek-v3.2'])
cost_usd = (input_tokens / 1_000_000 * pricing['input'] +
output_tokens / 1_000_000 * pricing['output'])
# Store NPS metadata (rating not yet collected)
nps_record = NPSRecord(
feedback_id=f"nps_{int(time.time()*1000)}",
timestamp=datetime.now().isoformat(),
user_id=user_id,
session_id=session_id,
model=model,
nps_score=-1, # Pending rating
feedback_type='pending',
response_latency_ms=round(latency_ms, 2),
tokens_used=input_tokens + output_tokens,
cost_usd=round(cost_usd, 6),
query_preview=messages[-1]['content'][:100] if messages else '',
response_preview=response_data['choices'][0]['message']['content'][:200],
was_helpful=False,
error_occurred=False
)
_store_nps_record(nps_record)
return jsonify({
"success": True,
"response": response_data,
"metadata": {
"latency_ms": round(latency_ms, 2),
"cost_usd": round(cost_usd, 6),
"feedback_id": nps_record.feedback_id,
"can_rate": True
}
})
except Exception as e:
latency_ms = (time.time() - start_time) * 1000
return jsonify({
"success": False,
"error": str(e),
"metadata": {"latency_ms": round(latency_ms, 2)}
}), 500
def _store_nps_record(record: NPSRecord):
"""Persist NPS record to database"""
conn = sqlite3.connect(nps_collector.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO nps_feedback VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
record.feedback_id,
record.timestamp,
record.user_id,
record.session_id,
record.model,
record.nps_score,
record.feedback_type,
record.response_latency_ms,
record.tokens_used,
record.cost_usd,
record.query_preview,
record.response_preview,
1 if record.was_helpful else 0,
1 if record.error_occurred else 0,
record.error_type
))
conn.commit()
conn.close()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
Frontend NPS Widget Implementation
The collection system is only as good as its user interface. We implemented a lightweight widget that captures feedback without disrupting the conversation flow. Users see a subtle prompt after receiving responses, with options for quick rating (0-10) and optional detailed feedback.
<!-- AI Chat NPS Widget - Vanilla JS Implementation -->
<div id="nps-widget" class="nps-widget" style="display: none;">
<div class="nps-header">
<span>How was this response?</span>
<button onclick="closeNPSWidget()" class="nps-close">×</button>
</div>
<div class="nps-scores">
<button class="nps-btn" onclick="submitNPS(0)">0</button>
<button class="nps-btn" onclick="submitNPS(1)">1</button>
<button class="nps-btn" onclick="submitNPS(2)">2</button>
<button class="nps-btn" onclick="submitNPS(3)">3</button>
<button class="nps-btn" onclick="submitNPS(4)">4</button>
<button class="nps-btn detractor" onclick="submitNPS(5)">5</button>
<button class="nps-btn detractor" onclick="submitNPS(6)">6</button>
<button class="nps-btn passive" onclick="submitNPS(7)">7</button>
<button class="nps-btn passive" onclick="submitNPS(8)">8</button>
<button class="nps-btn promoter" onclick="submitNPS(9)">9</button>
<button class="nps-btn promoter" onclick="submitNPS(10)">10</button>
</div>
<div class="nps-feedback" id="nps-feedback-section" style="display: none;">
<textarea id="nps-comment" placeholder="Optional: Tell us more..."></textarea>
<button onclick="submitDetailedFeedback()" class="nps-submit">Submit Feedback</button>
</div>
<div class="nps-labels">
<span>Not helpful</span>
<span>Very helpful</span>
</div>
</div>
<style>
.nps-widget {
position: fixed;
bottom: 20px;
right: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
padding: 16px;
width: 320px;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
z-index: 10000;
transition: all 0.3s ease;
}
.nps-widget.detractors {
border: 2px solid #ff4444;
}
.nps-widget.promoters {
border: 2px solid #00c853;
}
.nps-header {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-weight: 600;
color: #333;
}
.nps-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #999;
}
.nps-scores {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.nps-btn {
width: 24px;
height: 24px;
border-radius: 4px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
font-size: 11px;
transition: all 0.2s;
}
.nps-btn:hover {
transform: scale(1.1);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.nps-btn.detractor { background: #ffebee; color: #c62828; }
.nps-btn.passive { background: #fff8e1; color: #f57f17; }
.nps-btn.promoter { background: #e8f5e9; color: #2e7d32; }
.nps-labels {
display: flex;
justify-content: space-between;
font-size: 10px;
color: #999;
margin-top: 4px;
}
.nps-feedback {
margin-top: 12px;
}
.nps-feedback textarea {
width: 100%;
height: 60px;
border: 1px solid #ddd;
border-radius: 6px;
padding: 8px;
font-size: 13px;
resize: none;
}
.nps-submit {
width: 100%;
margin-top: 8px;
padding: 8px;
background: #1976d2;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
}
.nps-submit:hover { background: #1565c0; }
</style>
<script>
// NPS Widget State
let currentFeedbackId = null;
let selectedScore = null;
// Initialize NPS widget after AI response
function initNPSWidget(feedbackId, autoShowDelay = 5000) {
currentFeedbackId = feedbackId;
selectedScore = null;
// Auto-show after delay (configurable)
setTimeout(() => {
const widget = document.getElementById('nps-widget');
widget.style.display = 'block';
widget.className = 'nps-widget';
document.getElementById('nps-feedback-section').style.display = 'none';
}, autoShowDelay);
}
function submitNPS(score) {
selectedScore = score;
// Update button styles
document.querySelectorAll('.nps-btn').forEach(btn => {
btn.style.transform = '';
btn.style.boxShadow = '';
});
event.target.style.transform = 'scale(1.2)';
event.target.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
// Update widget border based on category
const widget = document.getElementById('nps-widget');
if (score <= 6) {
widget.className = 'nps-widget detractors';
} else if (score <= 8) {
widget.className = 'nps-widget passives';
} else {
widget.className = 'nps-widget promoters';
}
// Send immediate rating to backend
fetch('/api/nps/rate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
feedback_id: currentFeedbackId,
nps_score: score,
feedback_type: 'explicit'
})
});
// Show detailed feedback option for detractors and promoters
if (score <= 6 || score >= 9) {
document.getElementById('nps-feedback-section').style.display = 'block';
} else {
// Auto-close for passives after 2 seconds
setTimeout(closeNPSWidget, 2000);
}
}
function submitDetailedFeedback() {
const comment = document.getElementById('nps-comment').value;
fetch('/api/nps/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
feedback_id: currentFeedbackId,
nps_score: selectedScore,
comment: comment,
feedback_type: 'detailed'
})
});
closeNPSWidget();
}
function closeNPSWidget() {
document.getElementById('nps-widget').style.display = 'none';
currentFeedbackId = null;
}
// Hook into AI response completion
const originalFetch = window.fetch;
window.fetch = async function(...args) {
const response = await originalFetch.apply(this, args);
// If this is our chat endpoint
if (args[0]?.includes?.('/api/chat')) {
const data = await response.clone().json();
if (data.metadata?.feedback_id) {
// Widget will auto-show after 5 seconds
initNPSWidget(data.metadata.feedback_id, 5000);
}
}
return response;
};
</script>
Advanced Analytics: Correlating NPS with Technical Metrics
The real power of AI API NPS emerges when you correlate satisfaction scores with technical performance data. Our analytics pipeline identifies patterns: do users rate responses lower when latency exceeds 200ms? When token costs spike during complex queries? When models hallucinate specific entity types?
#!/usr/bin/env python3
"""
NPS Analytics Dashboard - Correlation Engine
Identifies patterns between technical metrics and user satisfaction
"""
import sqlite3
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
from scipy import stats
import numpy as np
class NPSAnalyticsEngine:
"""Advanced NPS analysis for AI API performance"""
def __init__(self, db_path: str = "production_nps.db"):
self.db_path = db_path
def get_comprehensive_report(self, days: int = 30) -> dict:
"""Generate comprehensive NPS report with correlations"""
cutoff_date = (datetime.now() - timedelta(days=days)).isoformat()
conn = sqlite3.connect(self.db_path)
# Load all data with ratings
df = pd.read_sql_query('''
SELECT * FROM nps_feedback
WHERE nps_score >= 0 AND timestamp >= ?
''', conn, params=(cutoff_date,))
conn.close()
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek
df['is_weekend'] = df['day_of_week'] >= 5
report = {
'overall_nps': self._calculate_nps(df['nps_score'].tolist()),
'response_metrics': self._analyze_response_metrics(df),
'model_comparison': self._compare_models(df),
'latency_correlation': self._correlate_with_latency(df),
'cost_satisfaction': self._analyze_cost_satisfaction(df),
'temporal_patterns': self._analyze_temporal_patterns(df),
'error_impact': self._analyze_error_impact(df),
'detractor_analysis': self._analyze_detractors(df)
}
return report
def _calculate_nps(self, scores: list) -> dict:
"""Calculate NPS from score list"""
if not scores:
return {"nps": 0, "total": 0}
promoters = sum(1 for s in scores if s >= 9)
detractors = sum(1 for s in scores if s <= 6)
total = len(scores)
nps = ((promoters - detractors) / total) * 100
return {
"nps": round(nps, 1),
"total_responses": total,
"promoters": promoters,
"passives": sum(1 for s in scores if 7 <= s <= 8),
"detractors": detractors,
"average_score": round(sum(scores) / len(scores), 2),
"median_score": round(sorted(scores)[len(scores)//2], 1)
}
def _analyze_response_metrics(self, df: pd.DataFrame) -> dict:
"""Analyze response time and quality metrics"""
return {
"avg_latency_ms": round(df['response_latency_ms'].mean(), 2),
"p50_latency_ms": round(df['response_latency_ms'].quantile(0.5), 2),
"p95_latency_ms": round(df['response_latency_ms'].quantile(0.95), 2),
"p99_latency_ms": round(df['response_latency_ms'].quantile(0.99), 2),
"avg_cost_per_request": round(df['cost_usd'].mean(), 6),
"total_cost_usd": round(df['cost_usd'].sum(), 2),
"avg_tokens_per_request": round(df['tokens_used'].mean(), 0),
"error_rate": round(df['error_occurred'].mean() * 100, 2)
}
def _compare_models(self, df: pd.DataFrame) -> dict:
"""
Compare NPS by model with cost efficiency analysis
2026 Model Pricing: GPT-4.1 $8, Claude Sonnet 4.5 $15,
Gemini 2.5 Flash $2.50, DeepSeek V3.2 $0.42
"""
model_comparison = {}
for model in df['model'].unique():
model_df = df[df['model'] == model]
scores = model_df['nps_score'].tolist()
if not scores:
continue
model_comparison[model] = {
"nps": round(self._calculate_nps(scores)['nps'], 1),
"response_count": len(scores),
"avg_latency_ms": round(model_df['response_latency_ms'].mean(), 2),
"avg_cost_per_request": round(model_df['cost_usd'].mean(), 6),
"total_cost_usd": round(model_df['cost_usd'].sum(), 2),
"nps_per_dollar": round(
self._calculate_nps(scores)['nps'] / model_df['cost_usd'].sum()
if model_df['cost_usd'].sum() > 0 else 0, 2
),
"avg_score": round(sum(scores) / len(scores), 2)
}
return model_comparison
def _correlate_with_latency(self, df: pd.DataFrame) -> dict:
"""Analyze correlation between latency and NPS"""
valid_df = df[df['nps_score'] >= 0].copy()
if len(valid_df) < 10:
return {"correlation": 0, "interpretation": "Insufficient data"}
correlation, p_value = stats.pearsonr(
valid_df['response_latency_ms'],
valid_df['nps_score']
)
# Bin latencies and calculate NPS for each bin
bins = [0, 50, 100, 200, 500, 1000, float('inf')]
labels = ['0-50ms', '50-100ms', '100-200ms', '200-500ms', '500ms-1s', '1s+']
valid_df['latency_bin'] = pd.cut(
valid_df['response_latency_ms'],
bins=bins,
labels=labels
)
latency_nps = {}
for bin_label in labels:
bin_df = valid_df[valid_df['latency_bin'] == bin_label]
if len(bin_df) > 0:
latency_nps[bin_label] = {
"nps": round(self._calculate_nps(bin_df['nps_score'].tolist())['nps'], 1),
"count": len(bin_df),
"avg_latency": round(bin_df['response_latency_ms'].mean(), 1)
}
return {
"pearson_correlation": round(correlation, 3),
"p_value": round(p_value, 4),
"interpretation": self._interpret_correlation(correlation, p_value),
"latency_bins": latency_nps,
"threshold_recommendation": self._find_latency_threshold(valid_df)
}
def _interpret_correlation(self, correlation: float, p_value: float) -> str:
"""Human-readable interpretation of correlation"""
if p_value > 0.05:
return "Not statistically significant (p > 0.05)"
if abs(correlation) < 0.1:
strength = "negligible"
elif abs(correlation) < 0.3:
strength = "weak"
elif abs(correlation) < 0.5:
strength = "moderate"
elif abs(correlation) < 0.7:
strength = "strong"
else:
strength = "very strong"
direction = "negative" if correlation < 0 else "positive"
return f"{strength.capitalize()} {direction} correlation - {'Higher latency reduces NPS' if correlation < 0 else 'Higher latency increases NPS'}"
def _find_latency_threshold(self, df: pd.DataFrame) -> dict:
"""Find latency threshold where NPS drops significantly"""
# HolySheep AI targets <50ms latency
holy_sheep_target = 50
below_50 = df[df['response_latency_ms'] <= holy_sheep_target]
above_50 = df[df['response_latency_ms'] > holy_sheep_target]
nps_below = self._calculate_nps(below_50['nps_score'].tolist())['nps'] if len(below_50) > 0 else 0
nps_above = self._calculate_nps(above_50['nps_score'].tolist())['nps'] if len(above_50) > 0 else 0
return {
"holy_sheep_target_ms": holy_sheep_target,
"nps_at_or_below_target": round(nps_below, 1),
"nps_above_target": round(nps_above, 1),
"nps_improvement": round(nps_below - nps_above, 1),
"meets_sla": nps_above < nps_below
}
def _analyze_cost_satisfaction(self, df: pd.DataFrame) -> dict:
"""Analyze relationship between cost per request and satisfaction"""
valid_df = df[df['cost_usd'] > 0].copy()
if len(valid_df) < 10:
return {"correlation": 0, "interpretation": "Insufficient data"}
correlation, p_value = stats.spearmanr(
valid_df['cost_usd'],
valid_df['nps_score']
)
# HolySheep AI offers ¥1=$1 rate (85%+ savings vs ¥7.3 competitors)
cost_bins = [0, 0.001, 0.005, 0.01, 0.05, 0.10, float('inf')]
labels = ['<$0.001', '$0.001-0.005', '$0.005-0.01', '$0.01-0.05', '$0.05-0.10', '>$0.10']
valid_df['cost_bin'] = pd.cut(valid_df['cost_usd'], bins=cost_bins, labels=labels)
cost_satisfaction = {}
for label in labels:
bin_df = valid_df[valid_df['cost_bin'] == label]
if len(bin_df) > 0:
cost_satisfaction[label] = {
"nps": round(self._calculate_nps(bin_df['nps_score'].tolist())['nps'], 1),
"count": len(bin_df),
"avg_cost": round(bin_df['cost_usd'].mean(), 6)
}
return {
"spearman_correlation": round(correlation, 3),
"p_value": round(p_value, 4),
"cost_bins": cost_satisfaction,
"value_recommendation": self._calculate_value_recommendation(cost_satisfaction)
}
def _calculate_value_recommendation(self, cost_satisfaction: dict) -> str:
"""Determine optimal cost-satisfaction balance"""
if not cost_satisfaction:
return "Insufficient data"
# Find cost tier with best NPS
best_tier = max(cost_satisfaction.items(), key=lambda x: x[1]['nps'])
# Find most cost-efficient tier (high NPS, low cost)
efficient_tiers = [
(tier, data) for tier, data in cost_satisfaction.items()
if data['nps'] > 0 and data['count'] >= 5
]
if efficient_tiers:
best_value = max(efficient_tiers, key=lambda x: x[1]['nps'] / (x[1]['avg_cost'] + 0.0001))
return f"Best satisfaction: {best_tier[0]} (NPS: {best_tier[1]['nps']}). Best value: {best_value[0]} (NPS: {best_value[1]['nps']}, Cost: ${best_value[1]['avg_cost']:.6f})"
return f"Best tier: {best_tier[0]} with NPS {best_tier[1]['nps']}"
def _analyze_temporal_patterns(self, df: pd.DataFrame) -> dict:
"""Analyze NPS patterns by time of day and day of week"""
hour_nps = {}
for hour in range(24):
hour_df = df[df['hour'] == hour]
if len(hour_df) > 0:
hour_nps[f"{hour:02d}:00"] = {
"nps": round(self._calculate_nps(hour_df['nps_score'].tolist())['nps'], 1),
"count": len(hour_df)
}
dow_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
dow_nps = {}
for dow in range(7):
dow_df = df[df['day_of_week'] == dow]
if len(dow_df) > 0:
dow_nps[dow_names[dow]] = {
"nps": round(self._calculate_nps(dow_df['nps_score'].tolist())['