Что ж, с тех пор, как я познакомился с этой плотной сетью технологий, у меня появилось много вопросов, на которые требовались ответы. Один из них заключался в том, что мой тариф на такси уже определялся всякий раз, когда я заказывал поездку на OLA или Uber. Мало того, что они указали цену за проезд, она часто менялась в зависимости от времени и трафика. Думаю, я получил свой ответ, теперь пора объяснить вам всем, как мы это делаем!

Итак, как обычно, я загрузил некоторую базу данных в Kaggle, чтобы немного повеселиться и узнать больше. Мое внимание привлекла одна красавица, где основная задача этой базы данных заключалась в прогнозировании тарифа гонщика. Фишка в том, что данные буквально огромны и состоят из 55 миллионов записей. Теперь, когда мне было очень любопытно узнать больше об этом наборе данных, я решил срезать некоторые углы, и вот мы:

Кстати, вы можете найти набор данных здесь

Импортировать соответствующие библиотеки

import numpy as np 
import pandas as pd
import sklearn
import seaborn as sns
import matplotlib.pyplot as plt

Как упоминалось ранее, данный набор данных состоит из 55 миллионов записей, и на самом деле было невозможно загрузить все данные за один раз, поэтому после обращения к некоторым блокнотам я решил взять 20%, что составляет 11 миллионов записей. Этого было бы достаточно для целей обучения, хотя мы можем взять любую выборку данных, но 55 миллионов - это слишком много. Я использовал функцию nrows. Для тех, кто действительно заинтересован в загрузке всего резервуара, здесь - вот как это можно сделать.

Загрузка набора данных

train = pd.read_csv("../input/new-york-city-taxi-fare-prediction/train.csv", nrows = 1000000)
test = pd.read_csv("../input/new-york-city-taxi-fare-prediction/test.csv")

Обучающий набор состоит из 1000000 строк и 8 столбцов, а набор для тестирования состоит из 9914 записей и 7 столбцов.

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

Предварительная обработка данных

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

train.isnull().sum()

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

train = train.dropna(how = 'any', axis = 'rows')

test.isnull().sum()

Я так неуклюже, тороплюсь найти все пустые записи, что совершенно забыл взглянуть на наш набор данных, чтобы узнать больше о наборе данных и его атрибутах

train.head()

Итак, как видно из фрейма данных, есть 7 независимых столбцов и один зависимый столбец - fare_amount.

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

train[‘fare_amount’].describe()

Минимальное значение fare_amount составляет -44 $ (кажется нереальным).

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

train = train.drop(train[train['fare_amount']<0].index, axis=0)

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

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

Хотя я не специалист по геологии, после поисков я кое-что нашел:

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

train = train.drop(((train[train['dropoff_latitude']<-90])|(train[train['dropoff_latitude']>90])).index, axis=0)
train = train.drop(((train[train['dropoff_longitude']<-180])|(train[train['dropoff_longitude']>180])).index, axis=0)

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

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

Это будет очень полезно - хотя я не думал, что это гений. Поскольку вместо использования калькулятора расстояния или евклидова расстояния, которые могут замедлить наш тренировочный процесс, мы могли бы создать столбец с разницей в долготе и широте точек взятия и сброса, потому что разница в 1 между любыми двумя долготами или широтой означает 66 миль. А какова общая территория Нью-Йорка?

train['diff_lat'] = ( train['dropoff_latitude'] - train['pickup_latitude']).abs()
train['diff_long'] = (train['dropoff_longitude'] - train['pickup_longitude'] ).abs()

Как видно из таблицы, все значения находятся в диапазоне от 0 до 1, и это должно быть так, поскольку такси в основном используются для передвижения по городу, и, как упоминалось выше, разница в 1 эквивалентна 66 милям.

Пройдя через это, у меня возникло любопытство проверить, есть ли какие-то комбинации, которые имеют разницу более чем на 1, давайте посмотрим

plot = train.iloc[:2000].plot.scatter('diff_long', 'diff_lat')

Пришло время устранить наш выброс:

train = train[(train.diff_long < 5.0) & (train.diff_lat < 5.0)]

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

train['passenger_count'].describe()

Я думаю, нам тоже нужно избавиться от этого выброса

train = train.drop(train[train['passenger_count']==208].index, axis = 0)

Линейная регрессия

Я недавно открыл новую возможность после ссылки на одно из ядер, и я собираюсь поделиться ею с вами здесь:

Мы собираемся использовать библиотечную функцию numpy lstsq, чтобы найти столбец w с оптимальным весом. Но каков оптимальный вес?

Вот простая математика, которая интегрирована в функцию numpy lstsq

Допустим, вместо A у нас есть X (набор всех зависимых переменных), а b - это y (набор всех независимых переменных), поэтому x и y будут нашими оптимальными весами или w.

Это будет очень полезно и эффективно для повышения точности и эффективности нашей модели. Чтобы реализовать приведенную выше формулу, нам сначала нужно преобразовать наш фрейм данных в матрицу, а также добавить столбец 1 (единицы) в нашу матрицу для постоянного члена. Давайте создадим функцию для реализации всех вышеуказанных ограничений:

def get_input_matrix(df):
    return np.column_stack((df.diff_long, df.diff_lat, np.ones(len(df))))
train_X = get_input_matrix(train)
train_y = np.array(train['fare_amount'])

Наконец, мы готовы к последнему этапу нашей фазы обучения:

(w, _, _, _) = np.linalg.lstsq(train_X, train_y, rcond = None)
print(w)

А для тех, кто еще не понял, как мы получили это w, я собираюсь объяснить это еще раз, поскольку это действительно важно в нашей тренировочной части.

Xw = y, просто рассмотрите третий столбец X как все единицы, так что у нас есть X, у нас будут все diff_lat, diff_long, 1 и y будут иметь fare_amount, поэтому наш w будет a, имеющий значения x, y, z как

(3 X 3) . (3 X 1) = (3 X 1).

Фаза тестирования

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

#get_input_matrix is the function which we have made above.To convert dataframe to matrix.
test_X = get_input_matrix(test)
test_y = np.matmul(test_X, w).round(decimals = 2)

Полученные результаты

submission = pd.DataFrame()
submission["key"] = test.key
submission["fare_amount"] = test_y
submission.to_csv('submission.csv', index = False)

Возможности для улучшений

Что ж, чем больше, тем лучше. Это означает, что это всего лишь результат 20% данных. Если мы увеличим долю данных, результаты однозначно увеличатся, но скорость вычислений обязательно снизится!

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

А вот ссылка на полный код для справки.