2A.eco - Exercice API SNCF corrigé

Manipulation d'une API REST, celle de la SNCF est prise comme exemple. Correction d'exercices.

In [1]:
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Out[1]:
run previous cell, wait for 2 seconds

Partie 0 - modules recommandés et connexion à l'API

Il vous faudra sûrement les modules suivant :

  • requests
  • datetime
  • pandas
  • matplotlib

Créer un login pour vous connecter à l'API de la SNCF https://data.sncf.com/api

Vous pouvez maintenant commencer. Ce notebook peut prendre du temps à s'éxécuter, surout à partir de la partie 3

In [2]:
# !!!!! Attention à bien mettre votre token ici  !!!!!

token_auth = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
In [3]:
import keyring, os
if "XXXXXX" in token_auth:
    token_auth = keyring.get_password("sncf", "ensae_teaching_cs,key")

Partie 1 - Trouver les gares accessibles via la SNCF

  • Trouver l'ensemble des gares disponibles sur l'API et créer un fichier csv avec les codes de la gare, son nom et ses coordonnées latitude et longitude, ainsi que les informations administratives de la région quand elles sont disponibles

  • Représentez les sur un graphique

In [4]:
import pandas as pd
import requests
In [5]:
def page_gares(numero_page) :
    return requests.get(
        ('https://api.sncf.com/v1/coverage/sncf/stop_areas?start_page={}').format(numero_page),
        auth=(token_auth, ''))

######################################
# on commence par la première page qui nous donne le nombre de résultats par page ainsi que le nombre total de résultats                    

page_initiale = page_gares(0)                        
item_per_page = page_initiale.json()['pagination']['items_per_page']
total_items = page_initiale.json()['pagination']['total_result']
dfs = []

# on fait une boucle sur toutes les pages suivantes 
print_done = {}

for page in range(int(total_items/item_per_page)+1) : 
    stations_page = page_gares(page)
    
    ensemble_stations = stations_page.json()
    
    if 'stop_areas' not in ensemble_stations:
        # pas d'arrêt
        continue
    
    # on ne retient que les informations qui nous intéressent 
    for station in ensemble_stations['stop_areas']:

        station['lat'] = station['coord']['lat']
        station["lon"]  = station['coord']['lon']

        if 'administrative_regions' in station.keys() : 
            for var_api, var_df in zip(['insee','name','label','id','zip_code'],
                                       ['insee','region','label_region','id_region','zip_code']): 
                try:
                    station[var_df] = station['administrative_regions'][0][var_api]
                except KeyError:
                    if var_api not in print_done:
                        print("key '{0}' not here but {1}".format(var_api, 
                                ",".join(station['administrative_regions'][0].keys())))
                        print_done[var_api] = var_api

        [station.pop(k,None) for k in ['coord','links','administrative_regions', 'type', 'codes']]

    stations = ensemble_stations['stop_areas']
    try:
        dp = pd.DataFrame(stations)
    except Exception as e:
        # La SNCF modifie parfois le schéma de ses données.
        # On affiche station pour avoir une meilleure idée que l'erreur retournée par pandas
        raise Exception("Problème de données\n{0}".format(stations)) from e
        
    dfs.append(dp)
    if page % 10 == 0:
        print("je suis à la page", page, "---", dp.shape)

