Дорогой друг!

Специально для тебя, есть готовое переработанное решение в формате ."IPYNB" (.ipynb), но оно платное.

При получении вами критического (красного) замечания по проекту, проведу правки.

Решение сдесь: Добавь в корзину - Перейди в корзину - Укажи Email - Оплатить по СБП - СБП (приложение вашего банка) - Получи решение на Email

Не можешь оплатить, тогда через "Помощь" запроси другую систему оплаты (Webmoney, Alipay, Telegram (USDT, TON)).

Внимательно: Ниже представлено правильное решение, но его посмотрело уже больше 5000 человек. Думаю, что им можно пользоваться только для руководства при написании самостоятельного проекта, но сдавать без правок уже нельзя, есть вероятность получить замечание от опытных ревьюеров: "Это плагиат! Переделайте решение" и потом думать как всё исправить. 

Поэтому принято решение переделать проект заново и ограничить доступ к нему введя символическую оплату. Также поступлю и с последующими проектами. За готовым решением переходи по ссылке выше.

 

Вернуться в раздел: Статистический анализ данных

Вернуться в оглавление: Я.Практикум

 

В теме:

1. Задача: Проект: Статистический анализ данных

2. Решение: Проект: Статистический анализ данных

 

Проект: Статистический анализ данных

 

Проект: Статистический анализ данных

Импортируем библиотеки
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats as st
from scipy.stats import binom

Шаг 1. Загрузка данных

1.1 Считаем CSV-файлы с данными зададим переменные, отобразим первые строки

users_go = pd.read_csv('/datasets/users_go.csv')
print('Таблица users_go: \n\n', users_go.head(), '\n')
rides_go = pd.read_csv('/datasets/rides_go.csv')
print('Таблица rides_go: \n\n', rides_go.head(), '\n')
subscriptions_go = pd.read_csv('/datasets/subscriptions_go.csv')
print('Таблица subscriptions_go: \n\n', subscriptions_go, '\n')
1.2 Изучим общую информацию о каждом датафрейме.
print(users_go.info())
print('Количество явных дубликатов в `users_go.csv` = ', users_go.duplicated().sum())
print(rides_go.info())
print('Количество явных дубликатов в `rides_go.csv` = ', rides_go.duplicated().sum())
print(subscriptions_go.info())
Построим гистограммы значимых фреймов данных
plt.hist(users_go['age'], bins=100)
plt.title('age')
plt.xlabel('Возраст (лет)')
plt.ylabel('Количество')
plt.show()

plt.hist(rides_go['distance'], bins=100)
plt.title('distance')
plt.xlabel('Расстояние (метров)')
plt.ylabel('Количество')
plt.show()

plt.hist(rides_go['duration'], bins=100)
plt.title('duration')
plt.xlabel('Продолжительность (минут)')
plt.ylabel('Количество')
plt.show()

plt.hist(rides_go['date'], bins=12)
plt.title('date')
plt.xlabel('Дата')
plt.xticks(rotation=90)
plt.ylabel('Количество')
plt.show()

Вывод:

В данных:

  • users_go 1565 записи, пропуски отсутствуют, количество явных дубликатов 31.
  • users_go 18068 записи, пропуски отсутствуют, явных дубликатов нет.
  • subscriptions_go 2 записи, пропуски отсутствуют, явных дубликатов нет. Названия столбцов в норме. Для целого ряда столбцов некорректный тип данных. Данные признаны полными и достаточными для исследования.

Шаг 2. Предобработка данных

2.1 Предобработка users_go
  • Удалим дубликаты
rides_go['distance'] = rides_go['distance'].round(2)
rides_go['duration'] = np.ceil(rides_go['duration']).astype('int')
rides_go['date'] = pd.to_datetime(rides_go['date'], format='%Y-%m-%d')
rides_go['month'] = rides_go['date'].dt.month
print('Записи с ', rides_go['date'].min(), 'по', rides_go['date'].max())

2.3 Предобработка subscriptions_go

  • Не требуется
Вывод:

