<jupyter_start><jupyter_text>Определение перспективного тарифа для телеком компании**Проектная работа №3 Яндекс.Практикум - Data Science**Спринт - Статистический Анализ Данных  Описание проекта**Исходные данные:**У телеком компании «Мегалайн» два тарифных плана: «Смарт» и «Ультра». Даны данные за 2018 год по пользованию услугами связи 500 клиентов на этих тарифах.**Цель проекта:**Проанализировать поведение клиентов и сделать вывод — какой тариф приносит больше денег компании.**Описание тарифов:*** Тариф «Смарт»    * Ежемесячная плата: 550 рублей    * Включено 500 минут разговора, 50 сообщений и 15 Гб интернет-трафика    * Стоимость услуг сверх тарифного пакета:    * минута разговора: 3 рубля    * сообщение: 3 рубля    * 1 Гб интернет-трафика: 200 рублей* Тариф «Ультра»    * Ежемесячная плата: 1950 рублей    * Включено 3000 минут разговора, 1000 сообщений и 30 Гб интернет-трафика    * Стоимость услуг сверх тарифного пакета:    * минута разговора: 1 рубль    * сообщение: 1 рубль    * 1 Гб интернет-трафика: 150 рублей  Структура проекта* [1. Загрузка и изучение общей информации датасета](start)* [2. Предобработка данных](preprocessing)    * [2.1 Приведение к нужным типам и обработка пропущенных значений](nan_types)    * [2.2 Расчёт дополнительных параметров](augmentation)* [3. Анализ данных](eda)* [4. Проверка гипотез](testing)    * [4.1 Гипотеза: средняя выручка пользователей тарифов «Ультра» и «Смарт» различается](tariffs)    * [4.2 Гипотеза: средняя выручка пользователей из Москвы отличается от выручки пользователей из других регионов](cities)* [5. Общий вывод](conclusion)  1. Загрузка и изучение общей информации датасета  Импортируем необходимые библиотеки<jupyter_code>import pandas as pd
import math
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats as st<jupyter_output><empty_output><jupyter_text>Отключим scientific notation<jupyter_code>pd.set_option('display.float_format', lambda x: '%.6f' % x)<jupyter_output><empty_output><jupyter_text>Читаем файлы с данными<jupyter_code>df_calls = pd.read_csv('../datasets/calls.csv', sep=',')
df_internet = pd.read_csv('../datasets/internet.csv', sep=',')
df_messages = pd.read_csv('../datasets/messages.csv', sep=',')
df_tariffs = pd.read_csv('../datasets/tariffs.csv', sep=',')
df_users = pd.read_csv('../datasets/users.csv', sep=',')<jupyter_output><empty_output><jupyter_text>Изучим загруженные датасеты<jupyter_code>df_calls.info()
df_calls.head(5)
df_internet.info()
df_internet.head(5)
df_messages.info()
df_messages.head(5)
df_tariffs.info()
df_tariffs
df_users.info()
df_users.head(5)<jupyter_output><empty_output><jupyter_text>В датасете по пользователям есть пропущенные значения по признаку 'churn_date' Выведем в удобном формате число звонков, интернет сессий и сообщений<jupyter_code>len_dataframes = pd.Series(
    data=[df_users.shape[0], df_calls.shape[0], df_internet.shape[0], df_messages.shape[0]],
    index=['users', 'calls', 'internet', 'messages']
)
len_dataframes<jupyter_output><empty_output><jupyter_text>Выводы:* определили объём и формат данных:    * 500 клиентов    * 202607 звонков    * 149396 интернет сессий    * 123036 сообщений* пропущенные значения отсутствуют (кроме параметра churn_date в таблице users, который принимает значение NaN, если тариф действующий)* необходимо будет округлить значения истраченного трафика и длительности звонка в соответствиями с уловиями тарифов телеком компании  2. Предобработка данных  2.1 Приведение к нужным типам и обработка пропущенных значений  Обработаем таблицу calls (информация о звонках) Необходимо преобразовать столбец call_date к типу datetime<jupyter_code>df_calls['call_date'] = pd.to_datetime(df_calls['call_date'],format='%Y-%m-%d')<jupyter_output><empty_output><jupyter_text>По условиям тарифа округлим до бОльшего целого значения истраченного трафика в каждой сессии<jupyter_code>df_calls.loc[:, 'duration'] = df_calls.loc[:, 'duration'].apply(lambda x: math.ceil(x))
df_calls.head(5)<jupyter_output><empty_output><jupyter_text>Избавимся от строк в которых продолжительность звонка 0.0. Такие звонки не идут в учёт потраченных минут в тарифе и не оплачиваются, не будем искажать число звонков пропущенными вызовами<jupyter_code>df_calls = df_calls[df_calls['duration'] > 0.0].reset_index()<jupyter_output><empty_output><jupyter_text>Обработаем таблицу internet (информация об интернет-сессиях) Удалим лишнюю столбец дублирующий индекс 'Unnamed: 0'<jupyter_code>del df_internet['Unnamed: 0']<jupyter_output><empty_output><jupyter_text>Необходимо преобразовать столбец session_date к типу datetime<jupyter_code>df_internet['session_date'] = pd.to_datetime(df_internet['session_date'],format='%Y-%m-%d')<jupyter_output><empty_output><jupyter_text>По условиям тарифа округлим до бОльшего целого значения истраченного трафика в каждой сессии<jupyter_code>df_internet.loc[:, 'mb_used'] = df_internet.loc[:, 'mb_used'].apply(lambda x: math.ceil(x))<jupyter_output><empty_output><jupyter_text>Проверим есть ли строки с 0 значением использованного трафика<jupyter_code>print('Количество строк с пропущенными звонками:', df_internet[df_internet['mb_used'] == 0.0].shape[0])<jupyter_output>Количество строк с пропущенными звонками: 19598<jupyter_text>Не будем никак обрабатывать строки 0 значением трафика, так как он всё равно не будет учтён при суммировании.<jupyter_code>df_internet.head(5)<jupyter_output><empty_output><jupyter_text>Обработаем таблицу messages (информация о сообщениях) Необходимо преобразовать столбец message_date к типу datetime<jupyter_code>df_messages['message_date'] = pd.to_datetime(df_messages['message_date'],format='%Y-%m-%d')<jupyter_output><empty_output><jupyter_text>Обработаем таблицу tariffs (информация о тарифах) Имеется всего 2 строки (2 тарифа), отсутствуют пропущенные значения и установленные типы данных правильные<jupyter_code>df_tariffs<jupyter_output><empty_output><jupyter_text>Обработаем таблицу users (информация о пользователях) Пропущенные значения сhurn_date означают, что тариф ещё действовал на момент выгрузки данных<jupyter_code>df_users['reg_date'] = pd.to_datetime(df_users['reg_date'],format='%Y-%m-%d')<jupyter_output><empty_output><jupyter_text>Выведем получившиеся размеры датасетов после обработки<jupyter_code>len_dataframes = pd.Series(
    data=[df_users.shape[0], df_calls.shape[0], df_internet.shape[0], df_messages.shape[0]],
    index=['users', 'calls', 'internet', 'messages']
)
len_dataframes<jupyter_output><empty_output><jupyter_text>2.2 Расчёт дополнительных параметров  Посчитаем количество сделанных звонков и израсходованных минут разговора по месяцам Для начала добавим новый признак определяющий месяц звонка<jupyter_code>df_calls['call_month'] = df_calls['call_date'].values.astype('datetime64[M]')
df_calls<jupyter_output><empty_output><jupyter_text>Теперь сгруппируем данные по месяцам для расчёта кол-ва звонков и минут<jupyter_code>calls_month = df_calls.groupby(['user_id','call_month'])[['duration']].agg(['count','sum']).reset_index()
calls_month.columns = ['user_id', 'month', 'calls_count', 'calls_duration']
calls_month.head(5)<jupyter_output><empty_output><jupyter_text>Проверим, что общее кол-во звонков и потраченных минут совпадает в изначальной и полученной группированием таблицах<jupyter_code>print('Общее коли-во истраченных минут:', sum(df_calls['duration']), ', кол-во звонков:', df_calls.shape[0])
print('Общее коли-во истраченных минут:', sum(calls_month['calls_duration']), ', кол-во звонков:', sum(calls_month['calls_count']))<jupyter_output>Общее коли-во истраченных минут: 1450301 , кол-во звонков: 162994
Общее коли-во истраченных минут: 1450301 , кол-во звонков: 162994<jupyter_text>Посчитаем количество отправленных сообщений по месяцам<jupyter_code>df_messages['message_month'] = df_messages['message_date'].values.astype('datetime64[M]')
messages_month = df_messages.groupby(['user_id','message_month'])[['id']].agg(['count']).reset_index()
messages_month.columns = ['user_id', 'month', 'messages']
messages_month<jupyter_output><empty_output><jupyter_text>Посчитаем объём израсходованного интернет-трафика по месяцам<jupyter_code>df_internet['session_month'] = df_internet['session_date'].values.astype('datetime64[M]')
internet_month = df_internet.groupby(['user_id','session_month'])[['mb_used']].agg(['sum']).reset_index()
internet_month.columns = ['user_id', 'month', 'mb_used']
internet_month<jupyter_output><empty_output><jupyter_text>Проверим, что общее кол-во звонков и потраченных минут совпадает в изначальной и полученной группированием таблицах<jupyter_code>print(sum(df_internet['mb_used']))
print(sum(internet_month['mb_used']))<jupyter_output>55369459
55369459<jupyter_text>Посчитаем помесячную выручку с каждого пользователя<jupyter_code>df_tariffs<jupyter_output><empty_output><jupyter_text>Начнём с того, что выполним слияние таблиц по звонкам, сообщениям и интернет-трафику в одну общую таблицу<jupyter_code>merged = pd.merge(calls_month, messages_month, how='outer', on=['user_id', 'month'])
merged = pd.merge(merged, internet_month, how='outer', on=['user_id', 'month'])
merged<jupyter_output><empty_output><jupyter_text>Теперь добавим к полученной таблице столбцы с тарифом и городом пользователя из таблицы df_users. Используем стратегию 'left', чтобы исключить пользователей которые не пользовались услугами телеком компании<jupyter_code>df_full = pd.merge(merged, df_users[['user_id', 'tariff', 'city']], how='left', on=['user_id'])
df_full<jupyter_output><empty_output><jupyter_text>Заполним пропуски посчитанных значений нулями<jupyter_code>df_full[['calls_count', 'calls_duration', 'messages', 'mb_used']] = df_full[['calls_count', 'calls_duration', 'messages', 'mb_used']].fillna(0.0).astype('int')
df_full<jupyter_output><empty_output><jupyter_text>Посчитаем выручку с каждого клиента за каждый месяц и добавим в общую таблицу<jupyter_code># Функция возвращает значение выручки в зависимости от тарифа (какой-то жуткий монстр)
# Считается стоимость потраченных минут/сообщений/трафика сверх тарифа и прибавляется базовая стоимость тарифа
def calc_earnings(row):
    tariff_cond = df_tariffs['tariff_name'] == row['tariff']
    
    return (
    max(0, row['calls_duration'] - df_tariffs.loc[tariff_cond, 'minutes_included'].iloc[0]) * df_tariffs.loc[tariff_cond, 'rub_per_minute'].iloc[0]
    + max(0, row['messages'] - df_tariffs.loc[tariff_cond, 'messages_included'].iloc[0]) * df_tariffs.loc[tariff_cond, 'rub_per_message'].iloc[0]
    + max(0, row['mb_used'] - df_tariffs.loc[tariff_cond, 'mb_per_month_included'].iloc[0]) * (df_tariffs.loc[tariff_cond, 'rub_per_gb'].iloc[0] / 1024.0)
    + df_tariffs.loc[tariff_cond, 'rub_monthly_fee'].iloc[0]
    )