import pandas
df = pandas.concat(dfs)
df.to_csv("./ensemble_gares.csv")  
print(df.shape)
df.head()
je suis à la page 0 --- (25, 11)
je suis à la page 10 --- (25, 11)
je suis à la page 20 --- (25, 11)
je suis à la page 30 --- (25, 11)
je suis à la page 40 --- (25, 11)
je suis à la page 50 --- (25, 11)
je suis à la page 60 --- (25, 11)
je suis à la page 70 --- (25, 11)
je suis à la page 80 --- (25, 11)
je suis à la page 90 --- (25, 11)
je suis à la page 100 --- (25, 11)
je suis à la page 110 --- (25, 11)
je suis à la page 120 --- (25, 11)
je suis à la page 130 --- (25, 11)
je suis à la page 140 --- (25, 11)
je suis à la page 150 --- (25, 11)
je suis à la page 160 --- (25, 11)
je suis à la page 170 --- (25, 11)
(4279, 11)
Out[5]:
name label timezone id lat lon insee region label_region id_region zip_code
0 . . Europe/Paris stop_area:OCE:SA:00 27.141247 -3.404382 NaN NaN NaN NaN NaN
1 Aachen/Aix la Chapelle Aachen/Aix la Chapelle (Aachen) Europe/Paris stop_area:OCE:SA:80153452 50.767759 6.091215 Aachen Aachen admin:osm:relation:62564
2 Abancourt Abancourt (Abancourt) Europe/Paris stop_area:OCE:SA:87313759 49.685665 1.77418 60001 Abancourt Abancourt (60220) admin:fr:60001 60220
3 Abbaretz Abbaretz (Abbaretz) Europe/Paris stop_area:OCE:SA:87481614 47.55523 -1.524196 44001 Abbaretz Abbaretz (44170) admin:fr:44001 44170
4 Abbeville Abbeville (Abbeville) Europe/Paris stop_area:OCE:SA:87317362 50.102194 1.824486 80001 Abbeville Abbeville (80100) admin:fr:80001 80100
In [6]:
df[df.name.str.contains('Lyon')]
Out[6]:
name label timezone id lat lon insee region label_region id_region zip_code
3 Lyon-Gorge-de-Loup Lyon-Gorge-de-Loup (Lyon) Europe/Paris stop_area:OCE:SA:87721175 45.766077 4.804731 69123 Lyon Lyon admin:fr:69123
4 Lyon-Jean-Macé Lyon-Jean-Macé (Lyon) Europe/Paris stop_area:OCE:SA:87282624 45.745198 4.841515 69123 Lyon Lyon admin:fr:69123
5 Lyon-Part-Dieu Lyon-Part-Dieu (Lyon) Europe/Paris stop_area:OCE:SA:87723197 45.760585 4.859435 69123 Lyon Lyon admin:fr:69123
6 Lyon-Part-Dieu-Gare-Rou Lyon-Part-Dieu-Gare-Rou (Lyon) Europe/Paris stop_area:OCE:SA:87697128 45.760585 4.859435 69123 Lyon Lyon admin:fr:69123
7 Lyon-Perrache Lyon-Perrache (Lyon) Europe/Paris stop_area:OCE:SA:87722025 45.748785 4.825941 69123 Lyon Lyon admin:fr:69123
8 Lyon-Perrache-Gare-Rout Lyon-Perrache-Gare-Rout (Lyon) Europe/Paris stop_area:OCE:SA:87697136 45.749712 4.82678 69123 Lyon Lyon admin:fr:69123
9 Lyon-Saint-Exupery-Tgv Lyon-Saint-Exupery-Tgv (Colombier-Saugnieu) Europe/Paris stop_area:OCE:SA:87762906 45.720928 5.075833 69299 Colombier-Saugnieu Colombier-Saugnieu (69124) admin:fr:69299 69124
10 Lyon-St-Paul Lyon-St-Paul (Lyon) Europe/Paris stop_area:OCE:SA:87721159 45.766082 4.826904 69123 Lyon Lyon admin:fr:69123
11 Lyon-St-Paul-Quai-Bondy Lyon-St-Paul-Quai-Bondy (Lyon) Europe/Paris stop_area:OCE:SA:87698332 45.766488 4.828105 69123 Lyon Lyon admin:fr:69123
12 Lyon-St-Paul-la-Feuilée Lyon-St-Paul-la-Feuilée (Lyon) Europe/Paris stop_area:OCE:SA:87698340 45.766342 4.829385 69123 Lyon Lyon admin:fr:69123
13 Lyon-Vaise Lyon-Vaise (Lyon) Europe/Paris stop_area:OCE:SA:87721001 45.780142 4.80405 69123 Lyon Lyon admin:fr:69123
14 Lyon-Vaise-Gare-Routièr Lyon-Vaise-Gare-Routièr (Lyon) Europe/Paris stop_area:OCE:SA:87697045 45.781597 4.805428 69123 Lyon Lyon admin:fr:69123
7 Paris-Gare-de-Lyon Paris-Gare-de-Lyon (Paris) Europe/Paris stop_area:OCE:SA:87686006 48.844922 2.373462 75056 Paris Paris (75000-75116) admin:fr:75056 75000;75116
5 Urcay-Hotel-du-Lyon-d'O Urcay-Hotel-du-Lyon-d'O (Urçay) Europe/Paris stop_area:OCE:SA:87409490 46.626818 2.589411 3293 Urçay Urçay (03360) admin:fr:3293 03360
In [7]:
df = pd.read_csv("./ensemble_gares.csv", encoding = "ISO-8859-1")
print(df.columns)
print(df.shape)
# Exemple des informations sur une gare
df.iloc[317]
Index(['Unnamed: 0', 'name', 'label', 'timezone', 'id', 'lat', 'lon', 'insee',
       'region', 'label_region', 'id_region', 'zip_code'],
      dtype='object')
