← Назад к вопросам

Как посчитаешь долю чатов которые сталкиваются с проблемой?

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. Исключайте шум:

  • Пустые чаты
  • Завершённые чаты
  • Нерелевантные сегменты
Как посчитаешь долю чатов которые сталкиваются с проблемой? | PrepBro