Traiter des données de localisation

Compétition Kaggle : « New York City Taxi Fare Prediction »

Récemment en me lançant dans la compétition Kaggle sur la prédiction du montant des courses des taxis New Yorkais je me suis confronté au traitement de données de localisation. Pour précisément j’ai du traiter des données Géo-Localisées (Latitude-Longitude). Pour tout dire dans cette compétition Kaggle on n’a presque que ça comme données ! en gros on récupère les coordonnées de récupération du(des) client(s) et celles de l’endroit où le taxi les a déposé(s), et c’est presque tout !

Comme je le disais on a très peu de données, il va donc falloir exploiter au maximum ces informations pour par exemple :

  • Déterminer la distance entre les points de récupération-dépose
  • Déterminer le temps de trajet (car bien sur celui-ci a un impact sur le montant de la course)
  • Supprimer les outliers. En regardant de près sur une carte on constatera certainement que des points sont hors limites, voire même dans l’eau ! il faudra supprimer ces données inexploitables.
  • Détecter la proximité d’un aéorport … en effet les courses vers ce type de destination sont bien souvent forfaitaires.
  • etc.

Pour cela il faut pouvoir exploiter les données de géolocalisation.

Vérifier que les données sont dans un cadre

Dans l’exemple des taxis c’est plutôt simple car nous devons restreindre les données géolocalisées au secteur de New York. Les autres données seront considérées comme des outliers (erreurs ou exceptions) que nous retirerons simplement du jeu de données. Ces données n’étant pas signifiante pour notre modèle.

Pour cela récupérons simplement les coordonnées de New-York sur internet …

On récupère donc les informations suivantes :

Les coordonnées de New York en degrés décimaux:

Latitude : 40.7142700°
Longitude : -74.0059700°

Les coordonnées de New York en degrés et minutes décimales:

Latitude : 40°42.8562′ Nord
Longitude : 74°0.3582′ Ouest

On va maintenant définir un cadre d’appartenance à New York (forcément on sera approximatif ici, la vile de NYC n’étant pas située dans un cadre parfait): (-74.3, -73.7, 40.5, 40.9)

Ensuite on définit une simple fonction (Python) AppartientCadre() qui vérifie que des coordonnées en entrée figurent bien dans le cadre voulu:

nycBox = (-74.3, -73.7, 40.5, 40.9)

# Cette fonction vérifie que les coordonnées passées (df) sont bien dans le cadre BB
def AppartientCadre(df, _nycBox):
    return (df.pickup_longitude <= _nycBox[0]) & \
 (df.pickup_longitude <= _nycBox[1]) & \ 
(df.pickup_latitude <= _nycBox[2]) & \ 
(df.pickup_latitude <= _nycBox[3]) & \ 
(df.dropoff_longitude <= _nycBox[0]) & \
(df.dropoff_longitude <= _nycBox[1]) & \ 
(df.dropoff_latitude <= _nycBox[2]) & \
(df.dropoff_latitude <= _nycBox[3])
pd_sample = pd_sample[AppartientCadre(pd_sample, nycBox)]

Affichage sur une carte

Voilà on a supprimé quelques outliers, mais il serait interressant de voir un peu à quoi ressemblent nos données vous ne trouvez pas ? pour cela je vous propose de les visualiser sur une carte. Pour cela on a encore la possibilité d'utiliser Google Maps en créant une carte avec les points que nous avons. Certains sites proposent même ce service via import d'un fichier par exemple. Néanmoins nous n'allons pas procéder comme cela car nous avons vraiment beaucoup de point à visualiser.

Nous allons superposer nos points sur une image (carte). Notre seul prérequis est de posséder une carte (image) et surtout de connaitre ses coordonnées GPS.

Pour notre exemple nous récupérons la carte via https://aiblog.nl/download/nyc_-74.3_-73.7_40.5_40.9.png

Ses coordonnées sont (-74.3, -73.7, 40.5, 40.9) ... non ce n'est pas un hasard, il s'agit bien du cadre de validation des coordonnées précédent 😉

Nous allons maintenant tracer sur cette image nos points avec la librairie matplotlib (scatterplot) :

import matplotlib.pyplot as plt
nyc = plt.imread('https://aiblog.nl/download/nyc_-74.3_-73.7_40.5_40.9.png')
def plotOnImage(df, _nycBox, nyc_map):
    fig, a = plt.subplots(ncols=1, figsize=(10, 10))
    a.set_title("Points sur NYC")
    a.set_xlim((_nycBox[0], _nycBox[1]))
    a.set_ylim((_nycBox[2], _nycBox[3]))
    a.scatter(df.pickup_longitude, df.pickup_latitude, zorder=1, alpha=0.3, c='r', s=1)
    a.imshow(nyc, zorder=0, extent=_nycBox)
plotOnImage(pd_sample, BB, nyc_map)

Regardez le résultat :

Les points sont tracés en rouge. On observe la concentration de ces dernier dans le centre ville comme on aurait pu s'y attendre !

Calculer la distance

La distance est bien sur une donnée importante à récupérer. Pour cela on peut avoir encore plusieurs approches. On peut calculer la distance entre deux point via la formule de Haversine ou alors utiliser les API Google Maps. Nous allons voir ces deux approches.

Calcul via la formule de Haversine

Voilà la formule mathématique :

Alors évidemment cette formule peut paraitre bien complexe. Mais ça serait oublier que nous sommes sur terre et que cette bonne vieille terre est sphérique ! il est donc impensable (sauf pour de très courtes distances) de ne pas prendre en considération la forme sphérique de la terre. D'où cette formule donc ...

En Python, voilà ce que ça donne :

def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295 # Pi/180
    a = 0.5 - np.cos((lat2 - lat1) * p)/2 + np.cos(lat1 * p) * np.cos(lat2 * p) * (1 - np.cos((lon2 - lon1) * p)) / 2
    return 0.6213712 * 12742 * np.arcsin(np.sqrt(a))

distance(pd_sample.pickup_latitude[0],
         pd_sample.pickup_longitude[0],
         pd_sample.dropoff_latitude[0],
         pd_sample.dropoff_longitude[0])

Calcul via Google maps

Là ça se corse un peu car pour utiliser l'API Google vous devez :

  1. Avoir un compte Google (gmail)
  2. Déclarer l'utilisation de l'API afin d'obtenir une clé. Pour cela allez sur l'URL https://console.developers.google.com et ajoutez l'API Maps Distance
  3. importer la librairie googlemaps (https://github.com/googlemaps/google-maps-services-python) via pip install googlemaps

Pour tester je vous invite à vérifier que l'API est bien active en tapant dans votre browser directement :

https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=Washington,DC&destinations=New+York+City,NY&key=[VOTRE CLE ICI]

NB: remplacez [VOTRE CLE ICI] par la clé que vous avez récupéré sur le site Google.

Vous devez avoir cet écran :


Maintenant vous pouvez faire une appel via Python de l'API:

pd_sample['pickup'] = pd_train.pickup_latitude[0].astype(str)+","+pd_train.pickup_longitude[0].astype(str)
pd_sample['dropoff'] = pd_train.dropoff_latitude[0].astype(str)+","+pd_train.dropoff_longitude[0].astype(str)
print ("Pickup:" + pd_sample['pickup'][0])
print ("Dropoff:" + pd_sample['dropoff'][0])

import googlemaps
gmaps = googlemaps.Client(key="[VOTRE CLE ICI]")
def distance_googlemaps(pickup, dropoff):
    geocode_result = gmaps.distance_matrix(pickup, dropoff)
    try:
        distance = float(geocode_result['rows'][0]['elements'][0]['distance']['text'].split()[0])
        duration = geocode_result['rows'][0]['elements'][0]['duration']['text'].split()
        if len(duration)==4:
            mins = float(duration[0])*60 + float(duration[2])
        else:
            mins = float(duration[0])
    except:
        mins = np.nan
        distance = np.nan
    return pd.Series((distance, mins))
distance_googlemaps(pd_sample['pickup'][0], pd_sample['dropoff'][0])

Vous avez remarqué Google nous offre même la durée de trajet entre les deux points 🙂
Voila nous avons vu comment récupérer, visualiser et enrichir des données de géolocalisation. Nous avons efleuré l'API Google mais si vous regardez de plus près vous trouverez pleins d'autres fonctions utiles ainsi que des paramètres intéressant à ajuster.

Partager cet article

2 Replies to “Traiter des données de localisation”

  1. Bonjour,

    j’ai une erreur quand j’execute la fonction AppartientCadre sur le daraframe pd_sample. il me dit :
    name ‘pd_sample’ is not defined.
    Voici mon code:
    nycBox = (-74.3, -73.7, 40.5, 40.9)

    def AppartientCadre(df, _nycBox):
    return (df.pickup_longitude <= _nycBox[0]) & \
    (df.pickup_longitude <= _nycBox[1]) & \
    (df.pickup_latitude <= _nycBox[2]) & \
    (df.pickup_latitude <= _nycBox[3])& \
    (df.dropoff_longitude <= _nycBox[0]) & \
    (df.dropoff_longitude <= _nycBox[1])& \
    (df.dropoff_latitude <= _nycBox[2]) & \
    (df.dropoff_latitude <= _nycBox[3])
    pd_sample = pd_sample[AppartientCadre(pd_sample, nycBox)]

    Merci de votre aide

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.