Séance 2 : Meilleur Affichage et Premiers Nettoyages

25 janvier

I. Présentation de la séance

A. Objectifs


Les objectifs définis à la dernière séance étaient :

  • Enlever les fonctions 'depricated'
  • Re-travailler sur le fond de carte
  • Organiser nos données pour pouvoir y accéder jour par jour
  • Tracer de premiers trajets, ie relier les points de façon cohérente
  • Commencer à travailler sur le nettoyage des trajets


Cette séance nous avons décidé de travailler sur :

  • Utiliser un fond de carte basé sur GoogleMaps
  • Pouvoir accéder aux données jour par jour
  • Commencer à travailler sur le nettoyage des trajets

B. Analyse technique

II. Code

A. Création de l'affichage

Explication démarche¶

La projection des différents points sur un fond de carte GoogleMaps est réalisé grâce à la librarie gmplot, qui peut être trouvé sur le repository GitHub <a href= https://github.com/vgm64/gmplot > suivant</a>.

In [1]:
import gmplot

Le fond de carte à proprement parlé est généré par la fonction gmplot.GoogleMapPlotter .

Celle ci prend 3 entrées :

  • latitude
  • longitude
  • clé API-Google-Maps

La dernière entrée est une clé API que nous avons dû enregistrer auprès de Google afin de pouvoir utiliser l'API Google Maps.

In [2]:
gmap = gmplot.GoogleMapPlotter( 45.757589, 4.831689, 13, apikey="AIzaSyDsYwvF3UUxTx8RB40wd4SnUVzfnbW66LM")

Les données qui nous intéressent peuvent être affichées en utilisant les fonctions :

  • plot
  • scatter
  • heatmap Le résulat final est alors sauvergardé dans une page html grâce à la fonction gmap.draw
In [3]:
gmap.plot([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056], 'cornflowerblue', edge_width=10)
gmap.scatter([45.784062,45.757071,45.760616],[4.767291,4.833720, 4.858056], '#3B0B39', size=40, marker=False)
gmap.scatter([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056], 'k', marker=True)
gmap.heatmap([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056])
gmap.draw("figure/2-LyonMap.html")

Enfin l'aperçu de la page web sauvegardée est visible grâce à la fonction IFrame .

Exemple affichage complet¶

In [4]:
import gmplot
gmap = gmplot.GoogleMapPlotter( 45.757589, 4.831689 , 13,apikey=" AIzaSyDsYwvF3UUxTx8RB40wd4SnUVzfnbW66LM ")
gmap.plot([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056], 'cornflowerblue', edge_width=10)
gmap.scatter([45.784062,45.757071,45.760616],[4.767291,4.833720, 4.858056], '#3B0B39', size=40, marker=False)
gmap.scatter([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056], 'k', marker=True)
gmap.heatmap([45.784062,45.757071,45.760616], [4.767291,4.833720, 4.858056])
gmap.draw("figure/2-lyonmap.html")
from IPython.display import IFrame
IFrame('figure/2-lyonmap.html', width=990, height=500)
Out[4]:

B. Chargement des données

Chargement des données globales¶

Dans un premier temps nous créons un data frame à partir du fichier Json. Nous ne gardons que les colonnes intéressantes, c'est à dire :

  • le timestampMs
  • la latitude
  • la longitude

Nous avons d'autre part ajouté

  • un colonne nous donnant la date, ce qui nous permettra par la suite de trier les données plus facielement
  • l'heure
  • le temps écoulé depuis la dernière position (delay)

Notons bien que les latitudes et longitudes ont, au cours de ces opérations, été mises au format standart, et non plus E7.

Par la suite, nous rajoutons certaines colonnes, que nous calculons à partir de données citées précédamment. Il s'agit dans un premier temps de la distance qui sépare un point du prochain point.

Nous rajoutons également la vitesse à laquelle l'utilisateur se trouve en un point. Il est important de noter que nous calculons la vitesse moyenne entre deux points, et que nous associons la vitesse moyenne à la vitesse du premier point.

