← Назад к вопросам
Как посчитаешь долю чатов которые сталкиваются с проблемой?
2.0 Middle🔥 191 комментариев
#SQL и базы данных#Метрики продукта
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Расчёт доли чатов со встреченной проблемой
Этот вопрос требует понимания того, как определить проблему, правильно считать события и учесть граничные случаи. Ответ зависит от контекста: что считается проблемой (ошибка, долгое время ответа, разрыв соединения), когда она возникает, и как её отследить.
Определение проблемы: Варианты
Прежде всего нужно договориться о том, что такое «проблема»:
# Вариант 1: Техническая ошибка
# Чат закончился с статусом error
has_technical_error = chat.final_status in ['error', 'connection_lost', 'timeout']
# Вариант 2: Пользовательская ошибка
# Пользователь получил неправильный ответ
has_user_error = chat.user_reported_issue == True
# Вариант 3: Долгое время ответа
# Ответ пришёл слишком долго
has_slow_response = chat.average_response_time > THRESHOLD_SECONDS
# Вариант 4: Неполная резолюция
# Проблему пользователя не решили
has_unresolved = chat.resolution_status == 'unresolved'
# Вариант 5: Комбинированный
# Любая из вышеперечисленных проблем
has_any_problem = has_technical_error or has_user_error or has_slow_response
Подход 1: Простой расчёт (базовый)
Формула:
Доля чатов с проблемой = (Количество чатов с проблемой) / (Всего чатов) × 100%
import pandas as pd
from datetime import datetime, timedelta
# Загрузили данные по чатам
chats = pd.DataFrame({
'chat_id': range(1, 101),
'user_id': range(1, 101),
'start_time': pd.date_range('2024-03-01', periods=100, freq='H'),
'end_time': pd.date_range('2024-03-01', periods=100, freq='H') + timedelta(minutes=5),
'status': ['success']*85 + ['error']*10 + ['timeout']*5,
'messages_count': [5, 3, 8, 12, 4] * 20,
'resolved': [True]*87 + [False]*13
})
# Определяем проблему: чат с ошибкой или неразрешённый
chats['has_problem'] = (chats['status'] != 'success') | (~chats['resolved'])
# Считаем долю
total_chats = len(chats)
problematic_chats = chats['has_problem'].sum()
percentage = (problematic_chats / total_chats) * 100
print(f"Всего чатов: {total_chats}")
print(f"Чатов с проблемой: {problematic_chats}")
print(f"Доля: {percentage:.1f}%")
Подход 2: По типам проблем (детальный анализ)
def analyze_chat_problems(chats_df):
"""
Анализ доли чатов с проблемами по категориям
"""
# Определяем разные типы проблем
chats_df['technical_error'] = chats_df['status'].isin(['error', 'timeout', 'connection_lost'])
chats_df['slow_response'] = chats_df['avg_response_time'] > 30 # 30 секунд
chats_df['unresolved'] = ~chats_df['resolved']
chats_df['low_satisfaction'] = chats_df['satisfaction_score'] < 3 # из 5
# Подсчитываем долю для каждого типа
total = len(chats_df)
results = {
'Технические ошибки': (chats_df['technical_error'].sum() / total) * 100,
'Долгое время ответа': (chats_df['slow_response'].sum() / total) * 100,
'Неразрешённые': (chats_df['unresolved'].sum() / total) * 100,
'Низкая удовлетворённость': (chats_df['low_satisfaction'].sum() / total) * 100,
}
print("=== ДОЛЯ ЧАТОВ С ПРОБЛЕМАМИ ===")
for problem_type, percentage in results.items():
print(f"{problem_type:30s}: {percentage:6.1f}%")
# Чаты с ЛЮБОЙ проблемой
any_problem = (
chats_df['technical_error'] |
chats_df['slow_response'] |
chats_df['unresolved'] |
chats_df['low_satisfaction']
)
any_problem_percentage = (any_problem.sum() / total) * 100
print(f"\nЧаты с ЛЮБОЙ проблемой: {any_problem_percentage:.1f}%")
# Чаты БЕЗ ПРОБЛЕМ
no_problems = ~any_problem
print(f"Чаты БЕЗ проблем: {(no_problems.sum() / total) * 100:.1f}%")
return results
Подход 3: С учётом тяжести проблемы
Одни проблемы критичнее других:
def weighted_problem_analysis(chats_df):
"""
Анализ с весами тяжести
"""
# Определяем веса (importance) для разных проблем
weights = {
'critical': 1.0, # Полная потеря
'major': 0.75, # Серьёзная проблема
'minor': 0.25, # Небольшая неудобство
'none': 0.0 # Нет проблемы
}
# Категоризируем чаты
def categorize_severity(row):
if row['status'] == 'error':
return 'critical'
elif row['status'] == 'timeout' or not row['resolved']:
return 'major'
elif row['avg_response_time'] > 30:
return 'minor'
else:
return 'none'
chats_df['severity'] = chats_df.apply(categorize_severity, axis=1)
chats_df['weight'] = chats_df['severity'].map(weights)
# Взвешенная доля проблемных чатов
weighted_problem_score = chats_df['weight'].mean() * 100
print(f"Взвешенный индекс проблемности: {weighted_problem_score:.1f}%")
print(f"\nРаспределение по тяжести:")
print(chats_df['severity'].value_counts())
return weighted_problem_score
Подход 4: SQL запрос для большого объёма данных
-- Подсчёт доли чатов с проблемами
SELECT
COUNT(*) as total_chats,
SUM(CASE
WHEN status != 'success' THEN 1
WHEN resolved = false THEN 1
WHEN response_time > 30 THEN 1
ELSE 0
END) as problematic_chats,
ROUND(SUM(CASE
WHEN status != 'success' THEN 1
WHEN resolved = false THEN 1
WHEN response_time > 30 THEN 1
ELSE 0
END) * 100.0 / COUNT(*), 2) as problem_percentage
FROM chats
WHERE created_at >= CURRENT_DATE - INTERVAL '7 days'
AND created_at < CURRENT_DATE;
-- По типам проблем
SELECT
'Technical Errors' as problem_type,
ROUND(SUM(CASE WHEN status != 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as percentage
FROM chats
UNION ALL
SELECT
'Unresolved',
ROUND(SUM(CASE WHEN resolved = false THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2)
FROM chats
UNION ALL
SELECT
'Slow Response',
ROUND(SUM(CASE WHEN response_time > 30 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2)
FROM chats;
Подход 5: Учёт сегментации
Доля может различаться по разным измерениям:
def segment_analysis(chats_df):
"""
Анализ доли проблем по сегментам
"""
# Определяем проблему
chats_df['has_problem'] = (chats_df['status'] != 'success') | (~chats_df['resolved'])
# По временам дня
print("\n=== ПО ВРЕМЕНИ ДНЯ ===")
time_segments = chats_df.groupby(chats_df['start_time'].dt.hour // 6).agg({
'has_problem': ['sum', 'count']
})
time_segments.columns = ['problems', 'total']
time_segments['percentage'] = (time_segments['problems'] / time_segments['total'] * 100).round(1)
print(time_segments)
# По типу пользователя
print("\n=== ПО ТИПУ ПОЛЬЗОВАТЕЛЯ ===")
user_segments = chats_df.groupby('user_type').agg({
'has_problem': ['sum', 'count']
})
user_segments.columns = ['problems', 'total']
user_segments['percentage'] = (user_segments['problems'] / user_segments['total'] * 100).round(1)
print(user_segments)
# По устройству
print("\n=== ПО УСТРОЙСТВУ ===")
device_segments = chats_df.groupby('device_type').agg({
'has_problem': ['sum', 'count']
})
device_segments.columns = ['problems', 'total']
device_segments['percentage'] = (device_segments['problems'] / device_segments['total'] * 100).round(1)
print(device_segments)
Подход 6: Временной тренд анализа
import matplotlib.pyplot as plt
def trend_analysis(chats_df):
"""
Как меняется доля проблем во времени
"""
chats_df['has_problem'] = (chats_df['status'] != 'success') | (~chats_df['resolved'])
# Группируем по дням
daily_stats = chats_df.groupby(chats_df['start_time'].dt.date).agg({
'has_problem': ['sum', 'count']
})
daily_stats.columns = ['problems', 'total']
daily_stats['percentage'] = (daily_stats['problems'] / daily_stats['total'] * 100)
# Визуализируем тренд
plt.figure(figsize=(12, 6))
plt.plot(daily_stats.index, daily_stats['percentage'], marker='o', linewidth=2)
plt.title('Доля чатов с проблемами (тренд)')
plt.xlabel('Дата')
plt.ylabel('Доля (%)')
plt.grid(True, alpha=0.3)
plt.show()
# Статистика
print(f"Среднее за период: {daily_stats['percentage'].mean():.1f}%")
print(f"Медиана: {daily_stats['percentage'].median():.1f}%")
print(f"Min: {daily_stats['percentage'].min():.1f}% на {daily_stats['percentage'].idxmin()}")
print(f"Max: {daily_stats['percentage'].max():.1f}% на {daily_stats['percentage'].idxmax()}")
Граничные случаи (Edge Cases)
def handle_edge_cases(chats_df):
"""
Обработка граничных случаев
"""
# 1. Пустые чаты (нет сообщений)
chats_df['is_empty'] = chats_df['messages_count'] == 0
print(f"Пустые чаты: {chats_df['is_empty'].sum()} ({chats_df['is_empty'].mean()*100:.1f}%)")
# 2. Очень длинные чаты (могут указывать на проблемы с резолюцией)
chats_df['is_too_long'] = chats_df['messages_count'] > 50
print(f"Очень длинные чаты: {chats_df['is_too_long'].sum()} ({chats_df['is_too_long'].mean()*100:.1f}%)")
# 3. Чаты которые начались но не закончились
chats_df['incomplete'] = chats_df['end_time'].isna()
print(f"Незавершённые чаты: {chats_df['incomplete'].sum()} ({chats_df['incomplete'].mean()*100:.1f}%)")
# 4. Исключаем нерелевантные чаты из подсчёта
# (если нужно считать только завершённые)
valid_chats = chats_df[~chats_df['incomplete']]
valid_problematic = valid_chats['has_problem'].sum()
valid_total = len(valid_chats)
valid_percentage = (valid_problematic / valid_total * 100) if valid_total > 0 else 0
print(f"\nДоля проблем (только завершённые): {valid_percentage:.1f}%")
print(f"Доля проблем (все чаты): {(chats_df['has_problem'].mean()*100):.1f}%")
Практический пример: Полный анализ
def complete_chat_analysis(data_source, date_range):
"""
Полный анализ доли чатов с проблемами
"""
# Загружаем данные
chats = pd.read_sql(f"""
SELECT
chat_id,
user_id,
start_time,
end_time,
status,
response_time,
resolved,
satisfaction_score,
device_type,
user_type
FROM chats
WHERE created_at BETWEEN '{date_range[0]}' AND '{date_range[1]}'
""", data_source)
# Определяем проблемы
chats['has_problem'] = (
(chats['status'] != 'success') |
(~chats['resolved']) |
(chats['response_time'] > 30) |
(chats['satisfaction_score'] < 3)
)
# Подсчитываем
total = len(chats)
problematic = chats['has_problem'].sum()
percentage = (problematic / total) * 100
# Выводим результаты
print("=== ФИНАЛЬНЫЙ ОТЧЁТ ===")
print(f"Период анализа: {date_range[0]} - {date_range[1]}")
print(f"Всего чатов: {total:,}")
print(f"Чатов с проблемой: {problematic:,}")
print(f"\nДоля чатов с проблемой: {percentage:.1f}%")
# Рекомендации
if percentage > 15:
print(f"\n⚠️ ВНИМАНИЕ: Доля проблем выше нормы (> 15%)")
print(f" Рекомендация: провести расследование и улучшение")
elif percentage > 10:
print(f"\n⚡ ВНИМАНИЕ: Доля проблем выше среднего (> 10%)")
print(f" Рекомендация: мониторить и оптимизировать")
else:
print(f"\n✓ Доля проблем в пределах нормы (< 10%)")
return {
'total': total,
'problematic': problematic,
'percentage': percentage
}
Best Practices
1. Ясное определение проблемы:
- Задокументируйте, что вы считаете проблемой
- Согласуйте с командой
2. Используйте несколько критериев:
- Не полагайтесь на один метрику
- Анализируйте по типам и категориям
3. Учтите контекст:
- Технические ошибки vs пользовательские
- Время дня, тип устройства
4. Мониторьте тренды:
- Улучшается ли со временем?
- Есть ли корреляция с развёртываниями?
5. Исключайте шум:
- Пустые чаты
- Завершённые чаты
- Нерелевантные сегменты