df_full['earnings'] = df_full.apply(calc_earnings, axis=1)
df_full<jupyter_output><empty_output><jupyter_text>Выводы:* удалили данные по звонкам длительность которых 0.0* округлили до бОльшего целого длительность звонков и количество потраченных мегабайт* привели даты к типу datetime* посчитали для каждого клиента за каждый месяц объем израсходованного трафика, количество минут разговора и количествои смс* обединили данные в итоговую таблицу в которой посчитали потраченные клиентом деньги за каждый месяц  3. Анализ данныхАнализ поведения клиентов исходя из имеющейся выборки  Посчитаем среднее количество, дисперсию и стандартное отклонение по минутам разговора, кол-ву сообщений и объёму интернет-трафика<jupyter_code>table_smart = pd.DataFrame(index=['mean', 'var', 'std'], columns=['calls_duration','messages','mb_used'])

for clmn in table_smart.columns:
    table_smart.loc['mean',clmn] = df_full.loc[df_full['tariff'] == 'smart', clmn].mean()
    table_smart.loc['var', clmn] = np.var(df_full.loc[df_full['tariff'] == 'smart', clmn])
    table_smart.loc['std', clmn] = np.sqrt(np.var(df_full.loc[df_full['tariff'] == 'smart', clmn]))
    
