Competition/Kaggle

[kaggle][필사] New York City Taxi Duration (2)

bisi 2020. 10. 4. 11:16

 

이번 필사 주제는 New York City Taxi Duration 이다.

 

이 대회는 뉴욕시에서 택시 여행의 총 승차 시간을 예측하는 모델을 구축하는 것이 목표이다.

 

AiswaryaRamachandran님의 커널을 참고하여 필사를 진행했다. 

 

 

목록

 

New York City Taxi Duration (1)

더보기

1. 데이터 분석 준비

  1) data description

 

2. 데이터 살펴보기

  1) missing data 찾기

  2) 분석을 위한 새로운 컬럼 생성

 

New York City Taxi Duration (2)

더보기

3. Exploratory Data Analysis

1) HeatMap

2) 시간, 요일

3) 거리, 지역, 속도

 

New York City Taxi Duration (3)

더보기

4. Feature Engineering

5. 모델 적용

  1) 모델 세우기

  2) 선형 모델 적용

  3) 랜덤포레스트 적용

 

 

 


 

 

 

 

 

3. Exploratory Data Analysis

데이터 탐구 분석

In [14]:
plt.figure(figsize=(8,5))
sns.distplot(train['trip_duration_in_hour']).set_title("Distribution of Trip Duration")
plt.xlabel("Trip Duration (in hour)")
Out[14]:
Text(0.5, 0, 'Trip Duration (in hour)')
 
In [15]:
# 24시간 보다 더 긴 시간 여행한 컬럼 생성. 우리는 여기에다가 집중할 것이다.
outlier_trip_duration = train.loc[train['trip_duration_in_hour']>24]
outlier_trip_duration
Out[15]:
  id vendor_id pickup_datetime dropoff_datetime passenger_count pickup_longitude pickup_latitude dropoff_longitude dropoff_latitude store_and_fwd_flag ... dropoff_date dropoff_day dropoff_hour dropoff_day_of_week pickup_latitude_round3 pickup_longitude_round3 dropoff_latitude_round3 dropoff_longitude_round3 trip_distance trip_duration_in_hour
355003 id1864733 1 2016-01-05 00:19:42 2016-01-27 11:08:38 1 -73.789650 40.643559 -73.956810 40.773087 N ... 2016-01-27 27 11 Wednesday 40.644 -73.790 40.773 -73.957 20.164477 538.815556
680594 id0369307 1 2016-02-13 22:38:00 2016-03-08 15:57:38 2 -73.921677 40.735252 -73.984749 40.759979 N ... 2016-03-08 8 15 Tuesday 40.735 -73.922 40.760 -73.985 5.987182 569.327222
924150 id1325766 1 2016-01-05 06:14:15 2016-01-31 01:01:07 1 -73.983788 40.742325 -73.985489 40.727676 N ... 2016-01-31 31 1 Sunday 40.742 -73.984 40.728 -73.985 1.636411 618.781111
978383 id0053347 1 2016-02-13 22:46:52 2016-03-25 18:18:14 1 -73.783905 40.648632 -73.978271 40.750202 N ... 2016-03-25 25 18 Friday 40.649 -73.784 40.750 -73.978 19.916280 979.522778

4 rows × 25 columns

In [16]:
train['trip_duration'].sort_values(ascending=False)
Out[16]:
978383     3526282
924150     2227612
680594     2049578
355003     1939736
1234291    86392  
           ...    
1034341    1      
346102     1      
1360664    1      
1382872    1      
207497     1      
Name: trip_duration, Length: 1458644, dtype: int64
In [17]:
"""
주행 기간이 매우 높은 4개의 기록이 있지만 주행 거리는 매우 낮다. 이것들은 특이치 입니다. 
하지만 이 여행들이 시작되거나 끝나는 특별한 장소가 있을까? 트립 지속시간도 편향되어 있으니 로그 변환을 해보자.
이러한 데이터도 테스트 데이터의 일부일 수 있으므로 분석에서 제거하지 않을 것이다.
"""