Во время проведения предобработки данных:

  1. в данных users_go
    • Удалили дубликаты
  2. в данных users_go
    • Округлили distance в метрах до 2 знаков после запятой
    • Округлили duration до целых в верх в соответствии с уточненными данными (п. 5.2) и перевели в int
    • Привели столбец date к типу datetime64
    • Создали столбец month с номером месяца из date
  3. в данных subscriptions_go
    • Изменений не проводили
Шаг 3. Исследовательский анализ данных
3.1 Частота встречаемости городов в выборке
 
(
    users_go.pivot_table(index='city', values='user_id', aggfunc='count').sort_values(by='user_id', ascending=True)
    .plot(kind='barh', grid=True, figsize=(10, 5))
)
plt.title('Количество пользователей по городам')
plt.ylabel('Города')
plt.xlabel('Количество пользователей')

plt.show()
print('Количество пользователей по городам ') 
users_go.pivot_table(index='city', values='user_id', aggfunc='count').sort_values(by='user_id', ascending=False)

Вывод:

Визуализация и сортировка количества пользователей по городам указывает, что первенство держит город Пятигорск за ним Екатеринбург, Ростов-на-Дону. Замечено, что в первой половине располагаются в основном южные города, где сезон проката больше, исключение составляет г.Екатеринбург, находящийся на 2 позиции.

3.2 Соотношение пользователей с подпиской и без подписки
(
    users_go.pivot_table(index='subscription_type', values='user_id', aggfunc='count')
    .plot.pie(y='user_id', figsize=(10,10),  label='Количество пользователей', 
    autopct='%1.1f%%', startangle=45,
    title='Отношение пользователей `free` - без подписки, `ultra` - с подпиской, к общему количеству')

)
plt.axis('equal')
plt.show()

Вывод:

Количество пользователей без подписки free 54,4% превышает количество пользователей с подпиской ultra 45,6%.

3.3 Возраст пользователей
 
(
    users_go.pivot_table(index='age', values='user_id', aggfunc='count').sort_values(by='age', ascending=True)
    .plot(kind='bar' , grid=True, figsize=(10, 5))
)
plt.title('Количество пользователей по возрастам')
plt.xlabel('Возраст')
plt.ylabel('Количество')
plt.show()
print('Среднее значение возраста пользователей', users_go['age'].mean().round(1))
users_go['age'].describe()

Вывод:

Визуализация количества пользователей в зависимости от возраста указывает, что средний возраст потребителя услуги подчиняется нормальному распределению с медианой примерно в 25 лет, при этом средний возраст и медиана практически совпадают.

3.4 Расстояние, которое пользователь преодолел за одну поездку

rides_go['distance'].describe()
plt.hist(rides_go['distance'], bins=500, range=(1,5620))
plt.title('Количество поездок в зависимости от расстояния поездки')
plt.xlabel('Расстояние в метрах')
plt.ylabel('Количество поездок')
plt.show()
Вывод:

Визуализация количества поездок в зависимости от расстояния указывает, медиана поездок находится на 3133 метров первый квартиль находится на значении 2543 метра, третий квартиль на 3776 метров, Если построить гистограмму в один размах range=(1310,5009), то можно заметить нормальное распределение зависимости, но при построении гистограммы с range=(1,5620) можно заметить второй пик на 700 метров, и хотя он не попадает в основное распределение, просто откидывать его нельзя, следует иметь в виду этот факт.

3.5 Продолжительность поездок
rides_go['duration'].describe()
plt.hist(rides_go['duration'], bins=41, range=(1,41))
plt.title('Количество поездок в зависимости от продолжительности')
plt.xlabel('продолжительность поездки')
plt.ylabel('Количество поездок')
plt.show()
plt.hist(rides_go['distance'] / rides_go['duration'])
plt.title('Количество поездок с определенной скоростью')
plt.xlabel('Рассояние / Время (Метр/Мин)')
plt.ylabel('Количество поездок')
plt.show()