table_smart
table_ultra = pd.DataFrame(index=['mean', 'var', 'std'], columns=['calls_duration','messages','mb_used'])

for clmn in table_ultra.columns:
    table_ultra.loc['mean',clmn] = df_full.loc[df_full['tariff'] == 'ultra', clmn].mean()
    table_ultra.loc['var', clmn] = np.var(df_full.loc[df_full['tariff'] == 'ultra', clmn])
    table_ultra.loc['std', clmn] = np.sqrt(np.var(df_full.loc[df_full['tariff'] == 'ultra', clmn]))
    
table_ultra<jupyter_output><empty_output><jupyter_text>Можно и так составить таблицу, но мне кажется, что две отдельные таблицы удобнее<jupyter_code>df_full.pivot_table(index='tariff', values=['calls_duration','messages','mb_used'], aggfunc=['mean', np.var, np.std]).transpose()<jupyter_output><empty_output><jupyter_text>Для лучшей интерпретации результатов узнаем доли пользователей разных тарифов<jupyter_code>df_full.pivot_table(index='tariff', values='user_id', aggfunc='count')<jupyter_output><empty_output><jupyter_text>Построим гистограммы для тарифов по звонкам<jupyter_code># Функция построения гистограммы
def plot_my_hist(data=None, index=None, value=None, custom_bins=None):
    """Plot custom histogram"""
    plt.figure(figsize=(14,6))
    plt.title('Histogram of '+value, fontsize=16)
    plt.xlabel(value, fontsize=14)
    plt.ylabel("count", fontsize=14)
    if custom_bins == None:
        bins=50
    else:
        bins=custom_bins
    for i in data[index].unique():
        plt.hist(data.loc[data[index] == i, value], bins=bins, alpha=0.7)
    plt.grid(True)
    plt.legend(data[index].unique(), loc='best', fontsize=14)
    #plt.show()
