Пожалуй, каждый владелец таксопарка хоть раз, но задумывался о том, стоит ли тратить больше денег на более дорогие автомобили. С одной стороны, это кажется плохой идеей, но данные бизнес-анализа доказывают обратное.
В этой статье я провел исследование поездок одной известной российской компании по предоставлению услуг такси и обнаружил интересную закономерность: чем выше класс автомобиля, тем дольше поездка, и тем выше будет чек.
Чтобы проанализировать данные, я загрузил все поездки клиентов компании, разделил их на классы и провел графический и статистический анализ с помощью Python.
#Импортируем необходимые библиотеки.
import pandas as pd
import seaborn as sns
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import matplotlib.pyplot as plt
#Загружаем датасет и смотрим что внутри.
df = pd.read_csv('dataset_taxi.csv', sep=';', decimal=',')
df.head(10)
Рис. 1: Датасет с показателями поездок в такси
Оценим размеры датасета и тип данных в нем.
print('В датасете строк/колонок:', df.shape)
df.dtypes
Рис. 2: Количество строк и колонок в датасете и типы данных в нем
Видим что все корректно.
Перед нами поставлена задача – понять как дистанции поездок влияют на выбор класса такси. Для анализа этого вопроса очевидно, что нам нужны всего 2 колонки из выборки. Создадим новый датафрейм с нужными данными.
new_df = df[['distance_km', 'offer_class_group']].copy()
new_df.head()
Рис. 3: Новый датафрейм для проведения статистического анализа
Прежде чем двигаться дальше, давайте посмотрим, содержит ли этот набор данных нулевые значения или нет.
print(new_df.isnull().sum())
Рис. 4: Количество NaN и пропущенных строк в датафрейме
Отлично! Теперь выведем описательную статистику датасета.
print(new_df.describe())
Рис. 5: Описательная статистика длительности поездок
Судя по этим данным понятно, что средняя поездка составляла 26 км и 75% поездок были на дистанцию в пределах 36 км. Однако разброс данных достаточно большой (от 0 до 138 км).
Давайте построим график сравнения частоты поездок и их длины.
freq = new_df['distance_km']
type(freq)
plt.rcParams["figure.figsize"] = (10,6)
freq.plot(kind='hist', title='График сравнения частоты поездок такси и их длины')
Рис. 6: График сравнения частоты поездок в такси и длины поездок
На графике очень хорошо видно, что большая часть поездок была в диапазоне 0-30 км. Самыми редкими были поездки на длину 70 и более километров.
Построим боксплот график распределения длины поездок.
new_df.plot(kind='box', subplots=True, layout=(2,2), sharex=False, sharey=False, figsize=[20,12])
plt.show()
Рис. 7: Боксплот распределения дистанции поездок на такси
На боксплоте еще более наглядны частоты длины поездок и выбросы.
Давайте теперь посмотрим сколько у нас было поездок длиной за границей бокса.
length40 = new_df[new_df['distance_km'] >= 40]
len40 = len(length40)
print('Больше 40 км:', len40, 'поездок.', 'Что составляет:', len40/len(df)*100, '%')
Больше 40 км: 421 поездок. Что составляет: 21.05 %
length75 = new_df[new_df['distance_km'] >= 75]
len75 = len(length75)
print('Больше 75 км:', len40, 'поездок.', 'Что составляет:', len75/len(df)*100, '%')
Больше 75 км: 421 поездок. Что составляет: 1.9 %
Давайте взглянем на распределение длины поездок по персентилям:
percentiles = [5, 10, 15, 25, 33, 50, 67, 75, 85, 90, 95]
for p in percentiles:
print(f'{p} персентиль: {np.percentile(new_df["distance_km"], p)}')
5 персентиль: 0.024
10 персентиль: 5.0863
15 персентиль: 7.226850000000001
25 персентиль: 12.177
33 персентиль: 15.170100000000001
50 персентиль: 22.8285
67 персентиль: 31.52049000000001
75 персентиль: 36.43425
85 персентиль: 44.9783
90 персентиль: 52.707100000000004
95 персентиль: 61.5578
Что-ж, анализ длины поездок говорит нам о том, что люди редко берут такси чтобы ездить на нем далеко. Поездки длиной в 61 и более километров возникали лишь в 5% случаев.
Давайте теперь вернемся к нашей гипотезе и сравним как длина поездки коррелирует с классом такси.
class_dist = px.bar(new_df, x="distance_km", y="offer_class_group", color="offer_class_group", title='Сравнение длины поездки и класса такси', width=700, height=500)
class_dist.show()
Рис. 8: Сравнение длины поездки и класса такси
По графику видно, что есть некоторая закономерность. Самыми длинными поездками оказались Premium и Comfort. То есть чем выше класс такси, тем дольше и дальше люди хотят на нем ехать.
Для наглядности давайте построим скатерплот всех поездок.
fig = px.scatter(df, x="order_gk", y="distance_km", color="offer_class_group", title='График поездок такси по классам и дистанции', width=700, height=500)
fig.show()
Рис. 9: График поездок по классам такси и дистанции
Большое число синих точек наверху свидетельствует о том, что люди заказывающие Premium действительно ездят на такси дольше и дальше. Но для подтверждения (или отрицания) нашей гипотезы “дистанции поездок не отличаются по классам” простого построения графиков недостаточно.
Давайте теперь проанализируем эти данные с помощью статистических методов. Для этого приведем текстовые данные названий классов в числовой вид:
# Создаем словарь для замены значений
replacement_dict = {
"Premium": "4",
"Comfort": "3",
"Delivery": "2",
"Economy": "1"
}
# Применяем замену с помощью словаря
new_df = new_df.replace(replacement_dict)
# Выводим первые строки DataFrame
new_df.head()/code>
Рис. 10: Энкодинг классов из строк в числа
Теперь посчитаем описательную статистику по каждому классу такси.
df_econom = new_df[(new_df['distance_km'] > 0)&(new_df['offer_class_group'] == '1')]
print('Economy class:')
df_econom.describe()
Рис. 11: Описательные статистики поездок класса такси “Эконом”
Видим что в эконом-классе средняя дистанция составляла 26 км.
df_delivery = new_df[(new_df['distance_km'] > 0)&(new_df['offer_class_group'] == '2')]
print('Delivery class:')
df_delivery.describe()
Рис. 12: Описательные статистики поездок класса такси “Деливери”
Видим что в деливери-классе средняя дистанция составляла 17,89 км.
df_comfort = new_df[(new_df['distance_km'] > 0)&(new_df['offer_class_group'] == '3')]
print('Comfort class:')
df_comfort.describe()
Рис. 13: Описательные статистики поездок “Комфорт” класса такси
В комфорт-классе средняя дистанция уже составляет 29,75 км.
df_premium = new_df[(new_df['distance_km'] > 0)&(new_df['offer_class_group'] == '4')]
print('Premium class:')
df_premium.describe()
Рис. 14: Описательные статистики поездок “Премиум” класса такси
Расстояние средней поездки такси в Премиум классе самое большое – 31 км.
Ок. Давайте теперь сравним интервалы поездок на графиках.
sns.distplot(df_econom['distance_km'], 50)
sns.distplot(df_delivery['distance_km'], 50)
sns.distplot(df_comfort['distance_km'], 50)
sns.distplot(df_premium['distance_km'], 50)
plt.title('Гистограмма распределения длины поездок по классам такси')
plt.legend(['Эконом', 'Деливери', 'Комфорт', 'Премиум'])
plt.show()
Рис. 15: Гистограмма распределения длины поездок по классам такси
На диаграмме выше мы видим что самый высокий график – класс Деливери. Однако он быстро затухает. Классы Эконом и Комфорт идут практически вровень, с небольшим преимуществом последнего. Самая очевидная разница в дистанции – между Эконом и Премиум классом. Давайте сравним их отдельно.
sns.distplot(df_econom['distance_km'], 50)
sns.distplot(df_premium['distance_km'], 50)
plt.title('Гистограмма поездок Эконом и Премиум класса')
plt.legend(['Эконом', 'Премиум'])
plt.show()
Рис. 16: Гистограмма поездок Эконом и Премиум класса
Очевидно что ответ на наш гипотетический вопрос (влияет ли класс на длину поездки) лежит в сравнении именно этих двух выборок.
Чуть позже мы сравним их статметодами. Но для начала подготовим датафрейм и проверим все ли корректно с типом данных.
new_df['offer_class_group'] = new_df['offer_class_group'].astype(int)
new_df.dtypes
Рис. 17: Типы данных в датафрейме
Везде числовые значения. Отлично! Проведем общий интервальный анализ:
new_df['slice1'] = pd.cut(new_df['distance_km'],15)
new_df.groupby('slice1', dropna=False)['offer_class_group'].agg(['mean', 'median'])
Рис. 18: Анализ значений среднего и медианы по интервалам дистанции поездок
Из данной таблицы видно, что уже с 64 километра превалируют комфорт и премиум классы. А с 92 километра начинается полное доминирование Премиум.
Давайте построим для наглядности интервальный график этих показателей.
new_df['slice1'] = pd.qcut(new_df['distance_km'],15,duplicates='drop')
statgraph = new_df.groupby('slice1', dropna=False)['offer_class_group'].agg(['count','mean'])
statgraph['mean'].plot(kind='bar', title='График взаимосвязи цены (класса) такси и длины поездки', width=2, figsize=(9, 6))
Рис. 19: График взаимосвязи цены (класса) такси и длины поездки
График подтверждает перекос в сторону более дорогих тарифов, начиная с 40 км.
И все же барчарты получились шумноватыми. Давайте построим боксплоты для большей наглядности этой разницы.
boxplot = new_df.boxplot(column=['distance_km'], by = ['offer_class_group'], figsize=[10,6])
Рис. 20: Боксплоты сравнения длины поездок в зависимости от класса такси
Пожалуй, это самый интересный график нашего исследования. На этом боксплоте очень наглядно видно преимущество Премиум класса в длинных поездках.
Давайте теперь посчитаем корреляцию этих параметров друг с другом.
new_df['distance_km'].corr(new_df['offer_class_group'])
0.15708288126873632
Корреляция между длиной поездки и классом небольшая, но она все же присутствует.
И здесь напрашивается еще провести сравнение километража Эконом и Премиум класса:
ave_eco = df_econom['distance_km'].mean()
ave_prem = df_premium['distance_km'].mean()
med_eco = df_econom['distance_km'].median()
med_prem = df_premium['distance_km'].median()
print('Средняя дистанция в Эконом: ', ave_eco)
print('Средняя дистанция в Премиум: ', ave_prem)
print('Медианная дистанция в Эконом: ', med_eco)
print('Медианная дистанция в Премиум:', med_prem)
print('Разница средних: ', ave_prem-ave_eco)
print('Разница медиан: ', med_prem-med_eco)
Средняя дистанция в Эконом: 26.283098
Средняя дистанция в Премиум: 31.166556
Медианная дистанция в Эконом: 23.798000000000002
Медианная дистанция в Премиум: 26.265
Разница средних: 4.883458000000001
Разница медиан: 2.4669999999999987
Что и требовалось доказать.
И, в заключение, давайте проведем статистический парный т-тест выборок, с помощью которого подтвердим или опровергнем нашу гипотезу о влиянии класса такси на дальность поездок.
from scipy.stats import ttest_ind
from scipy.stats import mannwhitneyu
alpha = 0.05 #уровень значимости
result = ttest_ind(df_econom['distance_km'], df_premium['distance_km'])
print('p-value: %.8f' % result.pvalue) #точность 8 знаков
if (result.pvalue < alpha):
print('дистанции поездок отличаются по классам')
else:
print('дистанции поездок не отличаются по классам')
p-value: 0.00025768
дистанции поездок отличаются по классам
Парный т-тест подтвердил нашу гипотезу. А следовательно таксопарку стоит задуматься над тем, чтобы расширять свой парк автомобилей новыми машинами высокого класса, так как это приведет к более дальним поездкам, и следовательно – увеличению среднего чека.