plt.hist(rides_go['distance'] / rides_go['duration'], bins=100, range=(1000,7000))
plt.title('Количество поездок с определенной скоростью')
plt.xlabel('Рассояние / Время (Метр/Мин)')
plt.ylabel('Количество поездок')
plt.show()

print('Максимальная скорость перемещения (метр/мин)', (rides_go['distance'] / rides_go['duration']).max())

Вывод:

Визуализация количества поездок в зависимости от продолжительности указывает, медиана поездок находится на 18 мин первый квартиль находится на значении 14 мин, третий квартиль на 22 мин. На гисторгамме замечено аномальное время поездок порядка 100 с временем менее 1 минуты. Построенные гисторгаммы указывают, что в столбце времени присутствуют аномалии. Расчет средней скорости перемещения указывает, что в некоторых случаях самокаты перемещались со средней скоростью от 4000 до 7211,01 метр/мин, но это практически невозможно. О данной аномалии необходимо указать владельцам проката для устранения. Пока такие значения оставим без изменения.

Вывод по исследовательскму анализу
  1. Услугами сервиса аренды самокатов пользуются больше всего в южных городах, где сезон проката больше, но на второй позиции оказался уральский город Екатеринбург.

  2. Количество пользователей без подписки free 54,4% превышает количество пользователей с подпиской ultra 45,6%.

  3. Количества пользователей в зависимости от возраста указывает, что средний возраст потребителя услуги подчиняется нормальному распределению с медианой примерно в 25 лет, при этом средний возраст и медиана практически совпадают.

  4. Количество поездок в зависимости от расстояния указывает, медиана поездок находится на 3133 метров первый квартиль находится на значении 2543 метра, третий квартиль на 3776 метров, Если построить гистограмму в один размах range=(1310,5009), то можно заметить нормальное распределение зависимости, но при построении гистограммы с range=(1,5620) можно заметить второй пик на 700 метров, и хотя он не попадает в основное распределение, просто откидывать его нельзя, следует иметь в виду этот факт.

  5. Визуализация количества поездок в зависимости от продолжительности указывает, медиана поездок находится на 18 мин первый квартиль находится на значении 14 мин, третий квартиль на 22 мин. На гисторгамме замечено аномальное время поездок порядка 100 с временем менее 1 минуты. Построенные гисторгаммы указывают, что в столбце времени присутствуют аномалии. Расчет средней скорости перемещения указывает, что в некоторых случаях самокаты перемещались со средней скоростью от 4000 до 7211,01 метр/мин, но это практически невозможно. О данной аномалии необходимо указать владельцам проката для устранения. Пока такие значения оставим без изменения.

Шаг 4. Объединение данных
 
4.1 Объединим данные о пользователях, поездках и подписках в один датафрейм. Для этого воспользуйтесь методом merge().
df = pd.merge(users_go, subscriptions_go, on='subscription_type')
df = pd.merge(rides_go, df, on='user_id')
print('Количество явных дубликатов в `df` = ', df.duplicated().sum(), '\n')
df.info()
Количество явных дубликатов в `df` =  0 

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 18068 entries, 0 to 18067
Data columns (total 12 columns):
 #   Column             Non-Null Count  Dtype         
---  ------             --------------  -----         
 0   user_id            18068 non-null  int64         
 1   distance           18068 non-null  float64       
 2   duration           18068 non-null  int32         
 3   date               18068 non-null  datetime64[ns]
 4   month              18068 non-null  int32         
 5   name               18068 non-null  object        
 6   age                18068 non-null  int64         
 7   city               18068 non-null  object        
 8   subscription_type  18068 non-null  object        
 9   minute_price       18068 non-null  int64         
 10  start_ride_price   18068 non-null  int64         
 11  subscription_fee   18068 non-null  int64         
dtypes: datetime64[ns](1), float64(1), int32(2), int64(5), object(3)
memory usage: 1.5+ MB
df.head()
 
  user_id distance duration date month name age city subscription_type minute_price start_ride_price subscription_fee