(4279, 12)
Out[7]:
Unnamed: 0                             17
name                              Bayonne
label                   Bayonne (Bayonne)
timezone                     Europe/Paris
id              stop_area:OCE:SA:87673004
lat                               43.4968
lon                               -1.4704
insee                               64102
region                            Bayonne
label_region              Bayonne (64100)
id_region                  admin:fr:64102
zip_code                            64100
Name: 317, dtype: object
In [8]:
# on crée un dictionnaire des correspondances entre les noms et les codes des gares
dict_label_gare_code = df[['label','id']].set_index('label').to_dict()['id']
dict_nom_gare_code = df[['name','id']].set_index('name').to_dict()['id']
In [9]:
print(df.columns)

# graphique dans le plan des gares
%matplotlib inline
import matplotlib.pyplot as plt
lng_var = df[(df['lat']>35) & (df['lat']<60)]["lon"].tolist()
lat_var = df[(df['lat']>35) & (df['lat']<60)]["lat"].tolist()
plt.scatter(x = lng_var , y = lat_var,marker = "o");
Index(['Unnamed: 0', 'name', 'label', 'timezone', 'id', 'lat', 'lon', 'insee',
       'region', 'label_region', 'id_region', 'zip_code'],
      dtype='object')

Les trajets depuis la Gare de Lyon

Partons à Lyon

Imaginez que vous vouliez un peu voyager hors de Paris, et il se trouve que justement on vous propose de passer quelques jours à Lyon. Vous partez le 17 novembre vers 19h50 pour ne pas trop écourter votre journée de travail (documentation de l'API).

Question 1

  • Commencez par récupérer les informations sur le trajet entre Paris Gare de Lyon et Lyon Perrache le 17 novembre à 19h57

    • Paris - Gare de Lyon (code de la gare : stop_area:OCE:SA:87686006)

    • Lyon - Gare Lyon Perrache (code de la gare : stop_area:OCE:SA:87722025)

    • Indice : utiliser la requête "journeys"

    • Autre indice : le format de la date est AAAAMMJJTHHMMSS (Année, mois, jour, heure, minutes, secondes)

  • Répondez aux questions suivantes

    • combien y a-t-il d'arrêts entre ces deux gares ? (utilisez la clé 'journeys')
    • combien de temps d'arrêt à chacune d'elles ?

Tout d'abord une fonction qui sera utile pour calculer des temps :

In [10]:
from datetime import datetime, timedelta

def convertir_en_temps(chaine) : 
    ''' on convertit en date la chaine de caractères de l API'''
    return datetime.strptime(chaine.replace('T',''),'%Y%m%d%H%M%S')

Et l'inverse :

In [11]:
def convertir_en_chaine(dt) : 
    ''' on convertit en chaîne de caractères un datetime'''
    return datetime.strftime(dt, '%Y%m%dT%H%M%S')

now = datetime.now()
convertir_en_chaine(now)
Out[11]:
'20200125T135513'
In [12]:
convertir_en_temps(convertir_en_chaine(now))
Out[12]:
datetime.datetime(2020, 1, 25, 13, 55, 13)
In [13]:
# informations sur le trajet qu'on choisit dans le futur
# l'API ne retourne pas de résultatq très loin dans le passé
now = datetime.now()
dt = now + timedelta(14)  # dans deux semaines

date_depart = convertir_en_chaine(dt)
gare_depart = 'stop_area:OCE:SA:87686006'
gare_arrivee = 'stop_area:OCE:SA:87722025'

date_depart
Out[13]:
'20200208T135513'

Ensemble des départs :

In [14]:
paris_lyon = requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/journeys?'
     'from={}&to={}&datetime={}').format(gare_depart, gare_arrivee, date_depart),
    auth=(token_auth, '')).json()

Les gares du chemin entre Paris et Lyon sur ce trajet ainsi que le temps d'arrêt :

In [15]:
session = paris_lyon['journeys'][0]['sections'][1]
rows = []
if "stop_date_times" in session:
    for i in session['stop_date_times']:
        rows.append(dict(name=i['stop_point']['name'],
                         depart=convertir_en_temps(i['departure_date_time']),
                         arrivee=convertir_en_temps(i['arrival_date_time'])))
pandas.DataFrame(rows)
Out[15]:
name depart arrivee
0 Paris-Gare-de-Lyon 2020-02-08 14:59:00 2020-02-08 14:59:00
1 Lyon-Part-Dieu 2020-02-08 17:01:00 2020-02-08 16:56:00
2 Lyon-Perrache 2020-02-08 17:09:00 2020-02-08 17:09:00

Question 2

Vous êtes un peu pressé et vous avez peur de vous tromper en arrivant à la gare car d'autres TGV partent à peu près en même temps (à partir de 19h00) de la gare de Lyon. Si vous demandez à l'API, combien de résultats vous donne-t-elle ?

