Анурадха прошла курс Прикладное машинное обучение и представляет свой проект в популярном наборе данных NYC Taxi Trip Duration.

Это продолжение предыдущей истории исследовательского анализа данных на том же наборе данных. Если вы еще не проверяли его, я настоятельно рекомендую вам сначала проверить это. Он может быть найден здесь". Использованный набор данных можно скачать здесь.

Здесь мы продолжим с того места, где мы уехали.

Краткий обзор выполненного EDA.

  1. Загрузили соответствующие библиотеки и набор данных.
  2. Мы выделили категориальные и непрерывные признаки.
  3. Мы выполнили универсальный анализ некоторых столбцов и двойную вариацию некоторых по отношению к целевому столбцу trip_duration. С помощью этих процессов мы выявили некоторые выбросы и присвоили им соответствующие значения.
  4. Мы пришли к определенному выводу на основе нашего EDA.

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

Мы также будем импортировать новые библиотеки Python, когда и где это необходимо.

Давайте сначала рассмотрим некоторые особенности:

Продолжительность поездки

Мы рассмотрим самые большие и самые маленькие значения Trip_Duration.

print('The value of largest 5 trip duration values are as follows : \n {} '.format(df['trip_duration'].nlargest(5)))
print('The the number of rows with 1 as their trip duration values is {}'.format(len(df[df['trip_duration']==1 ])))

Мы видим, что есть 1 очень большое значение и 13 значений с длительностью 1 секунда, что абсурдно. Следовательно, мы отбрасываем эти строки.

df=df[df.trip_duration!=df.trip_duration.max()]
df=df[df.trip_duration!=df.trip_duration.min()]

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

df['trip_duration_hour']=df['trip_duration']/3600 

Количество пассажиров

Посмотрим на частоту подсчета пассажиров.

df.passenger_count.value_counts()

Здесь количество записей с количеством пассажиров 0,9 и 7 очень мало по сравнению со всем набором данных. Следовательно, мы отбросим значения.

df=df[df.passenger_count<=6]
df=df[df.passenger_count!=0]

Pickup_datetime и Dropoff_datetime

Мы уже преобразовали эти 2 столбца в тип datetime в предыдущем рассказе. Теперь мы создаем новые столбцы, отображающие месяц и день недели, когда была совершена конкретная поездка.

df['pickup_day']=df['pickup_datetime'].dt.day_name()
df['dropoff_day']=df['dropoff_datetime'].dt.day_name()
df['pickup_month']=df['pickup_datetime'].dt.month
df['dropoff_month']=df['dropoff_datetime'].dt.month

Давайте посмотрим на распределение по месяцам получения и возврата.

df['pickup_month'].value_counts()

df['dropoff_month'].value_counts()

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

Для отправлений, совершенных в июле, мы найдем частотное распределение для соответствующего месяца отправления. Находим и соответствующую дату.

print(df[df.dropoff_month==7].pickup_datetime.dt.month.value_counts())
print(df[df.dropoff_month==7].pickup_datetime.dt.day.value_counts())

Таким образом, мы видим, что все отправления были сделаны 30 июня, а высадка - в июле. Так что с данными все в порядке.

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

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

#a function is created to calculate the distance from latitudes and longitudes
from math import radians, cos, sin, asin, sqrt
def haversine(df):
    lat1, lon1, lat2, lon2 = df.pickup_latitude,df.pickup_longitude,df.dropoff_latitude,df.dropoff_longitude 
    R = 3959.87433 # this is in miles.  For Earth radius in kilometers use 6372.8 km
dLat = radians(lat2 - lat1)
    dLon = radians(lon2 - lon1)
    lat1 = radians(lat1)
    lat2 = radians(lat2)
a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
    c = 2*asin(sqrt(a))
return R * c

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

df['distance'] = df.apply(lambda x: haversine(x), axis = 1)

Мы посмотрим на распределение этой функции расстояния в зависимости от значения trip_duration.