0 1 4409.92 26 2021-01-01 1 Кира 22 Тюмень ultra 6 0 199
1 1 2617.59 16 2021-01-18 1 Кира 22 Тюмень ultra 6 0 199
2 1 754.16 7 2021-04-20 4 Кира 22 Тюмень ultra 6 0 199
3 1 2694.78 19 2021-08-11 8 Кира 22 Тюмень ultra 6 0 199
4 1 4028.69 27 2021-08-28 8 Кира 22 Тюмень ultra 6 0 199

Вывод:

Созданный датафрейм df объединяющий таблицы rides_go, users_go, subscriptions_go в своем составе имеет 18068 строк во всех столбцах, что соответствует исходной таблице rides_go и указывает на отсутствие пропусков в столбцах. Дополнительная произведена проверка на явные дубликаты.

резюмируя вышесказанное : Объединение таблиц прошло успешно.

4.2 Создадим ещё два датафрейма из датафрейма, созданного на этапе 4.1:
4.2.1 С данными о пользователях без подписки
df_free = df[df['subscription_type'] == 'free']
df_free.head()
4.2.2 С данными о пользователях с подпиской
df_ultra = df[df['subscription_type'] == 'ultra']
df_ultra.head()
 
if len(df) == (len(df_free) + len(df_ultra)):
    print('Разделение прошло успешно')
else:
    print('ВНИМАНИЕ проверь код, что то пошло не так')
4.3 Визуализируем информацию о расстоянии и времени поездок для пользователей обеих категорий.
plt.hist([df_free['distance'], df_ultra['distance']], bins=50, range=(1,5600), label=['free', 'ultra'])
plt.legend(loc='upper right')
plt.title('Количество поездок в зависимости расстояния поездки')
plt.xlabel('Расстояние в метрах')
plt.ylabel('Количество поездок')
plt.show()
 
plt.hist([df_free['duration'], df_ultra['duration']], bins=30, range=(1,30), label=['free', 'ultra'])
plt.legend(loc='upper right')
plt.title('Количество поездок в зависимости от продолжительности')
plt.xlabel('продолжительность поездки')
plt.ylabel('Количество поездок')
plt.show()
df_free['distance'].describe()
df_ultra['distance'].describe()
df_free['duration'].describe()
df_ultra['duration'].describe()

Вывод по объединению и визуализации данных пользователей без подписки free и с подпиской ultra

  1. Визуализация созданных датафреймов зависимости количества поездок на определенное расстояние и описательная статистика (.describe()) по пользователям без подписки free и с подпиской ultra указывает, что подписка влияет на медианные значения (расстояния поезки без подписки 3114.65 метров с подпиской 3148.64 метров), при этом заметно что у пользователей с подпиской меньший разброс ( видно из графиков и по стандартному отклонению free - 1246,17 ultra - 836.89), также заметно смешение центра дополнительного пика с 500 метров (free) до 1000 (ultra).

  2. Визуализация созданных датафреймов зависимости количества поездок за определенное время и описательная статистика (.describe()) по пользователям без подписки free и с подпиской ultra указывает, что подписка влияет на медианные значения (время поезки без подписки 18 мин с подпиской 19 мин), так же замечено снижение стандартного отклонения free - 6.33 ultra - 5.57.

Резюмируя : Подписка влияет на расстояние поездок и на время в пути

Шаг 5. Подсчёт выручки

5.1 Создадим датафрейм с агрегированными данными о поездках на основе датафрейма с объединёнными данными из шага 4: найдем суммарное расстояние, количество поездок и суммарное время для каждого пользователя за каждый месяц.
users_months_revenue = df.pivot_table(
    index=('user_id', 'month'), 
    values=(
        'distance', 
        'duration', 
        'minute_price', 
        'start_ride_price', 
        'subscription_fee'
    ), 
    aggfunc=({'distance':{'count', 'sum'}, 
             'duration':'sum',
             'minute_price': 'mean',
             'start_ride_price':'mean',
             'subscription_fee':'mean',})
)