Soit l'API a changé, soit...

In [16]:
requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/stop_points/'
     'stop_area:OCE:SA:87686006/departures'), auth=(token_auth, '')).json()
Out[16]:
{'pagination': {'start_page': 0,
  'items_on_page': 0,
  'items_per_page': 0,
  'total_result': 0},
 'links': [],
 'disruptions': [],
 'notes': [],
 'feed_publishers': [],
 'departures': [],
 'context': {'timezone': 'Europe/Paris',
  'current_datetime': '20200125T135513'},
 'error': {'message': 'ptref : Filters: Unable to find object',
  'id': 'unknown_object'},
 'exceptions': []}
In [17]:
### les trains qui partent autour de 19h00
departs_paris = requests.get(
    ('https://api.sncf.com/v1/coverage/sncf/stop_points/stop_point:OCE:SP:'
     'TGV-87686006/departures?from_datetime={}').format(
        date_depart), auth=(token_auth, '')).json()

# Nombre de trains que l'API renvoie à partir de cet horaire-là
#print(len(departs_paris['departures']))
departs_paris
Out[17]:
{'pagination': {'start_page': 0,
  'items_on_page': 0,
  'items_per_page': 0,
  'total_result': 0},
 'links': [],
 'disruptions': [],
 'notes': [],
 'feed_publishers': [],
 'departures': [],
 'context': {'timezone': 'Europe/Paris',
  'current_datetime': '20200125T135513'},
 'error': {'message': 'ptref : Filters: Unable to find object',
  'id': 'unknown_object'},
 'exceptions': []}
  • Quels sont les horaires de départ de ces trains ?
In [18]:
for i in range(len(departs_paris['departures'])) :
    print(departs_paris['departures'][i]['stop_date_time']['departure_date_time'])
  • Parmi ces trains, combien de trains ont pour destination finale Lyon et qui partent le 17 novembre ?
In [19]:
nombre_trains_pour_lyon = 0

for depart in departs_paris['departures'] :     
    if "Lyon" in depart['display_informations']['direction'] : 
        if (convertir_en_temps(depart['stop_date_time']['arrival_date_time']) > 
                convertir_en_temps(date_depart) and
                convertir_en_temps(depart['stop_date_time']['arrival_date_time']) < 
                datetime(2016,11,18,0,0,0)):
            nombre_trains_pour_lyon += 1
            print("le prochain départ pour Lyon sera le",
                  convertir_en_temps(depart['stop_date_time']['arrival_date_time']))
        
print("Il y a" , nombre_trains_pour_lyon, "train(s) pour Lyon dans les trains proposés", 
      "par l'API qui partent encore le 17 novembre")
Il y a 0 train(s) pour Lyon dans les trains proposés par l'API qui partent encore le 17 novembre

C'est quand qu'on va où ?

En fait, vous n'êtes plus très sûr de vouloir aller à Lyon. Mais bon maintenant vous êtes Gare de Lyon et il est 18h00.

Question 3

  • Combien de tgv partent entre 18h00 et 20h00 ?
  • Lequel arrive le plus tôt à sa destination finale ?
In [20]:
# on crée deux fonctions : 

def trouver_destination_tgv(origine, datetime) : 
    '''Permet d avoir les 10 prochains départs d une gare donnée '''
    return requests.get('https://api.sncf.com/v1/coverage/sncf/stop_points/{}/' \
                        'departures?from_datetime={}'.format(origine, datetime) ,
                        auth=(token_auth, '')).json()

def trouver_trajet_dispo_max_heure(gare_depart, date_heure_depart, date_heure_max) : 
    '''Permet d avoir toutes les informations sur des trajets partant 
    d une gare entre une date X et une date Y'''
    
    destinations = []
    
    # on interroge l'API tant qu'il renvoie des information
    # sur les trains partant de Gare de lyon 

    while convertir_en_temps(date_heure_depart) < convertir_en_temps(date_heure_max) :
    # on prend toutes les destinations qui partent à partir d'une certaine heure
        destinations = destinations + trouver_destination_tgv(
            gare_depart, date_heure_depart)['departures']
        
        nombre_resultats = trouver_destination_tgv(
            gare_depart, date_heure_depart)['pagination']['items_on_page']
        
        if nombre_resultats <= 0:
            break

        # on trouve l'heure max de la première série de 10 solutions que l'application renvoie
        # on remplace l'heure qu'on cherche par celle là
        resultats = trouver_destination_tgv(gare_depart, date_heure_depart)
        date_heure_depart = resultats['departures'][nombre_resultats-1][
            'stop_date_time']['departure_date_time']

    return destinations