sns.scatterplot(x='distance',y='trip_duration',data=df)

Мы можем видеть несколько выбросов со значениями, намного превышающими 200 км, и многие значения с trip_distance = 0 км. Это могут быть строки с изображением отмененных поездок. Посмотрим, сколько существует таких аттракционов.

print('The no of rows with distance =0 are {}'.format(len(df[df.distance==0])))

Довольно много! Мы не будем отбрасывать эти строки. Вместо этого мы заменим эти данные на среднее расстояние

mean_dist=df['distance'].mean()
df.loc[df['distance']==0,'distance']=mean_dist

Теперь мы создадим новую функцию, называемую скоростью. Это поможет нам определить точки данных, в которых затраченное время и пройденное расстояние не совпадают. Мы также посмотрим на распределение скорости поездки.

df['speed']=df['distance']/df['trip_duration_hour']
sns.boxplot(df['speed'])

Здесь мы видим несколько выбросов. Средняя скорость такси в Нью-Йорке - около 11 км / час. У данных есть несколько точек данных с гораздо более высокой скоростью.

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

sns.scatterplot(x='distance',y='trip_duration_hour',data=df)

Здесь мы видим несколько точек данных, где расстояние составляет ‹20 км, а время составляет› 10 часов. Это очень абсурдно, так как средняя скорость составляет 11 км / час. Это может быть связано с сильной загруженностью дороги. Давайте трансформируем эти столбцы в журнал и снова посмотрим на распределение.

df['log_distance']=np.log(df.distance)
df['log_trip_duration']=np.log(df.trip_duration_hour)
sns.scatterplot(x='log_distance',y='log_trip_duration',data=df)

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

Таким образом, мы отбросим строки за пределами log_trip_duration ›2

df=df[df.log_trip_duration<2]

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

df.columns

Теперь мы не будем использовать их все для построения нашей модели, поскольку это сделало бы модель очень сложной. Мы создаем новый фрейм данных data2, чтобы выбрать только те функции, которые оказали некоторое влияние на целевую переменную trip_duration. Мы отбросили некоторые функции, поскольку они были преобразованы в другие функции. Мы также отказались от номинальных характеристик.

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

data2=df.loc[:,['passenger_count','store_and_fwd_flag','trip_duration', 'pickup_day', 'dropoff_day', 'pickup_month',
   'dropoff_month','pickup_timezone','dropoff_timezone','speed','log_distance','distance']]

Теперь мы преобразуем категориальные функции из фрейма данных data2 в одно горячее кодирование.

data2=pd.get_dummies(data2,columns=['store_and_fwd_flag','pickup_day','dropoff_day','pickup_month','dropoff_month','pickup_timezone', 'dropoff_timezone'])

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

Таким образом, мы видим, что некоторые функции имеют высокую корреляцию с другими функциями, а некоторые вообще не коррелированы.

Сначала мы создадим модель со средней продолжительностью поездки в качестве прогноза. Затем мы создадим модель базовой линии только с расстоянием, и у нее будет корреляция ›5 с trip_duration. Затем мы выберем другие функции, которые положительно коррелируют с trip_duration, и создадим третью модель.

Мы разделим наши данные на 2 части. Первую часть мы будем использовать для обучения наших данных, а вторую часть - для тестирования.

В первой части мы будем использовать перекрестную проверку K-Fold с использованием this. (к = 20)

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

base_line_col=['distance']
predictor_cols=['passenger_count','distance','store_and_fwd_flag_N','store_and_fwd_flag_Y',
               'pickup_day_Friday','pickup_day_Monday','pickup_day_Saturday','pickup_day_Sunday',
               'pickup_day_Thursday','pickup_day_Tuesday','pickup_day_Wednesday','dropoff_day_Friday',
               'dropoff_day_Monday','dropoff_day_Saturday','dropoff_day_Sunday','dropoff_day_Thursday',
               'dropoff_day_Tuesday','dropoff_day_Wednesday','pickup_month_1','pickup_month_5','pickup_month_6',
               'dropoff_month_1','dropoff_month_5','dropoff_month_6','pickup_timezone_late night',
               'pickup_timezone_midday','pickup_timezone_morning','dropoff_timezone_evening',
               'dropoff_timezone_late night','dropoff_timezone_midday','dropoff_timezone_morning']
