Hoy compartiré con ustedes el tercer proyecto de competencia de kaggle, New-York-City-Taxi-Fare-Prediction. La característica de este proyecto es que el conjunto de datos que se nos proporciona es relativamente grande, 5.3G, y la cantidad total de datos es 5400W filas. Sin embargo, cuando estamos haciendo este proyecto, no necesitamos tantos datos. Echemos un vistazo a este proyecto juntos.
Parte 1. Importación de datos y análisis preliminar
Primero importe nuestro conjunto de datos. Debido a la gran cantidad de datos, solo importamos las primeras filas de datos de 500W para modelar.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
train = pd.read_csv('train.csv',nrows=5000000)
test = pd.read_csv('test.csv')
test_ids = test['key']
train.head()
Se puede ver que nuestra cantidad de características de datos esta vez sigue siendo relativamente pequeña, aunque la cantidad total de datos es grande, solo hay 8 características.
train.info()
key: index
fare_amount: precio
pickup_datetime: hora
en que el taxi recibe un invitado pickup_longitude: longitud
a la salida
pickup_latitude: latitud a la salida dropoff_longitude: longitud
a la llegada dropoff_latitude: latitud a la llegada cuenta_de_pasajeros
: número de pasajeros
train.describe()
Los datos encerrados en el círculo rojo son un valor atípico: precios negativos, el número mínimo de pasajeros es 0 y el número máximo de pasajeros es 208.
Los datos trazados por la línea horizontal son desconcertantes: ¿Qué tipo de viaje tendrá un costo de alquiler de 1270?
Parte 2. Análisis de datos
Primero observe la distribución de las características del precio:
train.fare_amount.hist(bins=100,figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")
train[train.fare_amount <100 ].fare_amount.hist(bins=100, figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")
train[train.fare_amount >=100 ].fare_amount.hist(bins=100, figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")
train[train.fare_amount <100].shape
train[train.fare_amount >=100].shape
A partir del código y diagrama anteriores, podemos sacar varias conclusiones:
1. La distribución de precios está mayoritariamente dentro de 100, una pequeña parte está por encima de 100
y los precios dentro de 100 se concentran principalmente entre 0 y 20, 3
y fuera de 100 . La mayoría de los precios se concentran en torno a 200, y algunos precios relativamente altos pueden ser valores atípicos o pueden ser precios para el aeropuerto.
A continuación, observe la distribución del número de pasajeros:
train.passenger_count.hist(bins=100,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")
train[train.passenger_count<10].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")
train[train.passenger_count<7].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")
train[train.passenger_count>7].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")
train[train.passenger_count >7]
train[train.passenger_count ==0].shape
plt.figure(figsize= (16,8))
sns.boxplot(x = train[train.passenger_count< 7].passenger_count, y = train.fare_amount)
train[train.passenger_count <7][['fare_amount','passenger_count']].corr()
A partir del código y el gráfico anteriores, podemos sacar varias conclusiones:
1. La distribución del número de personas está mayormente dentro de 7, una pequeña parte está fuera
de 7. 2. En los datos con el número de personas fuera de 7, faltan la mayoría de las coordenadas de datos y el número de personas es 208
3. Hay 17,602 datos que indican que el número de pasajeros es 0, puede ser un taxi que transporta mercancías, o puede que falten datos.
4. En el cuadro de caja se puede ver que el precio promedio de los taxis con menos de 7 se acerca a
5. Use la interfaz .corr () para verificar que la correlación entre el número de pasajeros y el monto de la tarifa no sea alta, solo 0.013
Parte 3. Procesamiento de datos
1. Procesamiento de valor nulo
train.isnull().sum()#找出空值
train = train.dropna(how='any', axis=0)
36 valores perdidos son insignificantes para nuestro volumen de datos de 500 W, así que elijo eliminar directamente los valores perdidos
test = pd.read_csv('test.csv')
test_ids = test['key']
test.head()
test.isnull().sum()
Haga lo mismo con el equipo de prueba, pero no faltan valores en el equipo de prueba
2. Manejo de valores atípicos
train = train[train.fare_amount>=0]
Eliminar datos con precios negativos
3. Ingeniería de
funciones ① Acorte el alcance del conjunto de entrenamiento Debido a que la cantidad de datos en el conjunto de entrenamiento es relativamente grande, podemos reducir el conjunto de entrenamiento de
acuerdo con el rango de coordenadas del conjunto de prueba.
print(min(test.pickup_longitude.min(),test.dropoff_longitude.min()))
print(max(test.pickup_longitude.max(),test.dropoff_longitude.max()))
print(min(test.pickup_latitude.min(),test.dropoff_latitude.min()))
print(max(test.pickup_latitude.max(),test.dropoff_latitude.max()))
Obtenga -74,2 a -73 como rango de selección de longitud y 40,5 a 41,8 como rango de selección de latitud
def select_train(df, fw):
return (df.pickup_longitude >= fw[0]) & (df.pickup_longitude <= fw[1]) & \
(df.pickup_latitude >= fw[2]) & (df.pickup_latitude <= fw[3]) & \
(df.dropoff_longitude >= fw[0]) & (df.dropoff_longitude <= fw[1]) & \
(df.dropoff_latitude >= fw[2]) & (df.dropoff_latitude <= fw[3])
fw = (-74.2, -73, 40.5, 41.8)
train = train[select_train(train, fw)]
Use select_train para reducir los datos del conjunto de entrenamiento
②Construcción de nuevas características de hora. Las
características de hora originales no son adecuadas para que las usemos directamente. Teniendo en cuenta que los taxis pueden aumentar los precios en diferentes períodos de tiempo, años y meses, necesitamos extraer nuevos años y meses de las características de hora originales. , El día y la hora son novedades de nuestro modelo.
def deal_time_features(df):
df['pickup_datetime'] = df['pickup_datetime'].str.slice(0, 16)
df['pickup_datetime'] = pd.to_datetime(df['pickup_datetime'], utc=True, format='%Y-%m-%d %H:%M')
df['hour'] = df.pickup_datetime.dt.hour
df['month'] = df.pickup_datetime.dt.month
df["year"] = df.pickup_datetime.dt.year
df["weekday"] = df.pickup_datetime.dt.weekday
return df
train = deal_time_features(train)
test = deal_time_features(test)
train.head()
La función de tiempo procesado consta de hora, mes, año y día de la semana.
③Construir nuevas características de distancia
directamente usando coordenadas de latitud y longitud no favorece el funcionamiento de nuestro modelo, usamos fórmulas de conversión para convertir las coordenadas de latitud y longitud en distancia
def distance(x1, y1, x2, y2):
p = 0.017453292519943295
a = 0.5 - np.cos((x2 - x1) * p)/2 + np.cos(x1 * p) * np.cos(x2 * p) * (1 - np.cos((y2 - y1) * p)) / 2
dis = 0.6213712 * 12742 * np.arcsin(np.sqrt(a))
return dis
train['distance_miles'] = distance(train.pickup_latitude,train.pickup_longitude,train.dropoff_latitude,train.dropoff_longitude)
test['distance_miles'] = distance(test.pickup_latitude, test.pickup_longitude,test.dropoff_latitude,test.dropoff_longitude)
train.head()
train[(train['distance_miles']==0)&(train['fare_amount']==0)]
Después de construir la función de distancia, encontraremos que hay 15 datos más inútiles cuya distancia y precio son ambos 0, que se pueden eliminar
train = train.drop(index= train[(train['distance_miles']==0)&(train['fare_amount']==0)].index, axis=0)
④ Procesamiento especial
1. Elimine los datos con fare_amount menor que 2.5, porque la tarifa inicial de los taxis en Nueva York es 2.5
train = train.drop(index= train[train['fare_amount'] < 2.5].index, axis=0)
2. Eliminar datos con más de 7 personas
train[train.passenger_count >= 7]
train = train.drop(index= train[train.passenger_count >= 7].index, axis=0)
Parte 4. Modelado de datos
Eche un vistazo al aspecto final después de que se procesen los datos
train.describe().T
Utilice la interfaz .corr para ver cómo estas nuevas funciones se relacionan con los precios
train.corr()['fare_amount']
Pasos para modelar:
df_train = train.drop(columns= ['key','pickup_datetime'], axis= 1).copy()
df_test = test.drop(columns= ['key','pickup_datetime'], axis= 1).copy()
#使用copy后的数据进行建模
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df_train.drop('fare_amount',axis=1)
,df_train['fare_amount']
,test_size=0.2
,random_state = 42)
#用train_test_split分出训练集和测试集
import xgboost as xgb
params = {
'max_depth': 7,
'gamma' :0,
'eta':0.3,
'subsample': 1,
'colsample_bytree': 0.9,
'objective':'reg:linear',
'eval_metric':'rmse',
'silent': 0
}
def XGBmodel(X_train,X_test,y_train,y_test,params):
matrix_train = xgb.DMatrix(X_train,label=y_train)
matrix_test = xgb.DMatrix(X_test,label=y_test)
model=xgb.train(params=params,
dtrain=matrix_train,
num_boost_round=5000,
early_stopping_rounds=10,
evals=[(matrix_test,'test')])
return model
model = XGBmodel(X_train,X_test,y_train,y_test,params)
#建模
prediction = model.predict(xgb.DMatrix(df_test), ntree_limit = model.best_ntree_limit)
prediction
#数据预测
res = pd.DataFrame()
res['key'] = test_ids
res['fare_amount'] = prediction
res.to_csv('submission.csv', index=False)
#结果保存
Parte 5. Resumen
Mi enfoque es simplemente una forma de pensar relativamente simple, porque los únicos factores en los que puedo pensar acerca de los precios de los taxis son diferentes períodos de tiempo y distancias, estos dos factores tendrán un mayor impacto. Si tiene otras ideas y prácticas mejores, deje un mensaje en el área de discusión para informar al bloguero.
Además, hay una forma en la comunidad de kaggle de construir una nueva característica, que representa la distancia desde las coordenadas a tres aeropuertos locales diferentes. Este método lo usa el blogger directamente cuando los parámetros no están ajustados. El resultado es un aumento de 0.03. Creo que la mejora es No es demasiado grande y la función se superpone un poco con la distancia, por lo que no la usé al final. Publícalo aquí y compártelo con todos.
# def transform(data):
# # Distances to nearby airports,
# jfk = (-73.7781, 40.6413)
# ewr = (-74.1745, 40.6895)
# lgr = (-73.8740, 40.7769)
# data['pickup_distance_to_jfk'] = distance(jfk[1], jfk[0],
# data['pickup_latitude'], data['pickup_longitude'])
# data['dropoff_distance_to_jfk'] = distance(jfk[1], jfk[0],
# data['dropoff_latitude'], data['dropoff_longitude'])
# data['pickup_distance_to_ewr'] = distance(ewr[1], ewr[0],
# data['pickup_latitude'], data['pickup_longitude'])
# data['dropoff_distance_to_ewr'] = distance(ewr[1], ewr[0],
# data['dropoff_latitude'], data['dropoff_longitude'])
# data['pickup_distance_to_lgr'] = distance(lgr[1], lgr[0],
# data['pickup_latitude'], data['pickup_longitude'])
# data['dropoff_distance_to_lgr'] = distance(lgr[1], lgr[0],
# data['dropoff_latitude'], data['dropoff_longitude'])
# return data
# train = transform(train)
# test = transform(test)