users_months_revenue.columns = [
    'count', 'distance', 
    'duration', 
    'minute_price',
    'start_ride_price', 
    'subscription_fee'
]


users_months_revenue.head()
5.2 В этот же датафрейм добавим столбец с помесячной выручкой, которую принёс каждый пользователь.
users_months_revenue['revenue'] = (
users_months_revenue['start_ride_price']*users_months_revenue['count']+
users_months_revenue['minute_price']*users_months_revenue['duration']+
users_months_revenue['subscription_fee'])

print('Записи с ', df['date'].min(), 'по', df['date'].max())
print ('Выручка за 2021 год составила :',users_months_revenue['revenue'].sum(), 'руб.')

Вывод по подсчету выручки

Выручка сервиса аренды самокатов "GoFast" за год составила 3 878 641.0 руб.

Шаг 6. Проверка гипотез
6.1 Определим, тратят ли пользователи с подпиской больше времени на поездки?

Если да, то пользователи с подпиской могут быть «выгоднее» для компании.

Выдвинем гипотезы:

  • Н0: Средняя продолжительность поездок с подпиской ultra и без free равна
  • Н1: Средняя продолжительность поездок с подпиской ultra больше продолжительности поездок без подписок free

Проверим гипотезы, используем исходные данные о продолжительности каждой сессии — отдельно для подписчиков и тех, у кого нет подписки.

results = st.ttest_ind(
    df_ultra['duration'], 
    df_free['duration'], 
    equal_var=True, 
    alternative='greater')
print(results.pvalue)

alpha = 0.05
if results.pvalue < alpha:
    print('Отвергаем нулевую гипотезу')
else:
    print('Не получилось отвергнуть нулевую гипотезу')

Вывод по тратам с подпиской и без подписки

Нулевая гипотеза отвергнута, есть основания утверждать, что средняя продолжительность поездки с подпиской больше продолжительности поездок без подписки при установленном уровне значимости в 5 %, хотя на гистограммах, это практически не прослеживается.

6.2 Оптимальное растояние поездки с точки зрения износа самоката для пользователей с подпиской

Расстояние одной поездки в 3130 метров — оптимальное с точки зрения износа самоката.

Установим действительно ли, что расстояние, которое проезжают пользователи с подпиской за одну поездку, не превышает 3130 метров?

Выдвинем гипотезы:

  • Н0: Среднее расстояние поездок с подпиской ultra равно оптимальному расстоянию в 3130 метров.
  • Н1: Среднее расстояние поездок с подпиской ultra больше оптимального в 3130 метров.

Проверим гипотезы, используем исходные данные о расстоянии в для подписчиков.

distance = 3130
alpha = 0.05

results = st.ttest_1samp(
    df_ultra['distance'], 
    distance,
    alternative='greater')

print(results.pvalue)

if results.pvalue < alpha:
    print('Отвергаем нулевую гипотезу')
else:
    print('Не получилось отвергнуть нулевую гипотезу')

Вывод по оптимальному растоянию поездки с точки зрения износа самоката для пользователей с подпиской.

Нулевую гипотезу неудалось отвергнуть и следовательно, есть основания утверждать, что среднее расстояние поездки, пользователей с подпиской, не превышает оптимальное расстоянием в 3130 метров с точки зрения износа самоката, при установленном уровне значимости в 5 %.

6.3. Определим, будет ли помесячная выручка от пользователей с подпиской по месяцам выше, чем выручка от пользователей без подписки.

Выдвинем гипотезы:

  • Н0: Помесячная средняя выручка от пользователей с подпиской равна помесячной средней выручке от пользователей без подписки.
  • Н1: Помесячная средняя выручка от пользователей с подпиской больше помесячной средней выручки от пользователей без подписки.

Проверим гипотезы, используем данные о выручке с подпиской и без подписки.

results = st.ttest_ind(
    users_months_revenue.loc[users_months_revenue['subscription_fee'] > 0, 'revenue'], 
    users_months_revenue.loc[users_months_revenue['subscription_fee'] == 0, 'revenue'], 
    equal_var=True, 
    alternative='greater')