In [21]:
# on trouve l'ensemble des trajets dont le départ est compris entre deux horaires
# informations sur le trajet qu'on choisit dans le futur
# l'API ne retourne pas de résultatq très loin dans le passé
now = datetime.now()
if now.hour < 6:
    # pas trop tôt
    now += timedelta(hours=4)
dt = now + timedelta(14)  # dans deux semaines

date_heure = convertir_en_chaine(dt)
max_date_heure = convertir_en_chaine(dt + timedelta(hours=24))
print("entre", date_heure, "et", max_date_heure)

gare_initiale = 'stop_point:OCE:SP:TGV-87686006'

# on demande à avoir tous les trajets partant de gare de lyon
# entre deux heures précises

destinations_depuis_paris_max_20h = trouver_trajet_dispo_max_heure(
    gare_initiale, date_heure, max_date_heure)

# on veut supprimer ceux pour lesquels le départ est après 20h00

dictionnaire_destinations = {}

i = 0

for depart in destinations_depuis_paris_max_20h :    
    print(depart['display_informations']['direction'],
          depart['stop_date_time']['departure_date_time'])
    if (convertir_en_temps(depart['stop_date_time']['departure_date_time']) < 
            convertir_en_temps(max_date_heure)): 
        i += 1
        dictionnaire_destinations[i] = depart 
    
print("Je peux prendre", len(dictionnaire_destinations.keys()), 
      "trains qui partent entre 18h et 20h de Gare de Lyon le 17 novembre 2016")
entre 20200208T135514 et 20200209T135514
Je peux prendre 0 trains qui partent entre 18h et 20h de Gare de Lyon le 17 novembre 2016
In [22]:
# on cherche celui qui arrive le plus tôt à sa destination

def trouver_info_trajet(dep, arr, heure) :
    res = requests.get('https://api.sncf.com/v1/coverage/sncf/journeys?from={}&to={}&datetime={}'.format(dep, arr, heure), \
                         auth=(token_auth, '')).json()
    if 'journeys' not in res:
        if 'error' in res and "no solution" in res["error"]['message']:
            print("Pas de solution pour '{0} --> '{1}' h: {2}.".format(dep, arr, heure))
            return None
    return res['journeys'][0]

# on initiale l'heure à la fin de la journée : on veut réduire cette variable au maximum
# on veut 6h après le départ
heure_minimale = dt + timedelta(hours=8)
destination_la_plus_rapide = None
print("heure_minimale", heure_minimale, " len ", len(dictionnaire_destinations))

# parmi toutes les destinations possibles, on recherche le train qui arrive le plus tôt à sa destination finale
for code, valeurs in dictionnaire_destinations.items() : 
    ''' on prend le code de la gare'''
    code_destination = dictionnaire_destinations[code]['route']['direction']['id']
    ''' on regarde à quelle heure arrive le train'''
    trajet = trouver_info_trajet('stop_area:OCE:SA:87686006',code_destination,
                                 dictionnaire_destinations[code]['stop_date_time']['arrival_date_time'])
    if trajet is None:
        continue
    if heure_minimale > convertir_en_temps(trajet['arrival_date_time']) : 
        heure_minimale = convertir_en_temps(trajet['arrival_date_time'])
        destination_la_plus_rapide = dictionnaire_destinations[code]
heure_minimale 2020-02-08 21:55:14.033691  len  0
In [23]:
if destination_la_plus_rapide is not None:
    print(destination_la_plus_rapide['display_informations']['direction'], heure_minimale)
else:
    print("pas de résultat")
pas de résultat

Et les correspondances ?

Question 4

  • On va essayer de voir jusqu'où on peut aller, en prenant des trains au départ de la Gare de Lyon :
    • Quelles sont toutes les gares atteignables en partant le 17 novembre, sans faire de changement et sans partir après minuit ?
    • Si on prend un de ces trains, jusqu'où peut-on aller, avec une correspondance, sans partir après 8h le lendemain matin ?
In [24]:
# on va trouver toutes les gares qui sont sur les trajets des 
# trains retenus donc atteignables sans correspondance

def trouver_toutes_les_gares_du_trajet(gare_depart, gare_arrivee_finale, horaire_depart) :
    return requests.get('https://api.sncf.com/v1/coverage/sncf/journeys?from={}&to={}' \
                        '&datetime={}'.format(gare_depart,gare_arrivee_finale,horaire_depart), \
                        auth=(token_auth, '')).json()
In [25]:
# Exemple pour la première gare de la liste