plot_my_hist(df_full, index='tariff', value='calls_duration')
min_ylim, max_ylim = plt.ylim()
plt.axvline(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'minutes_included'].iloc[0], color='r', linestyle='dashed', linewidth=2)
plt.text(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'minutes_included'].iloc[0]*1.05, max_ylim*0.9, 'smart minutes included', fontsize=14)
plt.show()<jupyter_output><empty_output><jupyter_text>Довольно большая часть клиентов перерасходует лимит минут звонков на тарифе smart. Лимит на тарифе ultra не будем отображать на графике так как все (или почти все) клиенты укладываются в лимит звонков предоставляюемый тарифом.  Построим гистограммы для тарифов по сообщениям<jupyter_code>plot_my_hist(df_full, index='tariff', value='messages')
min_ylim, max_ylim = plt.ylim()
plt.axvline(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'messages_included'].iloc[0], color='r', linestyle='dashed', linewidth=2)
plt.text(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'messages_included'].iloc[0]*1.05, max_ylim*0.9, 'smart messages included', fontsize=14)
plt.show()<jupyter_output><empty_output><jupyter_text>Довольно большая часть клиентов перерасходует и лимит минут сообщений на тарифе smart. Лимит на тарифе ultra не будем отображать на графике так как все (или почти все) клиенты укладываются в лимит предоставляюемый тарифом.  Построим гистограммы для тарифов по сообщениям<jupyter_code>plot_my_hist(df_full, index='tariff', value='mb_used')
min_ylim, max_ylim = plt.ylim()
plt.axvline(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'mb_per_month_included'].iloc[0], color='r', linestyle='dashed', linewidth=2)
plt.text(df_tariffs.loc[df_tariffs['tariff_name'] == 'smart', 'mb_per_month_included'].iloc[0]*1.05, max_ylim*0.9, 'smart mb included', fontsize=14)
plt.axvline(df_tariffs.loc[df_tariffs['tariff_name'] == 'ultra', 'mb_per_month_included'].iloc[0], color='b', linestyle='dashed', linewidth=2)
plt.text(df_tariffs.loc[df_tariffs['tariff_name'] == 'ultra', 'mb_per_month_included'].iloc[0]*1.05, max_ylim*0.9, 'ultra mb included', fontsize=14)
plt.show()<jupyter_output><empty_output><jupyter_text>Вывод:* Пользователи тарифа ultra в среднем больше пользуются всеми услугами связи.* Разброс по использованию услуг связи на тарифе ultra выше, чем на тарифе smart. Dелика вероятность, что отдельно взятый клиент на тарифе ultra пользуется услугами связи меньше, чем отдельной взятый клиент на тарифе smart. Но нужно отметить, что в выборке пользователей тарифа smart более, чем в 2 раза больше, чем пользователей тарифы ultra, поэтому может быть данные по тарифу ultra менее репрезентативны.* Видны всплески на графиках около нуля по продолжительности звонков и, особенно, по количеству смс. Заметная часть пользователей пользуется только интернетом.* Есть заметный перекос вправо на графике использования смс на тарифе ultra, но клиенты укладываются в лимит на тарифе.* Гистограммы по тарифу smart представляют собой нормальное распределение со скошенностью влево по звонкам и сообщениях.* Гистограммы по тарифу ultra тоже напоминают нормальное распределение, но разброс значений значительный.* Клиенты переплачивают за используемый интернет, особенно это заметно на тарифе smart (более половины перерасходует лимит трафика).* Часть клиентов тарифа smart перерасходуют минуты и сообщения из лимита тарифа.  4. Проверка гипотез  Зададим предварительно уровень статистической значимости по которому будем делать вывод по проверке гипотез<jupyter_code>alpha = 0.05 # критический уровень статистической значимости<jupyter_output><empty_output><jupyter_text>4.1 Гипотеза: средняя выручка пользователей тарифов «Ультра» и «Смарт» различается * Нулевая гипотеза: средние выручки пользователей тарифов «Ультра» и «Смарт» РАВНЫ* Альтернативная гипотеза: средняя выручка пользователей тарифов «Ультра» и «Смарт» РАЗЛИЧАЮТСЯ<jupyter_code>results = st.ttest_ind(
    df_full.loc[df_full['tariff'] == 'ultra', 'earnings'], 
    df_full.loc[df_full['tariff'] == 'smart', 'earnings'])