print(results.pvalue)

alpha = 0.05
if results.pvalue < alpha:
    print('Отвергаем нулевую гипотезу')
else:
    print('Не получилось отвергнуть нулевую гипотезу')

Вывод по месячной выручке от пользователей с подпиской и без подписки.

Нулевая гипотеза отвергнута, есть основания утверждать, что средняя месячная выручка от пользователей с подпиской, больше средней месячной выручки от пользователей без подписки, при установленном уровне значимости в 5 %.

6.4. Разберем ситуацию: техническая команда сервиса обновила сервера, с которыми взаимодействует мобильное приложение. Она надеется, что из-за этого количество обращений в техподдержку значимо снизилось. Некоторый файл содержит для каждого пользователя данные о количестве обращений до обновления и после него. Какой тест вам понадобился бы для проверки этой гипотезы?
 
Теоретически это две зависимые выборки, иначе - парные. Если в данных некоторая переменная измеряется дважды (количество обращений до обновления и после него) для одних и тех же объектов (пользователь) и имеют одинаковые размеры, тогда можно применить метод scipy.stats.ttest_rel(), при проверке на то что, истинное среднее значение генеральной выборки до изменения больше, чем истинное среднее значение генеральной совокупности после изменения следует указать alternative="less".

Если требования к зависимым выборкам не выполняются, можно воспользоваться методом scipy.stats.ttest_ind().

Вывод по проверке гипотез

  1. Нулевая гипотеза отвергнута, есть основания утверждать, что средняя продолжительность поездки с подпиской больше продолжительности поездок без подписки при установленном уровне значимости в 5 %, хотя на гистограммах, это практически не прослеживается.

  2. Нулевую гипотезу неудалось отвергнуть и следовательно, есть основания утверждать, что среднее расстояние поездки, пользователей с подпиской, не превышает оптимальное расстоянием в 3130 метров с точки зрения износа самоката, при установленном уровне значимости в 5 %.

  3. Нулевая гипотеза отвергнута, есть основания утверждать, что средняя месячная выручка от пользователей с подпиской, больше средней месячной выручки от пользователей без подписки, при установленном уровне значимости в 5 %.

  4. Дано пояснение о необходимости применения метода scipy.stats.ttest_rel(), для сравнения зависимых выборок, удвлетаоряющих условию что, переменная измеряется дважды (количество обращений до обновления и после него) для одних и тех же объектов (пользователь) и имеют одинаковые размеры. При проверке на то что, истинное среднее значение генеральной выборки до изменения больше, чем истинное среднее значение генеральной совокупности после изменения следует указать alternative="less". Если же требования к зависимым выборкам не выполняются, можно воспользоваться методом scipy.stats.ttest_ind().

Шаг 7. Распределения
 
7.1 Какое количество промокодов нужно раздать на один бесплатный месяц, чтобы в следующем месяце как минимум 100 существующих клиентов продлили подписку.
 

По условию задачи:

По завершении периода действия подписки пользователь может либо отказаться от неё, либо продлить, совершив соответствующий платёж.

Эта акция уже проводилась ранее и по итогу выяснилось, что после бесплатного пробного периода подписку продлевают 10 % пользователей. Выясните, какое минимальное количество промокодов нужно разослать, чтобы вероятность не выполнить план была примерно 5 %. Подберите параметры распределения, описывающего эту ситуацию, постройте график распределения и сформулируйте ответ на вопрос о количестве промокодов.

n = 1000
p = 0.1
k = 99
binom_cdf = 0.05

while binom_cdf < binom.cdf(k, n, p):
    n += 1
print(f'При раздаче промокодов в количестве {n} вероятность, не выполнить план меньше {binom.cdf(k, n, p).round(2)*100} %')

# зададим мат.ожидание и ст.отклонение нормального распределения равными
# мат.ожиданию и ст.отклонению биномиального распределения
mu = n * p
sigma = (n * p * (1 - p))**0.5

arange = np.arange((mu - 4 * sigma),(mu + 4 * sigma),1)