if len(dictionnaire_destinations) > 1:
    gare_depart = dictionnaire_destinations[1]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[1]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[1]['stop_date_time']['arrival_date_time']

    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)
    session = trajet_recherche['journeys'][0]['sections'][0]
    if "stop_date_times" in session:
        for i in session['stop_date_times']:
            print(i['stop_point']['name'])
In [26]:
# on fait la liste des gares où on
# peut aller sans correspondance

liste_gares_direct = []

for x in dictionnaire_destinations.keys():
    # on prend les deux gares départ + finale
    gare_depart = dictionnaire_destinations[x]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[x]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[x]['stop_date_time']['arrival_date_time']
    
    # on appelle la fonction définie précédemment
    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)
    if 'error' in trajet_recherche:
        continue
    session = trajet_recherche['journeys'][0]['sections'][0]
    if "stop_date_times" in session:
        for i in session['stop_date_times']: 
            print(i['stop_point']['name'], i['arrival_date_time'])
            liste_gares_direct.append(i['stop_point']['name'])
        print("-------------")    
    
#### là on a la liste des gares atteignables sans correspondance    
liste_gares_direct = set(liste_gares_direct)

Exemple : trouver toutes les correspondances possibles depuis le trajet entre les gares de Paris et de Perpignan

In [27]:
# pour le premier trajet gare de la liste trouvée à l'étape précédente
# on va chercher toutes les connexions des gares possibles, entre le moment de l'arrivée 
# et 8 heures le lendemain matin

if len(dictionnaire_destinations) > 1:
    gare_depart = dictionnaire_destinations[1]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[1]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[1]['stop_date_time']['arrival_date_time']

    horaire_max = convertir_en_chaine(dt + timedelta(hours=8))
    print("horaire_max", horaire_max)
else:
    horaire_train = None
In [28]:
# en partant de gare de lyon en direction de Perpignan

if horaire_train is not None:
    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart,gare_arrivee,horaire_train)

dictionnaire_correspondances = {}

if (horaire_train is not None and 
        'stop_date_times' in trajet_recherche['journeys'][0]['sections'][0]):
    for i in trajet_recherche['journeys'][0]['sections'][0]['stop_date_times']: 

        #print("la gare où on est descendu depuis Paris", i['stop_point']['name'])

        if i['stop_point']['id'] == "stop_point:OCE:SP:TGV-87686006" : 
            #print("on ne prend pas la gare de Lyon - ce n'est pas une gare du trajet")
            pass

        else :
            # on va appliquer à nouveau la fonction des trajets disponibles mais pour l'ensemble des gares
            gare_dep_connexion = i['stop_point']['id']
            nom_gare_dep = i['stop_point']['name']
            heure_dep_connexion = i['arrival_date_time']

            trajet_recherche_connexion = trouver_trajet_dispo_max_heure(gare_dep_connexion, heure_dep_connexion, horaire_max)

            test_as_connexion_on_time = True

            # pour chaque trajet possible depuis la gare où on est arrivé depuis paris, on va vérifier qu'on part bien  
            # avant 8h le lendemain
            autre_gare = None
            for vers_autre_gare in trajet_recherche_connexion :  
                heure_depart_depuis_autre_gare = vers_autre_gare['stop_date_time']['departure_date_time']
                destination_trajet = vers_autre_gare['display_informations']['direction']

                if convertir_en_temps(heure_depart_depuis_autre_gare) < convertir_en_temps(horaire_max) : 
                    dictionnaire_correspondances[(nom_gare_dep,heure_depart_depuis_autre_gare)] = destination_trajet  
                    test_as_connexion_on_time = False
                    # print(nom_gare_dep,heure_depart_depuis_autre_gare, "gare finale du trajet", destination_trajet)
                    autre_gare = vers_autre_gare

            if autre_gare and test_as_connexion_on_time: 
                dictionnaire_correspondances[(nom_gare_dep,autre_gare['stop_date_time']['departure_date_time'])] = ""
else:
    print("impossible de trouver 'stop_date_times'")
                