print('Средняя выручка пользователей тариф ultra: ', df_full.loc[df_full['tariff'] == 'ultra', 'earnings'].mean())
print('Средняя выручка пользователей тариф smart: ', df_full.loc[df_full['tariff'] == 'smart', 'earnings'].mean())

print('p-значение:', results.pvalue)

if (results.pvalue < alpha):
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу")<jupyter_output>Средняя выручка пользователей тариф ultra:  2060.5299710501267
Средняя выручка пользователей тариф smart:  1233.3494665489009
p-значение: 1.172655564462924e-190
Отвергаем нулевую гипотезу<jupyter_text>Средние выручки отличаются почти в 2 раза, а проверка гипотезы показывает, что крайне маловероятно, что выборки по выручке равны: p-value 1 в -190 степени.   4.2 Гипотеза: средняя выручка пользователей из Москвы отличается от выручки пользователей из других регионов * Нулевая гипотеза: средние выручки пользователей Москвы и пользователей из других регионов РАВНЫ* Альтернативная гипотеза: средние выручки пользователей Москвы и пользователей из других регионов РАЗЛИЧАЮТСЯ<jupyter_code>results = st.ttest_ind(
    df_full.loc[df_full['city'] == 'Москва', 'earnings'], 
    df_full.loc[df_full['city'] != 'Москва', 'earnings'])

print('Средняя выручка пользователей в Москве:   ', df_full.loc[df_full['city'] == 'Москва', 'earnings'].mean())
print('Средняя выручка пользователей в регионах: ', df_full.loc[df_full['city'] != 'Москва', 'earnings'].mean())

print('p-значение:', results.pvalue)

if (results.pvalue < alpha):
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу")<jupyter_output>Средняя выручка пользователей в Москве:    1507.276761968085
Средняя выручка пользователей в регионах:  1482.0637267996062
p-значение: 0.47443187631524797
Не получилось отвергнуть нулевую гипотезу
{'fraction_non_alphanumeric': 0.11696784549267333, 'fraction_numerical': 0.018692072697147, 'mean_word_length': 7.6882591093117405, 'pattern_counts': {'":': 0, '<': 146, '<?xml version=': 0, '>': 145, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '37', 'dir': 'jupyter-structured-clean-dedup', 'id': '566994', 'license': 'Unlicense', 'n_tokens_mistral': 7956, 'n_tokens_neox': 8762, 'n_words': 1548, 'path': 'Tariffs_of_telecom/tariffs_analysis.ipynb', 'repo_name': 'brut0/yandex.praktikum_ds_projects', 'size': '19313'}