Ces hypothèses font du sens pour le moment, elles pourront être modifiées par la suite si besoin.

La vitesse est exprimée en km/h.

L'ensemble de ces fonction est rassemblé dans la fonction parser.getData(nameFile,bComputeDistance,bComputeVitesse) .

In [5]:
import parser
import distance
In [6]:
df = parser.importJson("data/anonymous_2015.json")
In [7]:
max(df['velocity'])
Out[7]:
2182.773280188384

Nous avons ensuite décidé de tracer la vitesse en fonction du temps pour avoir une idée des résultats obtenus. On voit donc bien qu'il y a un certains nombre de points aberrants. La vitesse maximale est de 2182 km/h ce qui paraît très peu réaliste !

In [18]:
import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
plt.plot(df["velocity"],'-')
plt.grid(True)
plt.xlabel('Echantillons')
plt.ylabel('Vitesse (km/h)')
plt.show()

Chargement des données d'un jour particulier¶

Pour mieux se rendre compte de ce qu'il se passe, nous avons décidé de nous concentrer uniquement sur la journée du 21 septembre 2015. On sait que ce jour là, l'utilisateur se trouvait à lyon.

Nous avons crée une fonction permettant de sélectionner directement les données du jour qui nous intéresse.

In [9]:
data = parser.selectDate("21-09-2015", df)
In [10]:
data.head()
Out[10]:
timestampMs latitude longitude date time delay distance velocity acceleration
0 1442871799814 45.767907 4.832612 21-09-2015 23:43:19 1002.796 12.503509 0.081863 0.000536
1 1442871249961 45.767802 4.832557 21-09-2015 23:34:09 549.853 3.576697 0.102834 0.002957
2 1442871124748 45.767829 4.832583 21-09-2015 23:32:04 125.213 26.900267 0.790506 0.023230
3 1442871002243 45.767827 4.832929 21-09-2015 23:30:02 122.505 25.781638 0.673677 0.017603
4 1442870864471 45.767840 4.832597 21-09-2015 23:27:44 137.772 7.186476 0.058785 0.000481

Nous avons traçé cette journée, de la même manière qui a été vue plus haut.

In [11]:
lat = data['latitude']
lng = data['longitude']
In [12]:
import gmplot
gmap = gmplot.GoogleMapPlotter(45.757589, 4.831689, 13, apikey="AIzaSyDsYwvF3UUxTx8RB40wd4SnUVzfnbW66LM")
gmap.plot(lat, lng, 'cornflowerblue', edge_width=3)
gmap.draw("figure/2-lyon-map-data-an.html")
from IPython.display import IFrame
IFrame('figure/2-lyon-map-data-an.html', width=990, height=500)
Out[12]:

Nous avons ensuite traçé les vitesses relatives à cette journée.

Nous pouvons constater que les vitesse calculée sembllent cohérentent.

In [13]:
import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
plt.plot(data["velocity"],'-')
plt.grid(True)
plt.ylabel('Vitesse (km/h)')
plt.xlabel('Echantillons au cours de la journée')
plt.show()

C. Première tentative de débruitage

Explication de la démarche¶

On commence par implémenter les filtres mean filter et median filter.

Ces filtres remplacent les coordonnées des points (longitude et latitude) par respectivement leurs valeurs moyenne et mediane calculées sur une fenêtre de points centrée sur le point en question. Par exemple pour une fenêtre de taille 5, les longitude et latitude du point sont remplacées par la moyenne ou médiane des longitudes et latitudes de l'ensemble composé du point en question, des deux points précédents et des deux points suivants.

Ces filtres fonctionnent bien sur des points proches (spatialement et temporellement) mais sont moins pertinent sur des points très éloignés, en particulier lorsque beaucoup de temps les sépare. Une condition importante pour bien pouvoir les utiliser sera donc de bien segmenter les trajets en périodes temporelles cohérentes, pour pouvoir utiliser ces filtres sur des points constitutifs d'un même trajet. Cette segmentation sera explorée ultérieurement.

De plus vient la question de l'utilisation de ces filtres. En effet deux usages peuvent en être fait:

  • Remplacer tous les points par les valeurs calculées, et ainsi lisser les trajets quitte à modifier fortement les données initiales.
  • Repérer les points aberrants en comparant la distance entre le point initial et le point calculé à une valeur seuil. Les points aberrants peuvent alors être simplement supprimés ou remplacés par le point calculé.

Implémentation¶

Mean filter¶

On applique le filtre simplement (premier usage) aux déplacements non segmentés de la journée du 21 septembre 2015.

In [14]:
n=2 ## la fenêtre glissante sera de taille 2n+1

lat_filtered=[0]*len(data) ## initialisation des tableaux
long_filtered=[0]*len(data)

for i in range(n):
    lat_filtered[i]=data['latitude'][i]
    long_filtered[i]=data['longitude'][i]

for i in range(len(data)-n,len(data)):
    lat_filtered[i]=data['latitude'][i]
    long_filtered[i]=data['longitude'][i]

for i in range(n, len(data)-n):
    for j in range(i-n,i+n+1):
        lat_filtered[i]+=data['latitude'][j]/(2*n+1)
        long_filtered[i]+=data['longitude'][j]/(2*n+1)

lat_original = data['latitude'].values
long_original = data['longitude'].values
In [15]:
plt.figure(figsize=(12,8))
plt.grid(True)
plt.xlabel('Latitude')
plt.ylabel('Longitude')
plt.plot(lat_original, long_original,label="Parcours brute")
plt.plot(lat_filtered, long_filtered, 'r',label="Parcours filtré")
plt.legend(loc='upper left')
plt.savefig('figure/2-mean-filter.png')
plt.show()

On remarque que les chemins filtrés (en rouge) sont pertinents pour des séquences de déplacement (les deux trajets en diagonal) mais beaucoup moins pour les déambulation autour d'un point. Cela confirme la nécesité de n'appliquer ces filtres qu'à des segments de trajets.

Median filter¶

In [16]:
n = 2 

for i in range(n, len(data)-n):
    lat_window = data['latitude'][i-n:i+n+1].tolist()
    lat_window.sort()
    long_window = data['longitude'][i-n:i+n+1].tolist()
    long_window.sort()

    lat_filtered[i] = lat_window[n]
    long_filtered[i] = long_window[n]
In [17]:
plt.figure(figsize=(12,8))
plt.grid(True)
plt.xlabel('Latitude')
plt.ylabel('Longitude')
plt.plot(lat_original, long_original,label="Parcours brute")
plt.plot(lat_filtered, long_filtered, 'r',label="Parcours filtré")
plt.legend(loc='upper left')
plt.savefig('figure/2-median-filter.png')
plt.show()

On remarque que le median filter est plus fidèle aux données brutes lorsque le trajet est pertinent mais s'éloigne d'autant plus des points aberrants (notamment le point en haut à gauche). De même pour des trajets de déambulation

III. Conclusion

A. Bilan

Pour la partie chargement des données nous pouvons faire les conclusions suivantes :

  • Nous avons constaté que les données étaient acquises à des intervalles de temps très irréguliers. Il faut donc lire les vitesses avec une très grande précaution, du fait que de parfois les données sont acquises toutes les 200/300 secondes.
  • Les données que nous avons traitées sont assez veilles, et ce faible échantillonage peut peut être s'expliquer par le fait que le téléphné utilisé à ce moment là était plutôt vieux. On va donc essayer de regarder avec des jeux de données plus récents pour voir si on constate, ou non une différence.

L'affichage de la carte est terminé.

Le chargement des données est terminé.

B. Travail à faire de la prochaine séance

Les pistes de travail pour la semaine prochaine sont :

  • essayer de commencer à identifier des modes de transport grâce aux vitesses
  • calculer l'angle de la direction pour détecter les centres d'interets
  • changer de jeux de données pour voir si c'est mieux
  • travailler sur une approche vitesse pour reconnaitre les points