plt.plot(arange, st.norm.pdf(arange, mu, sigma), 'b-')
plt.axvline(x=k, color='red')
plt.axvline(x=mu, color='green')
plt.show()

Вывод

Необходимо довести до подписчиков как минимум 1161 промокода, для того чтобы как минимум 100 существующих клиентов продлили подписку (продлевают подписку 10% пролучивших промокод) с вероятностью 5% не выполнить план.

7.2 Оценить вероятность того, что уведомление откроют не более 399,5 тыс. пользователей, если разослать 1 млн уведомлений

По условию задачи:

Отдел маркетинга рассылает клиентам push-уведомления в мобильном приложении. Клиенты могут открыть его или не открывать. Известно, что уведомления открывают около 40 % получивших клиентов.

# параметры биномиального распределения
n = 1000000
p = 0.4
k = 399500
# зададим мат.ожидание и ст.отклонение нормального распределения равными
# мат.ожиданию и ст.отклонению биномиального распределения
mu = n * p
sigma = (n * p * (1 - p))**0.5

# задаём нормальное распределение
distr = st.norm(mu, sigma) 

# считаем вероятность хвоста слева от 399.5 тысяч
result = distr.cdf(k)
print('Вероятность открытия сообщений:', result)

arange = np.arange((mu - 4 * sigma),(mu + 4 * sigma),1)
plt.plot(arange, st.norm.pdf(arange, mu, sigma), 'b-')
plt.xticks(rotation=90)
plt.axvline(x=k, color='red')
plt.axvline(x=mu, color='green')
plt.show()

Вывод

Если разослать 1 млн уведомлени (откроют только 40 % получивших клиентов), тогда вероятность того, что уведомление откроют не более 399,5 тыс. пользователей составит 0.1537 или 15,37 %.

Общий вывод

В процессе исследования данных о пользователях популярного сервиса аренды самокатов 'GoFast', были выполнены следующие этапы и сделаны выводы:

  1. Загрузка данных
  • users_go 1565 записи, пропуски отсутствуют, количество явных дубликатов 31.
  • users_go 18068 записи, пропуски отсутствуют, явных дубликатов нет.
  • subscriptions_go 2 записи, пропуски отсутствуют, явных дубликатов нет. Названия столбцов в норме. Для целого ряда столбцов некорректный тип данных. Данные признаны полными и достаточными для исследования.
  1. Предобработка данных
  • в данных users_go
    • Удалили дубликаты
  • в данных users_go
    • Округлили distance в метрах до 2 знаков после запятой
    • Округлили duration до целых в верх в соответствии с уточненными данными (п. 5.2) и перевели в int
    • Привели столбец date к типу datetime64
    • Создали столбец month с номером месяца из date
  • в данных subscriptions_go
    • Изменений не проводили
  1. Исследовательский анализ данных
  • Услугами сервиса аренды самокатов пользуются больше всего в южных городах, где сезон проката больше, но на второй позиции оказался уральский город Екатеринбург.
  • Количество пользователей без подписки free 54,4% превышает количество пользователей с подпиской ultra 45,6%.
  • Количества пользователей в зависимости от возраста указывает, что средний возраст потребителя услуги подчиняется нормальному распределению с медианой примерно в 25 лет, при этом средний возраст и медиана практически совпадают.
  • Количество поездок в зависимости от расстояния указывает, медиана поездок находится на 3133 метров первый квартиль находится на значении 2543 метра, третий квартиль на 3776 метров, Если построить гистограмму в один размах range=(1310,5009), то можно заметить нормальное распределение зависимости, но при построении гистограммы с range=(1,5620) можно заметить второй пик на 700 метров, и хотя он не попадает в основное распределение, просто откидывать его нельзя, следует иметь в виду этот факт.
  • Визуализация количества поездок в зависимости от продолжительности указывает, медиана поездок находится на 18 мин первый квартиль находится на значении 14 мин, третий квартиль на 22 мин. На гисторгамме замечено аномальное время поездок порядка 100 с временем менее 1 минуты. Построенные гисторгаммы указывают, что в столбце времени присутствуют аномалии. Расчет средней скорости перемещения указывает, что в некоторых случаях самокаты перемещались со средней скоростью от 4000 до 7211,01 метр/мин, но это практически невозможно. О данной аномалии необходимо указать владельцам проката для устранения. Пока такие значения оставли без изменения.
  1. Объединение данных
  • Визуализация созданных датафреймов зависимости количества поездок на определенное расстояние и описательная статистика (.describe()) по пользователям без подписки free и с подпиской ultra указывает, что подписка влияет на медианные значения (расстояния поезки без подписки 3114.65 метров с подпиской 3148.64 метров), при этом заметно что у пользователей с подпиской меньший разброс ( видно из графиков и по стандартному отклонению free - 1246,17 ultra - 836.89), также заметно смешение центра дополнительного пика с 500 метров (free) до 1000 (ultra).
  • Визуализация созданных датафреймов зависимости количества поездок за определенное время и описательная статистика (.describe()) по пользователям без подписки free и с подпиской ultra указывает, что подписка влияет на медианные значения (время поезки без подписки 18 мин с подпиской 19 мин), так же замечено снижение стандартного отклонения free - 6.33 ultra - 5.57.