target_col=['trip_duration']

Мы определим функцию, которая будет принимать объект модели, тестовые данные, данные поезда, столбцы предикторов и целевые столбцы. В качестве метрики оценки мы будем использовать среднеквадратичную ошибку. Подробнее об этом здесь.

Функция распечатает RMSE данных поезда, среднее значение RMSE в каждой кратной перекрестной проверке K-Fold и тестовые данные. Он также вернет предсказанные значения для тестовых данных. Мы импортировали необходимые библиотеки.

from sklearn import  metrics
from sklearn.model_selection import cross_val_score
def modelfit(estimator,data_train,data_test,predictors,target):
    #print(data_train.head())
    #fitting model
    estimator.fit(data_train[predictors],data_train.loc[:,target])
    #train data prediction
    train_pred=estimator.predict(data_train[predictors])
    #cross_validation score
    cv_score=cross_val_score(estimator,data_train[predictors],data_train.loc[:,target],cv=20,scoring='neg_mean_squared_error')
    
    cv_score=np.sqrt(np.abs(cv_score))
    #Print model report:
    print ("\nModel Report")
    print ("RMSE on Train Data: %.4g" % np.sqrt(metrics.mean_squared_error(data_train.loc[:,target].values, train_pred)))
    print ("CV Score : Mean - %.4g | Std - %.4g | Min - %.4g | Max - %.4g" % (np.mean(cv_score),np.std(cv_score),np.min(cv_score),np.max(cv_score)))
    
    test_pred=estimator.predict(data_test[predictors])
    print ("RMSE on Test Data: %.4g" % np.sqrt(metrics.mean_squared_error(data_test.loc[:,target].values, test_pred)))
    
    
    
    return test_pred

Теперь мы разделим данные на обучающие и тестовые данные в соотношении 80:20.

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
validation_size = 0.20
seed = 7
X_train, X_test = train_test_split(data2,test_size=validation_size, random_state=seed)

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

mean_pred=np.repeat(X_train[target_col].mean(),len(X_test[target_col]))
from sklearn.metrics import mean_squared_error as mae
sqrt(mae(X_test[target_col],mean_pred))

RMSE от этого составляет 674,5795545337122. Теперь мы будем использовать это значение в качестве базового и попытаемся достичь RMSE меньше этого.

Затем мы возьмем функцию расстояния в качестве единственного столбца-предиктора и построим модель линейной регрессии. Мы посмотрим на полученное RMSE.

alg1 = LinearRegression(normalize=True)
print('The baseline model')
y_pred=modelfit(alg1, X_train, X_test,base_line_col,target_col)
coef1 = alg1.coef_
print('The coeffient is {}'.format(coef1))

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

Теперь мы возьмем все значения столбцов-предикторов и построим регрессионную модель.

alg2 = LinearRegression(normalize=True)
y_pred=modelfit(alg2, X_train, X_test, predictor_cols,target_col)
coef1 = pd.Series(alg2.coef_[0], predictor_cols).sort_values()
coef1.plot(kind='bar', title='Model Coefficients')

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

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

residuals=y_pred-X_test[target_col]Heteroskedasticity
plt.figure(figsize=(10, 6), dpi=120, facecolor='w', edgecolor='b')
f = range(0,145510)
k = [0 for i in range(0,145510)]
plt.scatter( f, residuals, label = 'residuals')
plt.plot( f, k , color = 'red', label = 'regression line' )
plt.xlabel('fitted points ')
plt.ylabel('residuals')
plt.title('Residual plot')
plt.legend()

Здесь распределение остатков гомоскедастическое. Итак, предположение о линейной регрессии остается в силе.

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

Спасибо за прочтение!