# on garde toutes les gares où on peut aller depuis une des gares de correspondance, avec un départ avant 8H
dictionnaire_correspondances
impossible de trouver 'stop_date_times'
Out[28]:
{}
In [29]:
# Pour les trajets qui partent avant 8h des gares, on va chercher toutes les gares qui sont sur le trajet
gares_avec_connexion = []
for k,v in dictionnaire_correspondances.items() : 
    if len(v) == 0 : 
        pass
    else :
        if k[0] not in dict_nom_gare_code:
            print("'{0}' pas trouvé dans {1}".format(k[0], ", ".join(
                sorted(_ for _ in dict_nom_gare_code if isinstance(_, str) and _[:4] == k[0][:4]))))
            continue
        if v not in dict_label_gare_code:
            print("'{0}' pas trouvé dans {1}".format(v, ", ".join(
                sorted(_ for _ in dict_label_gare_code if isinstance(_, str) and _[:4] == v[:4]))))
            continue

        dep = dict_nom_gare_code[k[0]]
        arr = dict_label_gare_code[v]
        
        gares_entre_dep_arr = trouver_toutes_les_gares_du_trajet(dep, arr,k[1])
        for gare in gares_entre_dep_arr['journeys'][0]['sections'][1]['stop_date_times']: 
            #print("gare depart:", k[0], gare['stop_point']['name'])
            gares_avec_connexion.append(gare['stop_point']['name'])
            
# la liste des gares atteignables avec 1 correspondance            
gares_avec_connexion = set(gares_avec_connexion)
In [30]:
print(gares_avec_connexion)
set()
In [31]:
# on crée la liste des gares atteignables seulement avec une correspondance (pas directement atteignable)
gares_atteintes_avec_connexion = [a for a in gares_avec_connexion if (a not in liste_gares_direct)]
print(gares_atteintes_avec_connexion)
[]
Exemple : trouver toutes les correspondances possibles depuis les trains qu'on prend de la Gare de Lyon

Maintenant qu'on a fait un exemple, on le fait pour tous les trajets qui partent de la Gare de Lyon

!!! Attention cette celulle prend du temps (beaucoup beaucoup de temps) !!!!

In [32]:
gares_avec_connexion = []

for gare_initiale in dictionnaire_destinations: 
    # pour le premier trajet gare de la liste trouvée à l'étape précédente
    # on va chercher toutes les connexions des gares possibles
    print(gare_initiale, "/", len(dictionnaire_destinations))

    gare_depart = dictionnaire_destinations[gare_initiale]['stop_point']['id']
    gare_arrivee = dictionnaire_destinations[gare_initiale]['route']['direction']['id']
    horaire_train = dictionnaire_destinations[gare_initiale]['stop_date_time']['arrival_date_time']
    
    # Pour les trajets qui partent avant 8h des gares, on va chercher toutes les gares qui sont sur le trajet
    
    trajet_recherche = trouver_toutes_les_gares_du_trajet(gare_depart, gare_arrivee, horaire_train)

    dictionnaire_correspondances = {}
    if 'journeys' not in trajet_recherche:
        print("Pas de trajet entre '{0}' et '{1}' h={2}.".format(gare_depart, gare_arrivee, horaire_train))
        continue
    session = trajet_recherche['journeys'][0]['sections'][0] 

    if "stop_date_times" in session:
        for i in session['stop_date_times']: 

            if i['stop_point']['id'] == "stop_point:OCE:SP:TGV-87686006" : 
                #print("on ne prend pas la gare de Lyon - ce n'est pas une gare du trajet")
                pass

            else :
                # on va appliquer à nouveau la fonction des trajets disponibles mais pour l'ensemble des gares
                gare_dep_connexion = i['stop_point']['id']
                nom_gare_dep = i['stop_point']['name']
                heure_dep_connexion = i['arrival_date_time']

                trajet_recherche_connexion = trouver_trajet_dispo_max_heure(gare_dep_connexion, heure_dep_connexion, horaire_max)

                test_as_connexion_on_time = True

                # pour chaque trajet possible depuis la gare où on est arrivé depuis paris, on va vérifier qu'on part bien  
                # avant 8h le lendemain
                for vers_autre_gare in trajet_recherche_connexion :  
                    heure_depart_depuis_autre_gare = vers_autre_gare['stop_date_time']['departure_date_time']
                    destination_trajet = vers_autre_gare['display_informations']['direction']

                    if convertir_en_temps(heure_depart_depuis_autre_gare) < convertir_en_temps(horaire_max) : 
                        dictionnaire_correspondances[(nom_gare_dep,heure_depart_depuis_autre_gare)] = destination_trajet  
                        test_as_connexion_on_time = False

                if test_as_connexion_on_time == True : 
                    dictionnaire_correspondances[(nom_gare_dep,vers_autre_gare['stop_date_time']['departure_date_time'])] = ""

    # on garde toutes les gares où on peut aller depuis une des gares de correspondance, avec un départ avant 8H

    for k,v in dictionnaire_correspondances.items() : 
        if len(v) == 0:
            continue
        if k[0] not in dict_nom_gare_code:
            print("'{0}' pas trouvé dans {1}".format(k[0], ", ".join(
                sorted(_ for _ in dict_nom_gare_code if isinstance(_, str) and _[:4] == k[0][:4]))))
            continue
        if v not in dict_label_gare_code:
            print("'{0}' pas trouvé dans {1}".format(v, ", ".join(
                sorted(_ for _ in dict_label_gare_code if isinstance(_, str) and _[:4] == v[:4]))))
            continue
        dep = dict_nom_gare_code[k[0]]
        arr = dict_label_gare_code[v]
        gares_entre_dep_arr = trouver_toutes_les_gares_du_trajet(dep, arr, k[1])
        if 'journeys' not in gares_entre_dep_arr:
            print("Pas de trajet entre '{0}' et '{1}'.".format(k[0], v))
            continue
        session = gares_entre_dep_arr['journeys'][0]['sections'][1]
        if "stop_date_times" in session:
            for gare in session['stop_date_times'] : 
                gares_avec_connexion.append(gare['stop_point']['name'])