Резюмируя : Подписка влияет на расстояние поездок и на время в пути

  1. Подсчёт выручки
  • Выручка сервиса аренды самокатов "GoFast" за год составила 3 878 641.0.0 руб.
  1. Проверка гипотез
  • Нулевая гипотеза отвергнута, есть основания утверждать, что средняя продолжительность поездки с подпиской больше продолжительности поездок без подписки при установленном уровне значимости в 5 %, хотя на гистограммах, это практически не прослеживается.
  • Нулевую гипотезу неудалось отвергнуть и следовательно, есть основания утверждать, что среднее расстояние поездки, пользователей с подпиской, не превышает оптимальное расстоянием в 3130 метров с точки зрения износа самоката, при установленном уровне значимости в 5 %.
  • Нулевая гипотеза отвергнута, есть основания утверждать, что средняя месячная выручка от пользователей с подпиской, больше средней месячной выручки от пользователей без подписки, при установленном уровне значимости в 5 %.
  • Дано пояснение о необходимости применения метода scipy.stats.ttest_rel(), для сравнения зависимых выборок, удвлетаоряющих условию что, переменная измеряется дважды (количество обращений до обновления и после него) для одних и тех же объектов (пользователь) и имеют одинаковые размеры. При проверке на то что, истинное среднее значение генеральной выборки до изменения больше, чем истинное среднее значение генеральной совокупности после изменения следует указать alternative="less". Если же требования к зависимым выборкам не выполняются, можно воспользоваться методом scipy.stats.ttest_ind().
  1. Распределения
  • Необходимо довести до подписчиков как минимум 1161 промокода, для того чтобы как минимум 100 существующих клиентов продлили подписку (продлевают подписку 10% пролучивших промокод) с вероятностью 5% не выполнить план.
  • Если разослать 1 млн уведомлени (откроют только 40 % получивших клиентов), тогда вероятность того, что уведомление откроют не более 399,5 тыс. пользователей составит 0.1537 или 15,37 %.

Резюмируя :

Да пользователи с подпиской являются более выгодными клиентами для сервиса аренды самокатов, в связи с этим руководству сервиса желательно принять меры для перехода клиентов на подписку!

Дорогой друг!

Я внимательно наблюдал за твоими действиями на сайте через вебвизор.

Если ты добрался до этой страницы, то скорее всего ты проходишь платное обучение на курсах.

Благодаря моим стараниям, целую главу ты можешь закрыть за сутки, а то и быстрее.

Думаю, что сэкономленное время, ты потратил с пользой, в то время как я сидел и пыхтел над заданиями.

Лучшая благодарность от тебя, это пожертвования на мой IT - проект.

Жертвуй столько, сколько не жалко, но и не скупись, чтобы было не стыдно.