Анализ поездок в такси с помощью статистических методов

Пожалуй, каждый владелец таксопарка хоть раз, но задумывался о том, стоит ли тратить больше денег на более дорогие автомобили. С одной стороны, это кажется плохой идеей, но данные бизнес-анализа доказывают обратное.

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

Чтобы проанализировать данные, я загрузил все поездки клиентов компании, разделил их на классы и провел графический и статистический анализ с помощью 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())

Количество NaN и пропущенных строк в датафрейме

Рис. 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 и более километров.

Читайте также:  Как находить точки роста онлайн-бизнеса с помощью Python

Построим боксплот график распределения длины поездок.

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 км.

Читайте также:  Находим неочевидные инсайты с визуализацией продаж в Tableau

Ок. Давайте теперь сравним интервалы поездок на графиках.

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: Боксплоты сравнения длины поездок в зависимости от класса такси

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

Читайте также:  Анализ данных с Python на примере исследования изменения температуры в мире и России

Давайте теперь посчитаем корреляцию этих параметров друг с другом.

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
дистанции поездок отличаются по классам

Парный т-тест подтвердил нашу гипотезу. А следовательно таксопарку стоит задуматься над тем, чтобы расширять свой парк автомобилей новыми машинами высокого класса, так как это приведет к более дальним поездкам, и следовательно – увеличению среднего чека.