In [33]:
# la liste des gares atteignables avec 1 correspondance            
gares_avec_connexion = set(gares_avec_connexion)
In [34]:
gares_connexion = [a for a in gares_avec_connexion if a not in liste_gares_direct]
print(gares_connexion)
[]

Question 5

  • Représenter toutes les gares atteignables avec un graphique de type scatter. Distinguer les gares atteintes en un seul trajet et celles atteintes avec une correspondance.
In [35]:
######### Type de chaque gare pour le graphique
dict_type_gares = {}
for a in liste_gares_direct :
    dict_type_gares[a] = "direct"
for a in gares_connexion :
    dict_type_gares[a] = "correspondance"    
dict_type_gares['Paris-Gare-de-Lyon'] = 'depart'
dict_type_gares
Out[35]:
{'Paris-Gare-de-Lyon': 'depart'}

On représente tout ça sur un graphique

In [36]:
# on va les représenter grâce à la base des latitude / longitude

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.lines import Line2D 

mpl.rcParams['axes.facecolor'] = "whitesmoke"

palette = plt.cm.spring 

liste_couleurs = [palette(0), palette(0.5), palette(0.8)]

data_all = pd.read_csv("./ensemble_gares.csv", encoding = "ISO-8859-1")

connexions = []
lat = []
lon = []
labels = []

dict_lat = data_all.set_index('name')['lat'].to_dict()
dict_lon = data_all.set_index('name')['lon'].to_dict()
#dict_lab = data_all.set_index('name')['name'].str.replace("gare de","").to_dict()


for gare in dict_type_gares: 
    if gare not in dict_lat:
        print("'{0}' pas trouvé dans dict_lat (problème d'accents?)".format(gare))
        continue
    if gare not in dict_lon:
        print("'{0}' pas trouvé dans dict_lon (problème d'accents?)".format(gare))
        continue
    lat.append(dict_lat[gare])                              
    lon.append(dict_lon[gare])
    labels.append(gare)
In [37]:
%matplotlib inline
In [38]:
### La carte 
###################################################################################################

def liste_unique(liste) : 
    unicite = [] 
    for x in liste : 
        if x in unicite :
            pass
        else :
            unicite.append(x)
    return unicite

lab_un = liste_unique(labels)
lat_un = liste_unique(lat)
lon_un = liste_unique(lon)

fig = plt.figure(figsize=(12,10))

for label, x, y in set(zip(labels, lon, lat)) :
    if dict_type_gares[label] == "direct" :  
        plt.annotate(label, xy = (x - 0.05, y - 0.05), horizontalalignment = 'right', size = 13)
    else :
        plt.annotate(label, xy = (x + 0.05, y + 0.05), horizontalalignment = 'left', size = 13)

colors = []     
for x in lab_un : 
    if dict_type_gares[x] == "depart" : 
        colors.append(liste_couleurs[0])
    if dict_type_gares[x] == "direct" :
        colors.append(liste_couleurs[1])
    if dict_type_gares[x] == "correspondance" : 
        colors.append(liste_couleurs[2])
                
        
plt.scatter(x = lon_un , y = lat_un, marker = "o", c = colors, s = 100, alpha = 0.5)

#### Legende

circ1 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[0])
circ2 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[1])
circ3 = Line2D([0], [0], linestyle="none", marker="o", alpha=0.5, markersize=10, markerfacecolor = liste_couleurs[2])

legende = plt.legend((circ1, circ2, circ3), ("Gare de départ", "Direct depuis Gare de Lyon le soir du 17 novembre", 
                                             "Avec une correspondance depuis une gare directe"), numpoints=1, loc="best")

legende.get_frame().set_facecolor('white')

plt.title("Gares atteignables avant minuit depuis la Gare de Lyon", size = 20);
In [39]: