import mermaid from ''; mermaid.initialize({ startOnLoad: true });
Ce notebook explicite une solution pour calculer la vitesse moyenne des velib sachant qu'on ne connaît que l'état des stations à intervalle réguliers.
%matplotlib inline
Même si je propose quelques jeux de données, il est possible de créer le sien en s'inspirant du code suivant : Récupérer les données Velib et les visualiser. Le premier lien décrit les données plus en détail : elles sont constituées des décomptes des vélos et places disponibles pour chaque station d'une même ville et pour chaque minute. La méthode proposée ici est celle des appariements décrites par le premier lien. L'algorithme peut être décrit en deux étapes :
La première partie ne pose pas de difficulté particulière. On peut juste penser à retirer les premiers vélos réposés qui ne pourront pas être appariés de toutes façons. On fait de même pour les derniers vélos retirés.
La seconde partie est constitutée de deux éléments :
Pour le coût, on peut y mettre à peu près toutes les contraintes imaginables (vitesse trop grande, durée trop grande ou trop petite). Pour la seconde, le code optimise de façon très naïve : on part d'un appariement aléatoire, on tire deux appariements $a_1 \rightarrow b_1$ et $a_2 \rightarrow b_2$. On inverse : $a_1 \rightarrow b_2$ et $a_2 \rightarrow b_1$. Si l'appariement est moins coûteux, on garde. Le code complet est disponible dans le module velib_trajectories.
from pyquickhelper.loghelper import str2datetime
from ensae_projects.datainc import besancon_df
import pandas
jeu = besancon_df()
df = pandas.read_csv(jeu, sep="\t", encoding="utf8")
df ["collect_date"] = df.apply( lambda r: str2datetime(r["collect_date"]),axis=1)
df.head()
address | available_bike_stands | available_bikes | banking | bike_stands | bonus | collect_date | contract_name | file | last_update | lat | lng | name | number | status | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Place Jacobins | 2 | 8 | 0 | 10 | 0 | 2014-05-22 02:40:03.954829 | Besancon | velib_data.2014-05-22_02-40-02.681782.txt | 2014-05-22 00:09:26 | 47.234142 | 6.033465 | 14 - JACOBINS | 14 | OPEN |
1 | Rue Charles NODIER Sortie du tunnel, direction... | 1 | 11 | 0 | 12 | 0 | 2014-05-22 02:40:03.954829 | Besancon | velib_data.2014-05-22_02-40-02.681782.txt | 2014-05-22 02:38:48 | 47.232098 | 6.035136 | 23 - RIVOTTE | 23 | OPEN |
2 | Avenue de la Gare d'Eau face Conseil Général | 4 | 6 | 0 | 10 | 0 | 2014-05-22 02:40:03.954829 | Besancon | velib_data.2014-05-22_02-40-02.681782.txt | 2014-05-21 13:24:02 | 47.232140 | 6.022843 | 16 - GARE D'EAU | 16 | OPEN |
3 | 18 rue Xavier Marmier | 6 | 6 | 0 | 12 | 0 | 2014-05-22 02:40:03.954829 | Besancon | velib_data.2014-05-22_02-40-02.681782.txt | 2014-05-22 00:00:44 | 47.241256 | 6.006690 | 30 - XAVIER MARMIER | 30 | OPEN |
4 | Rue Battant (au niveau du Square Bouchot) | 1 | 9 | 0 | 10 | 0 | 2014-05-22 02:40:03.954829 | Besancon | velib_data.2014-05-22_02-40-02.681782.txt | 2014-05-22 02:31:01 | 47.242990 | 6.022070 | 02 - SQUARE BOUCHOT | 2 | OPEN |
On vérifie les données pour la première date :
from manydataapi.velib import DataCollectJCDecaux as DataVelibCollect
dt = df["file"][0]
subset = df [ df["file"] == dt ]
fig,ax,plt = DataVelibCollect.draw(subset, figsize=(16,6))
ax.set_title("Besançon - {0} - {1} stations".format(dt.replace("besancon.","") \
.replace(".txt","").replace("_", " "),
len(subset)));
On calcule les événements (1 vélo apparu, 1 vélo disparu) :
import ensae_projects.challenge.velib_trajectories as velib
events = list(sorted(velib.enumerate_events(df)))
print("nb events",len(events))
events [:2]
nb events 589
[('velib_data.2014-05-22_04-17-04.635259.txt', Timestamp('2014-05-22 04:17:04.857260'), '24 - MEDIATHEQUE', 47.24010992877723, 6.027358757731063, 1), ('velib_data.2014-05-22_05-29-04.949889.txt', Timestamp('2014-05-22 05:29:05.262905'), '27 - MAIRIE', 47.23687885054159, 6.0222303583977155, 1)]
On calcule le meilleur appariement (ça prend un peu de temps). On ne prend que 200 itérations mais il en faudrait plus.
params = velib.ParemetreCoutTrajet()
print(params)
mindist, moyenne, appariement, positif, negatif = velib.appariement(events, iter=50, params=params)
print("vitesse moyenne", moyenne)
{'max_speed': 50, 'high_speed': 25, 'low_speed': 5, 'high_time': 0.75, 'low_time': 0.1} iteration 0 : app- 144 / 301 min 21743314023.08393 vitesse 5.185466030632195 nbchange 0 iteration 10 : app- 32 / 301 min 4217360521.9227724 vitesse 4.644576073119561 nbchange 272 iteration 20 : app- 26 / 301 min 3217656643.672325 vitesse 5.27657037459076 nbchange 104 iteration 30 : app- 19 / 301 min 2250635665.002002 vitesse 5.872166710865633 nbchange 88 iteration 40 : app- 16 / 301 min 1848259504.5709043 vitesse 6.277284851644164 nbchange 56 vitesse moyenne 6.407462005952014
La vitesse évolue encore et il faudrait faire tourner l'algorithme plus longtemps et le recoder de façon plus efficace. Voyons déjà ce que cela donne en terme de déplacement.
import matplotlib.pyplot as plt
app = [(positif[a], negatif[b] ) for a,b in appariement]
plt.figure(figsize=(16,6))
for deb,fin in app :
x = [deb[3], fin[3]]
y = [deb[4], fin[4]]
if x[0] > 0 and y[0] and x[1] > 0 and y[1] > 0 :
# on enlève les trajets aberrants
plt.plot(x,y,color="b")
Difficile de dire sur ce simple dessin si cela est sensé. Il faudrait faire tourner l'algorithme plus longtemps et le recoder de façon plus efficace.