plt.figure(figsize=(8,5))
sns.distplot(np.log(train['trip_duration'].values)).set_title("Distribution of Trip Duration")
plt.title("Disribution of trip duration (sec) in Log Scale")
Out[17]:
Text(0.5, 1.0, 'Disribution of trip duration (sec) in Log Scale')
 
In [18]:
"""
여행 지속시간의 로그 변환은 정규 분포를 따른다. 
대부분의 여행은 54초(4)에서 2980초(8) 사이에 있다. 
이것은 대부분의 여행이 한 시간 동안 함께 한다는 것을 나타낸다. 
그러나 1분도 안 되는 여행이 있어 자세히 살펴볼 필요가 있다. 
100시간 동안 지속되는 여행이 있는데, 택시 타기가 뉴욕 안에 있어서 이상하다.
"""
Out[18]:
'\n여행 지속시간의 로그 변환은 정규 분포를 따른다. \n대부분의 여행은 54초(4)에서 2980초(8) 사이에 있다. \n이것은 대부분의 여행이 한 시간 동안 함께 한다는 것을 나타낸다. \n그러나 1분도 안 되는 여행이 있어 자세히 살펴볼 필요가 있다. \n100시간 동안 지속되는 여행이 있는데, 택시 타기가 뉴욕 안에 있어서 이상하다.\n'
 

1) HeatMap

Heatmap of common locations from where pickup and dropoff occurs

In [19]:
pickup = train.groupby(['pickup_latitude_round3', 'pickup_longitude_round3'])['id'].count().reset_index().rename(columns={'id' : 'Num_Trips'})
In [20]:
pickup.head()
Out[20]:
  pickup_latitude_round3 pickup_longitude_round3 Num_Trips
0 34.360 -65.848 1
1 34.712 -75.354 1
2 35.082 -71.800 1
3 35.310 -72.074 1
4 36.029 -77.441 1
In [21]:
pickup_map = folium.Map(location=[40.730610, -73.935242], zoom_start=10,)
# print (pickup.shape)
In [22]:
pickup.Num_Trips.values
Out[22]:
array([1, 1, 1, ..., 1, 1, 1], dtype=int64)
In [23]:
pickup.Num_Trips = np.array(pickup.Num_Trips.values).astype('float64')
In [24]:
hm_wide = HeatMap(list(zip(pickup.pickup_latitude_round3.values, pickup.pickup_longitude_round3.values, pickup.Num_Trips.values)),
                 min_opacity=0.2,
                 radius = 5, blur =15,
                 max_zoom=1)

pickup_map.add_child(hm_wide)
pickup_map # json 오류로 출력 안됨...
Out[24]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [25]:
city_long_border = (-74.03, -73.75)
city_lat_border = (40.53, 40.85)
fig, ax = plt.subplots(ncols=1, sharex=True, sharey=True)
ax.scatter(train['pickup_longitude'], train['pickup_latitude'], color='blue', label='train', alpha=0.1)
fig.suptitle('Lat Lng of Pickups in Train Data as Scatter Plot')
ax.set_ylabel('latitude')
ax.set_xlabel('longitude')
plt.ylim(city_lat_border)
plt.xlim(city_long_border)
Out[25]:
(-74.03, -73.75)
 
In [26]:
drop=train.groupby(['dropoff_latitude_round3','dropoff_longitude_round3'])['id'].count().reset_index().rename(columns={'id':'Num_Trips'})
In [27]:
drop.head()
Out[27]:
  dropoff_latitude_round3 dropoff_longitude_round3 Num_Trips
0 32.181 -73.835 1
1 34.360 -65.848 1
2 35.174 -72.022 1
3 36.029 -77.441 1
4 36.119 -68.778 1
In [28]:
drop_map = folium.Map(location=[40.730610, -73.935242], zoom_start=10,)
hm_wide = HeatMap(list(zip(drop.dropoff_latitude_round3.values, drop.dropoff_longitude_round3.values, drop.Num_Trips)),
                 min_opacity=0.2,
                 radius=5, blur=15,
                 max_zoom=1)
drop_map.add_child(hm_wide)
drop_map
Out[28]:
Make this Notebook Trusted to load map: File -> Trust Notebook
 

Dropoff Heatmap은 Pickup과 유사하다.

 

pickup이 point로부터 왔을때, trip 시간 heatmap

In [29]:
pickup= train.groupby(['pickup_latitude_round3', 'pickup_longitude_round3'])['trip_duration'].mean().reset_index().rename(columns={'trip_duration':'Avg_Trip_duration'})
In [30]:
pickup.head()
Out[30]:
  pickup_latitude_round3 pickup_longitude_round3 Avg_Trip_duration
0 34.360 -65.848 961.0
1 34.712 -75.354 875.0
2 35.082 -71.800 611.0
3 35.310 -72.074 317.0
4 36.029 -77.441 782.0
In [31]:
pickup_map = folium.Map(location= [40.730610, -73.935242], zoom_start=10,)
hm_wide = HeatMap(list(zip(pickup.pickup_latitude_round3.values, pickup.pickup_longitude_round3.values, pickup.Avg_Trip_duration)),
                 min_opacity=0.2,
                 radius=7, blur=15,
                 max_zoom=1)
pickup_map.add_child(hm_wide)
pickup_map
Out[31]:
Make this Notebook Trusted to load map: File -> Trust Notebook
 

평균값은 트립이 JFK에서 시작할때 가장 높다. 좀더 들여다보면 맨하탄 이후임을 확인할 수 있다. JFK로부터 픽업은 높은 여행 시간을 가지는 경향이 있다.

 

2) 시간, 요일

어느 시간대가 높은가?

In [32]:
plt.figure(figsize=(8,5))
sns.countplot(x=train['pickup_hour']).set_title("Pick Hours Distribution")
Out[32]:
Text(0.5, 1.0, 'Pick Hours Distribution')
 
 

이른 아침시간에는 적고, 오후 6~8시 사이가 피크임을 알 수 있다.

In [33]:
plt.figure(figsize=(8,5))
sns.countplot(x=train['dropoff_hour']).set_title("Dropoff Hours Distribution")
Out[33]:
Text(0.5, 1.0, 'Dropoff Hours Distribution')
 
 

Drop 시간대도 pickup 시간대와 비슷하다.

 

전체 pickup 시간대 분포

In [34]:
plt.figure(figsize=(8,5))
plt.plot(train.groupby('pickup_date').count()[['id']], 'o-', label='train')
plt.title("Distribution of Pickups over time")
Out[34]:
Text(0.5, 1.0, 'Distribution of Pickups over time')
 
 

2016년 2월을 끝으로 pickup의 숫자는 떨어지는 것으로 보인다.

 

시간대 별로 trip 기간이 높은 것은 ?

In [35]:
avg_duration_hour = train.groupby(['pickup_hour'])['trip_duration'].mean()
plt.figure(figsize=(8,5))
plt.plot(train.groupby(['pickup_hour'])['trip_duration'].mean(), 'o-')
Out[35]:
[<matplotlib.lines.Line2D at 0x1ab68941948>]
 
 

10~15시간대 사이가 duration이 증가한다.

 

요일별 pickup 배분 시간

In [36]:
plt.figure(figsize=(8,5))
sns.countplot(train['pickup_day_of_week'],
              order=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'
                    , 'Saturday', 'Sunday'])
Out[36]:
<AxesSubplot:xlabel='pickup_day_of_week', ylabel='count'>
 
 

pickup 비율은 월요일이 제일 낮고, 화요일부터 증가하다가 금요일이 제일 높다.

 

요일별 평균 trip 기간

In [37]:
avg_duration_day = train.groupby(['pickup_day_of_week'])['trip_duration'].mean().reset_index().rename(columns={'trip_duration' : 'avg_trip_duration'})
In [38]:
plt.figure(figsize=(8,5))
sns.barplot(x='pickup_day_of_week', y='avg_trip_duration', data=avg_duration_day, order=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday', 'Sunday']).set_title("Avg Trip Duration vs Pickup Days of Week")
Out[38]:
Text(0.5, 1.0, 'Avg Trip Duration vs Pickup Days of Week')
 
 

목요일이 가장 높다.

 

3) 거리, 지역, 속도

Trip 거리 분포

In [39]:
train['trip_distance'].head()
Out[39]:
0    1.499697
1    1.806924
2    6.390110
3    1.486664
4    1.189521
Name: trip_distance, dtype: float64
In [40]:
train['trip_duration'].head()
Out[40]:
0    455 
1    663 
2    2124
3    429 
4    435 
Name: trip_duration, dtype: int64
In [41]:
# plt.figure(figsize=(8,5))
# sns.kdeplot(np.log(train['trip_distance'].values)).set_title("Trip Distance Distribution")
# sns.distplot(np.log(train['trip_distance'].values)).set_title("Distribution of Trip Duration") # cannot convert float infinity to integer
# sns.distplot(np.log(train['trip_distance'].values)).set_title("Trip Distance Distribution")
# plt.xlabel("Trip Distance (log)")
 

trip duration vs trip distance 비교

In [42]:
plt.scatter(np.log(train['trip_distance'].values),
            np.log(train['trip_duration'].values),
            color='blue', label='train'
            )
plt.title("Distribution of Trip Distance vs Trip Duration")
plt.xlabel("Trip Distance (log scale)")
plt.ylabel("Trip Duration (log scale)")
Out[42]:
Text(0, 0.5, 'Trip Duration (log scale)')
 
 

제공해주는 함수를 통해 여행 방향을 측정해본다.

In [43]:
 def calculateBearing(lat1, lng1, lat2, lng2):
        R=6371
        lng_delta_rad = np.radians(lng2-lng1)
        lat1, lng1, lat2, lng2 = map(np.radians, (lat1, lng1, lat2, lng2))
        y = np.sin(lng_delta_rad) * np.cos(lat2)
        x = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(lng_delta_rad)
        return np.degrees(np.arctan2(y, x))   
        
In [44]:
train['bearing'] = train.apply(lambda row : calculateBearing(row['pickup_latitude_round3'], row['pickup_longitude_round3'], row['dropoff_latitude_round3'], row['dropoff_longitude_round3']), axis=1)
# train['bearing'] = train.apply(lambda row : calculateBearing(row['pickup_latitude_round3'], row['pickup_longitude_round3'], row['dropoff_latitude_round3'], row['dropoff_longitude_round3']),axis=1)
 

bearing 분포

In [45]:
sns.kdeplot(train['bearing'])
Out[45]:
<AxesSubplot:>
 
 

Bearing vs Trip Duration

In [46]:
plt.figure(figsize=(8,5))
plt.scatter(train['bearing'].values, y=np.log(train['trip_duration'].values))
plt.xlabel("Bearing")
plt.ylabel("Trip Duration (log scale)")
Out[46]:
Text(0, 0.5, 'Trip Duration (log scale)')
 
 

-50 지점에서 outlier가 보인다.

 

Store와 FWD Flag 분포

In [47]:
train['store_and_fwd_flag'].value_counts()
Out[47]:
N    1450599
Y    8045   
Name: store_and_fwd_flag, dtype: int64
In [48]:
plt.figure(figsize=(8,5))
sns.kdeplot(np.log(train.loc[train['store_and_fwd_flag']=='Y', 'trip_duration'].values), label='Store and Fwd = Yes')
sns.kdeplot(np.log(train.loc[train['store_and_fwd_flag']=='N', 'trip_duration'].values), label='Store and Fwd = No')

plt.title("Distribution of Store and Fwd Flas vs Trip Duration(log scale)")
plt.xlabel('Trip Duration (log scale)')
plt.ylabel('Destiny')
Out[48]:
Text(0, 0.5, 'Destiny')
 
 

여행의 5~7시간 여행에서는 Store and Fwd Flag 값이 No가 더 많이 분포되어 있다.

 

클러스터내에 지역 그룹

이것은 아마 지역을 생성하는데 도움이 될것이다. 특정 지역으로부터 pickup은 아마 긴 trip 시간을 가질 수 있다.

In [49]:
coords = np.vstack((train[['pickup_latitude','pickup_longitude']].values,
                   train[['dropoff_latitude','dropoff_longitude']].values,
                   test[['pickup_latitude','pickup_longitude']].values,
                   test[['dropoff_latitude','dropoff_longitude']].values))
kmeans = KMeans(n_clusters=8, random_state=0).fit(coords)
In [50]:
train.loc[:, 'pickup_neighbourhood'] = kmeans.predict(train[['pickup_latitude', 'pickup_longitude']])
train.loc[:, 'dropoff_neighbourhood'] = kmeans.predict(train[['dropoff_latitude', 'dropoff_longitude']])
city_long_border = (-74.03, -73.75)
city_lat_border = (40.63, 40.85)
fig, ax = plt.subplots(ncols=1, sharex=True, sharey=True)
ax.scatter(train['pickup_longitude'], train['pickup_latitude'],
          c=train['pickup_neighbourhood'], label='train', alpha=0.1)

fig.suptitle('Pickup Neighbourbood')
ax.set_ylabel('latitude')
ax.set_xlabel('longitude')
plt.ylim(city_lat_border)
plt.xlim(city_long_border)
Out[50]:
(-74.03, -73.75)
 
 

예측한 값을 지도에 출력하였다.

 

각 지역안에서 pickup 수

In [51]:
plt.figure(figsize=(8,5))
sns.countplot(train['pickup_neighbourhood']).set_title("Distribution of Number of Pickups across Neighbourhood")
Out[51]:
Text(0.5, 1.0, 'Distribution of Number of Pickups across Neighbourhood')
 
 

지역 0, 3, 6 순으로 pickup 수치가 크다.

In [52]:
avg_duration_neighbourhood = train.groupby(['pickup_neighbourhood'])['trip_duration'].mean().reset_index().rename(columns={'trip_duration':'avg_trip_duration'})
plt.figure(figsize=(8,5))
sns.barplot(x='pickup_neighbourhood', y='avg_trip_duration', data=avg_duration_neighbourhood).set_title("Avg Trip Duration vs Neighbourhood")
Out[52]:
Text(0.5, 1.0, 'Avg Trip Duration vs Neighbourhood')
 
 
  • 4, 2 지역 순으로 평균 trip duration 시간이 길다.
  • 1, 5, 7은 위의 pickup neighbourhood 숫자가 0에 가까울지라도 평균 이용 기간은 높은 평균에 속한다.
 

평균 속도 분포

In [53]:
train['avg_speed_kph'] = train['trip_distance'] / train['trip_duration_in_hour']
plt.figure(figsize=(8,5))
sns.kdeplot(train['avg_speed_kph'].values).set_title("Distribution of Average Speed (in kph)")
Out[53]:
Text(0.5, 1.0, 'Distribution of Average Speed (in kph)')
 
In [54]:
print("Average speed is", np.mean(train['avg_speed_kph']), "kph")
 
Average speed is 14.434528392345062 kph
 

일주일의 요일 평균 속도 - 교통 속도를 의미함.

In [55]:
avg_speed_per_day = train.groupby(['pickup_day_of_week'])['avg_speed_kph'].mean().reset_index()
plt.figure(figsize=(8,5))
sns.barplot(x='pickup_day_of_week', y='avg_speed_kph', data=avg_speed_per_day, order=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday', 'Sunday']).set_title("Avg Speed (kph) vs Pickup Days of Week")
Out[55]:
Text(0.5, 1.0, 'Avg Speed (kph) vs Pickup Days of Week')
 
 

일요일과 월요일에 평균 속도가 높다.

 


github 소스코드 바로가기