Introduction à Python

Python est un langage de programmation en pleine croissance dans le domaine de la Data Science et du Big Data. Il a une syntaxe particulière pour certains aspects, avec des points communs tout de même avec de nombreux autres. Nous allons ici aborder les premiers éléments de syntaxe de ce langage.

Eléments de base

Utilisation en mode console

python est un langage scripté, dont l'exécution se fait dans une console. Dans celle-ci, il est donc possible d'exécuter les commandes les unes après les autres. Il est aussi possible (et recommander) d'écrire son script dans un fichier texte (souvent avec l'extension .py) et de l'exécuter via execfile().

Pour accéder à l'aide d'une fonction, il existe la fonction help(), prenant éventuellement en paramètre une fonction directement, ou une chaîne de caractère. Si la fonction n'a pas de paramètre, elle démarre l'aide interactive.

Notebook

Le module jupyter permet de travailler avec des notebooks, qui sont des documents contenant à la fois le code, les résultat et du texte. Ce document est ainsi un notebook.

Types de données

Comme tous les autres langages, python a plusieurs types possibles pour les données. En voici quelques uns. Vous pouvez exécuter les commandes dans une console pour voir le résultat.

type valeur type() commentaires
entier 1 int
réel 1.234 float
chaîne de caractères "un mot" str on peut utiliser indifférement "" ou ''
liste [1, 2] list chaque élément peut être différent des autres
tuple (1, 2) tuple similaire à une liste mais constante
dictionnaire {"a": 1, "b": "deux"} dict ensemble de valeurs quelconques nommées

Les tuples, lists et les dicts peuvent s'imbriquer les uns dans les autres.

(1, (2, 3), [4, 5], {"a": 1, "b": "deux"})
[1, [2, 3], (4, 5), {"a": 1, "b": "deux"}]
{"a": 1, "b": "deux", "c": (5, 6), "d": [7, 8]}

Il existe des fonctions permettant de passer d'un type à l'autre (quand cela est possible), telles que int(), float(), str(), tuple() et list().

Il existe aussi des valeurs prédéfinies, telles que True (vrai), False (faux) et None (donnée absente).

Création et suppression de variables

Il n'y a pas de mot-clé pour la définition d'une variable. Celle-ci est définie/créée lors de sa première affectation. Si elle n'existe pas mais qu'on essaie de l'utiliser, alors un message d'erreur apparaît. Il est aussi possible de la supprimer via la fonction del().

Type dynamique

Bien que python soit rigoureux dans l'évaluation des expressions (il ne fait pas de cast automatique - i.e. changement de type des données), le type d'une variable est dit dynamique. Le type d'une variable dépend uniquement de la valeur de son affectation. Voici un exemple simple de ce phénomène.

In [1]:
a = 1
print(a)
type(a)
1
Out[1]:
int
In [2]:
a = "deux"
print(a)
type(a)
deux
Out[2]:
str

La variable a est passée du type int au type str sans qu'on l'explicite. Il faut donc faire attention lors de l'écriture de ses programmes.

Affichage

Comme vu précédemment, il existe la fonction print() permettant d'afficher du texte et/ou le contenu des variables dans la console. Celle-ci peut prendre les paramètres sep, qui permet d'indiquer le ou les caractères séparant les champs (un espace " " par défaut), et end, qui permet d'indiquer le caractère de fin de ligne (retour à la ligne "\n" par défaut).

In [3]:
a = 1.234
print("Bonjour")
print("a =", a)
print("a", a, sep = "=")
print("a=", end = "") # permet de ne pas revenir à la ligne 
print(a) # la valeur s'affiche donc à la suite de la ligne précédente
Bonjour
a = 1.234
a=1.234
a=1.234

Opérateurs

Il existe bien évidemment tous les opérateurs classiques, tels que présentés ci-dessous.

Type Opérateurs
Arithmétiques + - * / // % **
Comparaisons > >= < <= == !=
Booléens | & not()

Eléments de langage

Traitement conditionnel

Comme dans tout langage, le traitement conditionnel se fait à partir d'un if. Voici un exemple très simple d'utilisation.

In [4]:
a = 3
if (a > 2):
    print("sup")
sup

Une particularité de python est d'utiliser l'indentation (i.e. le décalage à droite à l'aide d'au moins une tabulation) pour définir les opérations à réaliser dans un bloc. Si on veut faire plusieurs opérations dans le if, voila comment procéder par exemple.

In [5]:
if (a > 2):
    print("dans le IF")
    print("sup")
dans le IF
sup

Il existe aussi la possibilité d'ajouter soit un traitement alternatif simple (avec else), soit un traitement alternatif conditionnel lui aussi (avec elif).

In [6]:
# Avec un else seulement
a = 1
if (a > 2):
    print("sup")
else:
    print("inf")
inf
In [7]:
# Avec elif en plus
if (a > 2):
    print("sup")
elif (a > 0):
    print("mid")
else:
    print("inf")
mid

Il n'existe rien dans python pour le switch/casetel qu'on peut le voir par ailleurs. Mais on peut passer par un dictionnaire pour des tests d'égalité (cf plus bas pour plus d'informations sur les dictionnaires).

In [8]:
jour = {
    0: "lundi",
    1: "mardi",
    2: "mercredi",
    3: "jeudi",
    4: "vendredi",
    5: "samedi",
    6: "dimanche"
}
jour.get(2)
Out[8]:
'mercredi'

Traitement itératif

On utilise en premier la boucle for dans laquelle on peut utiliser la fonction range() pour avoir les valeurs entre 0 (par défaut) et la valeur passée en paramètre. Vous remarquerez que i est persistant à la boucle et garde la dernière valeur.

In [9]:
for i in range(5):
    print(i)
print("dernière valeur de i :", i)
0
1
2
3
4
dernière valeur de i : 4

Cette fonction range() peut prendre deux paramètres, et dans ce cas, génère la boucle entre les deux par pas de 1.

In [10]:
for i in range(5, 10):
    print(i)
5
6
7
8
9

Et si l'on souhaite modifier le pas de séquence, on ajoute un troisième paramètre. Celui-ci doit être cohérent par rapport aux deux premières valeurs.

In [11]:
for i in range(10, 5, -1):
    print(i)
10
9
8
7
6

Il est possible d'utiliser une list ou un tuple pour définir les valeurs dans lesquelles naviguer.

In [12]:
for i in [4, 1, 10]:
    print(i)
4
1
10

Et en utilisant une chaîne de caractère, on peut naviguer dans celle-ci.

In [13]:
for l in "Bonjour":
    print(l)
B
o
n
j
o
u
r

Mais en utilisant un groupe de chaîne (list ou tuple), on travaille sur les chaînes au complet.

In [14]:
for l in ("jour", "soir"):
    print("Bon", l, sep = "")
Bonjour
Bonsoir

La fonction enumerate() permet de récupérer à la fois les indices des valeurs et les valeurs.

In [15]:
a = [3, 1, 9, 4]
for i, x in enumerate(a):
    print("i =", i, "\tx =", x)
i = 0 	x = 3
i = 1 	x = 1
i = 2 	x = 9
i = 3 	x = 4

Et la fonction zip() permet elle de travailler sur plusieurs groupes de valeurs (ici deux listes). Notez que cette fonction limite le résultat à la taille du plus petit regroupement.

In [16]:
b = ["trois", "un", "neuf"]
for i, j in zip(a, b):
    print(" i =", i, "\tj =", j)
 i = 3 	j = trois
 i = 1 	j = un
 i = 9 	j = neuf

On peut aussi faire une boucle sur un dictionnaire, l'itérateur prenant les valeurs des champs de celui-ci.

In [17]:
for i in jour:
    print("i =", i, "\tjour(i) = ", jour[i])
i = 0 	jour(i) =  lundi
i = 1 	jour(i) =  mardi
i = 2 	jour(i) =  mercredi
i = 3 	jour(i) =  jeudi
i = 4 	jour(i) =  vendredi
i = 5 	jour(i) =  samedi
i = 6 	jour(i) =  dimanche

Enfin, on dispose aussi de la boucle while qui teste en début de boucle si une condition est toujours vérifiée. Bien évidemment, à la fin de la boucle, i a la première valeur à rendre la condition fausse. Ici, i += 1 est un raccourci pour i = i + 1.

In [18]:
i = 0
while i < 10:
    print(i)
    i += 1
print("Valeur de i :", i)
0
1
2
3
4
5
6
7
8
9
Valeur de i : 10

Création et manipulations d'objets

Comme indiqué, il existe différents types d'objets en python. Sont présentés ici des exemples de créations et de manipulations de chaînes (str), de tuples, de list et de dictionnaires (dict).

Chaînes

Une chaîne de caractère se définit à l'aide des quotes simples ('') ou doubles (""). Par défaut, python présentera les chaînes avec des simples quotes. Mais en présence d'une apostrophe dans la chaîne, il faut la déclarer avec des doubles quotes. Il est possible de connaître la longueur de la chaîne avec la fonction len().

In [19]:
"bonjour"
Out[19]:
'bonjour'
In [20]:
"aujourd'hui"
Out[20]:
"aujourd'hui"
In [21]:
a = 'bonjour'
len(a)
Out[21]:
7

Pour extraire des sous-chaînes, on utilise l'indexation en séquence python, en prenant en compte que le premier caractère est en position 0. La séquence par défaut est par pas de 1 (par exemple, 1:5 renvoie les positions 1, 2, 3, 4, 5). Si on omet le premier ou le dernier, python comprend qu'on désire le début ou la fin de la chaîne. On peut ajouter un paramètre à la séquence, permettant de jouer sur le pas entre les valeurs de la séquence.

In [22]:
a[1:5]
Out[22]:
'onjo'
In [23]:
a[0:3]
Out[23]:
'bon'
In [24]:
a[:3]
Out[24]:
'bon'
In [25]:
a[3:len(a)]
Out[25]:
'jour'
In [26]:
a[3:]
Out[26]:
'jour'
In [27]:
a[::]
Out[27]:
'bonjour'
In [28]:
a[::-1]
Out[28]:
'ruojnob'
In [29]:
a[::2]
Out[29]:
'bnor'
In [30]:
a[1:5:2]
Out[30]:
'oj'
In [31]:
a[5:1:-2]
Out[31]:
'uj'

Sur ces chaînes, on peut réaliser un certain nombre d'opérations classiques, telles que le changement de casse (upper() ou lower()), la mise en majuscule des premières lettres de chaque mot (capitalize()), la recherche d'une sous-chaîne (find() - première occurence), le remplacement d'une sous-chaîne (replace()), le dénombrement de sous-chaînes (count()) ou le découpage en sous-chaînes selon un caractère (split()). En voici quelques exemples.

In [32]:
a.upper()
Out[32]:
'BONJOUR'
In [33]:
a.capitalize()
Out[33]:
'Bonjour'
In [34]:
a.find('j')
Out[34]:
3
In [35]:
a.replace('jour', 'soir')
Out[35]:
'bonsoir'
In [36]:
a.count('o')
Out[36]:
2
In [37]:
a.split('j')
Out[37]:
['bon', 'our']

Tuples

Un tuple en python est un ensemble déclaré via des (), composé de valeurs pas forcément de même type et éventuellement complexe, qu'il n'est pas possible de modifier. C'est en quelque sorte une constante, une fois déclarée.

Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.

Listes

Une list est aussi un ensemble déclarée via des [], composé d'éléments pas forcément tous du même type et possiblement complexe. A la différence d'un tuple, une liste est modifiable.

In [38]:
a = [3, 1, 9, 7]
print(a)
[3, 1, 9, 7]
In [39]:
len(a)
Out[39]:
4
In [40]:
a[0]
Out[40]:
3
In [41]:
a[1:3]
Out[41]:
[1, 9]

Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.

Nous disposons sur ces listes de plusieurs fonctions tels que reverse() (pour inverser la liste), sort() (tri, avec l'option reverse pour le choix du tri), pop() (pour récupérer et supprimer le dernier élément), append() (pour ajouter un élément à la fin), insert() (pour insérer un élément dans la liste, à la position indiquée - paramètres = position suivie de la valeur), remove() (pour supprimer les valeurs passées en paramètre). Toutes ces fonctions modifient directement la liste sur laquelle on les applique.

In [42]:
a.reverse()
print(a)
[7, 9, 1, 3]
In [43]:
a.sort()
print(a)
[1, 3, 7, 9]
In [44]:
a.sort(reverse=True)
print(a)
[9, 7, 3, 1]
In [45]:
a.pop()
print(a)
[9, 7, 3]
In [46]:
a.append(5)
print(a)
[9, 7, 3, 5]
In [47]:
a.insert(0, 6)
print(a)
[6, 9, 7, 3, 5]
In [48]:
a.remove(7)
print(a)
[6, 9, 3, 5]

Un autre moyen d'insérer une valeur, voire plusieurs, à une liste est d'utiliser l'opérateur +, tel qu'indiqué ci-dessous. Celui-ci permet une concaténation des deux listes en une seule. L'opérateur * permet lui de répéter une liste autant de fois que désiré.

In [49]:
a + [1, 2]
a * 2
Out[49]:
[6, 9, 3, 5, 6, 9, 3, 5]

On peut utiliser un mécanisme spécifique, appelé list comprehension (fonctionnant aussi sur les chaînes et les tuples), permettant de récupérer les valeurs (ou un calcul sur chaque valeur) pour tous les éléments de la liste (ou certains si on applique un if).

In [50]:
a = [3, 1, 9, 7]
[x**2 for x in a]
Out[50]:
[9, 1, 81, 49]
In [51]:
[x**2 for x in a if x >= 4]
Out[51]:
[81, 49]

Par contre, il faut faire très attention au passage de référence lorsqu'on copie une liste. En effet, dans le code suivant, on copie a dans b. Et en modifiant a, on remarque que b est aussi modifié. Et l'inverse est aussi vrai.

In [52]:
a = [1, 2, 3, 4]
print(a)
[1, 2, 3, 4]
In [53]:
b = a
print(b)
[1, 2, 3, 4]
In [54]:
a[0] = 5
print(a)
print(b)
[5, 2, 3, 4]
[5, 2, 3, 4]
In [55]:
b[1] = 9
print(b)
print(a)
[5, 9, 3, 4]
[5, 9, 3, 4]

Pour remédier à ce problème, on doit duppliquer la liste avec la fonction copy() de la liste initiale, comme ci-dessous.

In [56]:
b = a.copy()
a[0] = -1
print(a)
print(b)
[-1, 9, 3, 4]
[5, 9, 3, 4]

Dictionnaires

Les dictionnaires (dict en python) sont des listes nommées (définies via des {}), c'est-à-dire que chaque élément a un nom (appelé aussi clé). Ces éléments ne sont pas forcément tous du même type, et peuvent aussi être complexe.

In [57]:
a = { "nom": "Jollois", "prenom": "FX", "langues": ["R", "Python", "SQL", "SAS"], "labo": { "nom": "LIPADE", "lieu": "CUSP"}}
print(a)
len(a)
{'nom': 'Jollois', 'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'labo': {'nom': 'LIPADE', 'lieu': 'CUSP'}}
Out[57]:
4

Pour accéder aux éléments du dictionnaire, on peut utiliser le formalisme suivant.

In [58]:
a["nom"]
Out[58]:
'Jollois'
In [59]:
a["langues"]
Out[59]:
['R', 'Python', 'SQL', 'SAS']
In [60]:
a["langues"][0]
Out[60]:
'R'
In [61]:
a["labo"]
Out[61]:
{'nom': 'LIPADE', 'lieu': 'CUSP'}
In [62]:
a["labo"]["lieu"]
Out[62]:
'CUSP'

Il existe aussi des fonctions utiles sur ces objets, telles que get() (pour récupérer la valeur d'une clé), keys() (pour avoir la liste des clés de l'objet), values() (pour avoir les valeurs des clés, dans le même ordre que listé dans keys()), popitem() (pour récupérer un dictionnaire avec le dernier item, et le supprimer du dictionnaire initiale) et pop() (pour récupérer la valeur de l'item passé en paramètre, et le supprimer de l'élément de départ).

In [63]:
a.get("nom")
Out[63]:
'Jollois'
In [64]:
a.keys()
Out[64]:
dict_keys(['nom', 'prenom', 'langues', 'labo'])
In [65]:
a.values()
Out[65]:
dict_values(['Jollois', 'FX', ['R', 'Python', 'SQL', 'SAS'], {'nom': 'LIPADE', 'lieu': 'CUSP'}])
In [66]:
a.popitem()
Out[66]:
('labo', {'nom': 'LIPADE', 'lieu': 'CUSP'})
In [67]:
print(a)
{'nom': 'Jollois', 'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS']}
In [68]:
a.pop("nom")
Out[68]:
'Jollois'
In [69]:
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS']}

On peut ajouter facilement un item à un dictionnaire, en lui affectant une valeur.

In [70]:
a["type"] = "MCF"
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}

De même que pour les listes, il faut faire attention lors de l'affectation d'un dictionnaire à un autre. La fonction copy() permet donc d'obtenir une copie indépendante de l'objet initial.

In [71]:
b = a
b["prenom"] = "Xavier"
print(b)
print(a)
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
In [72]:
b = a.copy()
b["prenom"] = "FX"
print(b)
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}

Le mécanisme de list comprehension est aussi utilisable pour créer un dictionnaire. Il faut dans ce cas indiquer deux valeus : la clé et sa valeur. Dans notre cas, la fonction dict() appliqué sur le résultat de la fonction zip() des deux listes nous permet d'avoir le même résultat.

In [73]:
fruits = ["pommes", "bananes", "poires", "oranges"]
nombres = [5, 2, 10, 4]
{fruits[i]:nombres[i] for i in range(4)}
dict(zip(fruits, nombres))
Out[73]:
{'pommes': 5, 'bananes': 2, 'poires': 10, 'oranges': 4}

Fonctions

Définition

L'opérateur def permet de créer une fonction (ou une procédure qui sera juste une fonction ne renvoyant rien). L'opérateur return indiquant le résultat à renvoyer le cas échéant. Le premier exemple est une fonction renvoyant une valeur (approximative) de $\pi$. Comme pour un if, le bloc d'instructions est défini selon l'indentation.

In [74]:
def pi():
    res = 3.141593 ** 2
    return res
pi()
Out[74]:
9.869606577649

Ce deuxième exemple est une procédure affichant tout simplement "Bonjour".

In [75]:
def afficheBonjour():
    print("Bonjour")
afficheBonjour()
Bonjour

Il est bien évidemment possible de passer un paramètre à une fonction, sans qu'on ait à déclarer son type. Bien sûr, un appel de la fonction sans valeur pour un paramètre défini entraîne une erreur.

In [76]:
def afficheBonjour(nom):
    print("Bonjour", nom)
afficheBonjour("Jollois")
Bonjour Jollois

Lorqu'il y a plus d'un paramètre, on peut faire un appel classique. Mais il est aussi possible de nommer explicitement les paramètres. Avec ce mécanisme, il est ainsi possible de les déclarer dans l'ordre que l'on veut. Mais si l'on nomme un paramètre, il est obligatoire de nommer les autres (erreur d'exécution sinon).

In [77]:
def afficheBonjour(nom, prenom):
    print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
Bonjour FX Jollois
In [78]:
afficheBonjour(nom = "Jollois", prenom = "FX")
Bonjour FX Jollois
In [79]:
afficheBonjour(prenom = "FX", nom = "Jollois")
Bonjour FX Jollois

Il existe la possibilité de définir une valeur par défaut à un paramètre dans une fonction. Ceci permet d'appeler la fonction sans donner de valeur pour ce paramètre (la fonction utilisera celle par défaut donc).

In [80]:
def afficheBonjour(nom, prenom = "?"):
    print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
Bonjour FX Jollois
In [81]:
afficheBonjour("Jollois")
Bonjour ? Jollois

Pureté d'une fonction

Une fonction est dite pure si elle n'a pas d'effet de bords lors de son appel. Dans la fonction f1() définie ci-dessous, le paramètre a est local et son affectation ne change pas la valeur de la variable a globale. f1() sera donc considérée comme pure.

In [82]:
def f1(a):
    a = [b**2 for b in a]
    return a
a = list(range(5))
print(a)
print(f1(a))
print(a)
[0, 1, 2, 3, 4]
[0, 1, 4, 9, 16]
[0, 1, 2, 3, 4]

Maintenant, si nous définissons la fonction comme f2() ci-dessous, nous remarquons que la variable a globale est modifiée suite à l'appel de la fonction. f2() est considérée impure.

In [83]:
def f2(a):
    for i in a:
        a[i] = a[i] ** 2
    return a
a = list(range(5))
print(a)
print(f2(a))
print(a)
[0, 1, 2, 3, 4]
[0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]

Gestion des erreurs

Il arrive régulièrement que l'on doive gérer les erreurs d'exécution dans une fonction. On utilise pour cela l'opérateur try, qui contiendra le code à exécuter normalement. Et dans le bloc except, on indiquera la marche à suivre en cas d'erreur lors de l'exécution du try. On peut définir une suite d'instruction à réaliser après la gestion de l'erreur avec un bloc finally (optionnel).

Ci-dessous, nous définissons une fonction renvoyant la somme d'une liste ou d'un tuple. Si la somme est impossible à réaliser (par exemple, car il y a une chaîne dans la liste passée en paramètre), on affiche un message d'erreur et on renvoie la valeur None.

In [84]:
def somme(v):
    try:
        res = sum(v)
    except:
        print("Erreur : somme impossible !")
        res = None
    finally:
        return res
a = somme([1, 3, 5])
print(a)
a = somme(["un", 3, 5])
print(a)
9
Erreur : somme impossible !
None

Fonctions sur Listes

Dans le cadre de l'utilisation de liste, il est nécessaire d'utiliser des fonctions particulières, dites high order functions. Ces dernières prennent en paramètre une fonction et une liste.

Pour les exemples, nous définissons une liste de nombres.

In [85]:
a = [13, 7, 2, 9, 1, 10, 6, 3, 8]
print(a)
[13, 7, 2, 9, 1, 10, 6, 3, 8]

map()

La fonction map() permet d'appliquer une fonction sur chaque élément d'une liste (ou d'un tuple). Elle renvoie un objet de type map que l'on peut transformer en liste avec la fonction list(). Une fois celle-ci appliqué, l'objet renvoyé par map() est vide.

Ici, nous définissons une fonction carre() qui renvoie le carré du paramètre passé. Et nous l'appliquons sur chaque élément de la liste a créé précédemment.

In [86]:
def carre(v):
    return v ** 2
carre(5)
Out[86]:
25
In [87]:
b = map(carre, a)
print(list(b))
[169, 49, 4, 81, 1, 100, 36, 9, 64]

filter()

L'idée de la fonction filter() est de filtrer les valeurs de la liste, en fonction du résultat de la fonction passée en paramètre. De même que pour map(), nous listons les résultats pour l'avoir sous une forme de list.

La fonction pair() ci-dessous teste si la valeur passée en paramètre est divisible par 2 (donc si elle est paire). L'utlisation de la fonction filter() sur a, avec cette fonction, nous renvoie les valeurs de a paires.

In [88]:
def pair(v):
    return v%2 == 0
pair(5)
pair(8)
Out[88]:
True
In [89]:
b = filter(pair, a)
print(list(b))
[2, 10, 6, 8]

reduce()

La dernière fonction intéressante sur les listes est reduce(), permettant de réduire une liste en une valeur. Elle est contenue dans le module functools, qu'on importe via la commande import.

La fonction à passer en paramètre doit donc prendre deux valeurs et renvoyer une valeur de même type. Le deuxième paramètre est la liste sur laquelle appliquer la réduction. Il est aussi possible d'ajouter une valeur initiale.

Cette fonction reduce() regroupe deux éléments de liste ensemble, puis regroupe le résultat précédent avec l'élément suivant, et ainsi de suite, jsuq'à épuisement de la liste.

Voici un exemple d'utilisation de reduce() pour le calcul de la somme des éléments d'une liste. On définit la fonction somme() qui prend deux éléments et qui renvoie la somme de deux. On l'applique, via reduce() sur la liste. Le résultat obtenu est bien la somme sur a. Le deuxième appel est avec une valeur initialisée à 100.

In [90]:
def somme(v1, v2): 
    return v1 + v2
somme(3, 10)
Out[90]:
13
In [91]:
sum(a)
Out[91]:
59
In [92]:
import functools
b = functools.reduce(somme, a)
print(b)
59
In [93]:
b = functools.reduce(somme, a, 100)
print(b)
159

Fonction anonyme

Dans ces appels de fonctions, il est courant de passer en paramètre une fonction simple. Dans ce cadre, il existe un mécanisme évitant la déclaration de la fonction avant. On parle de fonction anonyme, déclarée via l'opérateur lambda. Le principe est de définir la fonction lors de son passage comme paramètre. La contrainte est que cette fonction doit tenir sur une seule ligne.

Dans l'exemple qui suit, nous appliquons une fonction calculant le carré d'une valeur à une liste, via la fonction map().

In [94]:
b = map(lambda v: v ** 2, a)
print(list(b))
[169, 49, 4, 81, 1, 100, 36, 9, 64]

Il est possible d'ajouter une condition if dans la fonction lambda avec un formalisme de type valeurTrue if condition else valeurFalse. Ci-après, nous calculons le carré de chaque valeur, multiplié par -1 pour celles inférieur ou égale à 8.

In [95]:
b = map(lambda v: v **2 if v > 8 else -(v ** 2), a)
print(list(b))
[169, -49, -4, 81, -1, 100, -36, -9, -64]

Manipulation de données

Nous allons utiliser dans ce TP le module pandas permettant la gestion de données avec Python dans un format (individus décrits par des variables) plus classique pour les méthodes statistiques.

Importation de données avec pandas

Nous allons travailler sur les données tips. Vous pouvez trouver des informations (ici). Voici comment lire ces données dans python avec read_csv() de pandas.

In [96]:
import pandas

# Lecture d'un fichier texte
tips = pandas.read_csv("tips.csv", header = 0, sep = ",")
tips.head()
Out[96]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

Sur ces données, il est bien évidemment possible de voir quelques informations classiques.

In [97]:
# Type de la variable
type(tips)
Out[97]:
pandas.core.frame.DataFrame
In [98]:
# nombre de lignes et de colonnes de la table
tips.shape
Out[98]:
(244, 7)
In [99]:
# nombre de valeurs présentes dans chaque colonne
tips.count()
Out[99]:
total_bill    244
tip           244
sex           244
smoker        244
day           244
time          244
size          244
dtype: int64
In [100]:
# détail complet de la table
tips.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
total_bill    244 non-null float64
tip           244 non-null float64
sex           244 non-null object
smoker        244 non-null object
day           244 non-null object
time          244 non-null object
size          244 non-null int64
dtypes: float64(2), int64(1), object(4)
memory usage: 13.5+ KB
In [101]:
# nom des colonnes (au format liste)
list(tips.columns)
Out[101]:
['total_bill', 'tip', 'sex', 'smoker', 'day', 'time', 'size']
In [102]:
# idem
list(tips)
Out[102]:
['total_bill', 'tip', 'sex', 'smoker', 'day', 'time', 'size']

Manipulation à la SQL

Une fois qu'on a des données, la première chose qu'on souhaite savoir faire souvent, est de pouvoir manipuler ces données; C'est-à-dire réaliser les opérations classiques en bases de données, à savoir :

  • Restriction
  • Projection
  • Tri et limitation des résultats
  • Ajout de nouvelles variables (via des valeurs ou un calcul à partir des autres)
  • Agrégats
  • Jointures (qui ne seront pas vues ici)

Il y a d'autres opérations, spécifiques à Python, qu'on ne verra pas ici. La dernière opération que l'on verra est la modification de la forme du tableau de données.

Note Bene : certaines fonctions renvoient un nouvel objet qu'il faudra donc stocker dans une variable (nouvelle ou la même). Par contre, d'autres fonctions modifient directement l'objet en question.

Restriction

Première étape essentiel, cela consiste à sélectionner un certain nombre de lignes de la table, selon une condition sur les valeurs des variables. Il exite pour cela la fonction query() prenant en paramètre une chaîne de caractères contenant la condition à appliquer. Voici quelques exemples de condition. Il y a bien évidemment beaucoup d'autres fonctions existantes, en particuliers pour les chaînes.

In [103]:
tips.query('total_bill > 48') # que les factures de plus de 48$
Out[103]:
total_bill tip sex smoker day time size
59 48.27 6.73 Male No Sat Dinner 4
156 48.17 5.00 Male No Sun Dinner 6
170 50.81 10.00 Male Yes Sat Dinner 3
212 48.33 9.00 Male No Sat Dinner 4
In [104]:
tips.query('day.isin(("Sat", "Sun"))') # que les factures ayant eu lieu un samedi ou un dimanche
Out[104]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
... ... ... ... ... ... ... ...
238 35.83 4.67 Female No Sat Dinner 3
239 29.03 5.92 Male No Sat Dinner 3
240 27.18 2.00 Female Yes Sat Dinner 2
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2

163 rows × 7 columns

In [105]:
tips.query('size > 4 & sex == "Male"') # que les tables de plus de 4 convives et payées par un homme
Out[105]:
total_bill tip sex smoker day time size
141 34.30 6.7 Male No Thur Lunch 6
142 41.19 5.0 Male No Thur Lunch 5
156 48.17 5.0 Male No Sun Dinner 6
185 20.69 5.0 Male No Sun Dinner 5
187 30.46 2.0 Male Yes Sun Dinner 5
216 28.15 3.0 Male Yes Sat Dinner 5

Projection

Deuxième étape, celle-ci consiste tout simple à sélectionner certaines colonnes de la tables. En python, on utilise la fonction filter(). Il faut noter que celle-ci peut aussi fonctionner sur les lignes (quand celles-ci ont un index - pas vu ici).

In [106]:
tips.filter(["sex", "total_bill"])
Out[106]:
sex total_bill
0 Female 16.99
1 Male 10.34
2 Male 21.01
3 Male 23.68
4 Female 24.59
... ... ...
239 Male 29.03
240 Female 27.18
241 Male 22.67
242 Male 17.82
243 Female 18.78

244 rows × 2 columns

Quand on fait ce genre d'opérations, il est courant que nous nous retrouvions avec des lignes identiques. Pour supprimer les doublons, nous utilisons la fonction drop_duplicates() sur le résultat.

In [107]:
tips.filter(["sex", "smoker"]).drop_duplicates()
Out[107]:
sex smoker
0 Female No
1 Male No
56 Male Yes
67 Female Yes

Tri et limitation des résultats

Pour le tri, il existe la fonction sort_values(), dans laquelle on indique la ou les variables à utiliser pour le tri. Si on veut un tri descendant, on modifie la valeur du paramètre ascending (True par défaut).

In [108]:
tips.sort_values(by = "total_bill")
Out[108]:
total_bill tip sex smoker day time size
67 3.07 1.00 Female Yes Sat Dinner 1
92 5.75 1.00 Female Yes Fri Dinner 2
111 7.25 1.00 Female No Sat Dinner 1
172 7.25 5.15 Male Yes Sun Dinner 2
149 7.51 2.00 Male No Thur Lunch 2
... ... ... ... ... ... ... ...
182 45.35 3.50 Male Yes Sun Dinner 3
156 48.17 5.00 Male No Sun Dinner 6
59 48.27 6.73 Male No Sat Dinner 4
212 48.33 9.00 Male No Sat Dinner 4
170 50.81 10.00 Male Yes Sat Dinner 3

244 rows × 7 columns

In [109]:
tips.sort_values(by = "total_bill", ascending = False)
Out[109]:
total_bill tip sex smoker day time size
170 50.81 10.00 Male Yes Sat Dinner 3
212 48.33 9.00 Male No Sat Dinner 4
59 48.27 6.73 Male No Sat Dinner 4
156 48.17 5.00 Male No Sun Dinner 6
182 45.35 3.50 Male Yes Sun Dinner 3
... ... ... ... ... ... ... ...
149 7.51 2.00 Male No Thur Lunch 2
111 7.25 1.00 Female No Sat Dinner 1
172 7.25 5.15 Male Yes Sun Dinner 2
92 5.75 1.00 Female Yes Fri Dinner 2
67 3.07 1.00 Female Yes Sat Dinner 1

244 rows × 7 columns

Pour se limiter aux premières lignes (respectivement les dernières), on utilise la fonction head() (resp. tail()), qui affiche par défaut 5 lignes. Cette valeur est bien évidemment modifiable, comme vu ci-après.

In [110]:
tips.head(10) # 5 premières lignes par défaut
Out[110]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
5 25.29 4.71 Male No Sun Dinner 4
6 8.77 2.00 Male No Sun Dinner 2
7 26.88 3.12 Male No Sun Dinner 4
8 15.04 1.96 Male No Sun Dinner 2
9 14.78 3.23 Male No Sun Dinner 2
In [111]:
tips.tail(3) # 5 dernières lignes par défaut
Out[111]:
total_bill tip sex smoker day time size
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2
243 18.78 3.00 Female No Thur Dinner 2

Ajout de nouvelles variables

Il y a 2 possibilités ici :

  • à partir de valeurs déjà connues
  • à partir d'un calcul basé sur les autres variables

A partir de valeurs, soit vous en fournissez autant que de lignes, soit une seule qui sera donc dupliquée à toutes les lignes

In [112]:
tips['n_row'] = range(244)
tips['nouv'] = "nouvelle valeur"
tips.head()
Out[112]:
total_bill tip sex smoker day time size n_row nouv
0 16.99 1.01 Female No Sun Dinner 2 0 nouvelle valeur
1 10.34 1.66 Male No Sun Dinner 3 1 nouvelle valeur
2 21.01 3.50 Male No Sun Dinner 3 2 nouvelle valeur
3 23.68 3.31 Male No Sun Dinner 2 3 nouvelle valeur
4 24.59 3.61 Female No Sun Dinner 4 4 nouvelle valeur

Bien évidemment, on souhaite généralement faire un calcul à partir des autres variables. Ceci peut se faire avec la fonction assign().

In [113]:
# attention ici, l.size fait référence à la taille de l, car c'est un mot clé de python
tips.assign(per_person = lambda l: round(l.total_bill / l['size'], 2)) 
Out[113]:
total_bill tip sex smoker day time size n_row nouv per_person
0 16.99 1.01 Female No Sun Dinner 2 0 nouvelle valeur 8.49
1 10.34 1.66 Male No Sun Dinner 3 1 nouvelle valeur 3.45
2 21.01 3.50 Male No Sun Dinner 3 2 nouvelle valeur 7.00
3 23.68 3.31 Male No Sun Dinner 2 3 nouvelle valeur 11.84
4 24.59 3.61 Female No Sun Dinner 4 4 nouvelle valeur 6.15
... ... ... ... ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3 239 nouvelle valeur 9.68
240 27.18 2.00 Female Yes Sat Dinner 2 240 nouvelle valeur 13.59
241 22.67 2.00 Male Yes Sat Dinner 2 241 nouvelle valeur 11.34
242 17.82 1.75 Male No Sat Dinner 2 242 nouvelle valeur 8.91
243 18.78 3.00 Female No Thur Dinner 2 243 nouvelle valeur 9.39

244 rows × 10 columns

Agrégat

Le calcul d'un agrégat permet de calculer une statistique de base (dénombrement, somme, moyenne, minimum, maximum - rarement autre chose) sur un tableau de données. On peut soit calculer globalement, soit pour chaque modalité d'une variable (voire chaque couple de modalités de plusieurs variables).

Pour le faire globalement, on utilise la fonction aggregate() (ou agg()).

In [114]:
tips.filter(["total_bill", "tip", "size"]).aggregate(['count', "mean"])
Out[114]:
total_bill tip size
count 244.000000 244.000000 244.000000
mean 19.785943 2.998279 2.569672

Pour le faire pour chaque modalité d'une variable, on utilise la fonction groupby() en plus. Si on ne réalise qu'un seul calcul, on a directement les fonctions associées.

In [115]:
tips.filter(["sex", "total_bill", "tip", "size"]).groupby("sex").mean()
Out[115]:
total_bill tip size
sex
Female 18.056897 2.833448 2.459770
Male 20.744076 3.089618 2.630573

Si on a plusieurs variables dans le regroupement, le calcul se fait donc pour chaque couple de modalités de celles-ci.

In [116]:
tips.filter(["sex", "smoker", "total_bill", "tip", "size"]).groupby(["sex", "smoker"]).mean()
Out[116]:
total_bill tip size
sex smoker
Female No 18.105185 2.773519 2.592593
Yes 17.977879 2.931515 2.242424
Male No 19.791237 3.113402 2.711340
Yes 22.284500 3.051167 2.500000

Données Velib

Nous allons travailler sur des données Velib en temps réel, qui sont disponibles sur cette page.

Pour cela, nous avons besoin du module requests, tel qu'utilisé ci-dessous.

In [117]:
import requests

url = "https://opendata.paris.fr/api/records/1.0/search/"
par = dict(
    dataset = "velib-disponibilite-en-temps-reel"
)
r = requests.get(url = url, params = par)
r.json()
Out[117]:
{'nhits': 1393,
 'parameters': {'dataset': 'velib-disponibilite-en-temps-reel',
  'timezone': 'UTC',
  'rows': 10,
  'format': 'json'},
 'records': [{'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '091bd24dd13c5dffdef220b3dfdd1e353793a4fc',
   'fields': {'nbfreeedock': 34,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '16107',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-07-15',
    'densitylevel': '1',
    'nbedock': 35,
    'station': '{"code": "16107", "name": "Benjamin Godard - Victor Hugo", "state": "Operative", "type": "yes", "dueDate": 1531632033, "gps": {"latitude": 48.865983, "longitude": 2.275725}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Benjamin Godard - Victor Hugo',
    'nbdock': 0,
    'geo': [48.865983, 2.275725],
    'nbebike': 1,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point', 'coordinates': [2.275725, 48.865983]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'fc19c1f92f9d1773a6aac42bc58bd11a0b2d0ba7',
   'fields': {'nbfreeedock': 47,
    'station_state': 'Operative',
    'maxbikeoverflow': 55,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '6015',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-04-05',
    'densitylevel': '1',
    'nbedock': 52,
    'station': '{"code": "6015", "name": "André Mazet - Saint-André des Arts", "state": "Operative", "type": "yes", "dueDate": 1522965646, "gps": {"latitude": 48.85375581057431, "longitude": 2.3390958085656166}}',
    'nbfreedock': 3,
    'nbbike': 3,
    'station_name': 'André Mazet - Saint-André des Arts',
    'nbdock': 3,
    'geo': [48.85375581057431, 2.3390958085656166],
    'nbebike': 2,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3390958085656166, 48.85375581057431]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '74c886105e3c99d64eadc4853623a88e96c11ec0',
   'fields': {'nbfreeedock': 21,
    'station_state': 'Operative',
    'maxbikeoverflow': 21,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '9020',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-11-30',
    'densitylevel': '1',
    'nbedock': 21,
    'station': '{"code": "9020", "name": "Toudouze - Clauzel", "state": "Operative", "type": "yes", "dueDate": 1543615140, "gps": {"latitude": 48.87929591733507, "longitude": 2.3373600840568547}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Toudouze - Clauzel',
    'nbdock': 0,
    'geo': [48.87929591733507, 2.3373600840568547],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3373600840568547, 48.87929591733507]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'a910f176f6cb15354a06c2f3ab237d9d65fad2cb',
   'fields': {'nbfreeedock': 20,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '11104',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-10-22',
    'densitylevel': '1',
    'nbedock': 20,
    'station': '{"code": "11104", "name": "Charonne - Robert et Sonia Delauney", "state": "Operative", "type": "yes", "dueDate": 1540245629, "gps": {"latitude": 48.85590755596891, "longitude": 2.3925706744194035}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Charonne - Robert et Sonia Delauney',
    'nbdock': 0,
    'geo': [48.85590755596891, 2.3925706744194035],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3925706744194035, 48.85590755596891]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '56f13e9fae2b5eef38243bbd2a8d88eedfd36647',
   'fields': {'nbfreeedock': 30,
    'station_state': 'Operative',
    'maxbikeoverflow': 30,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '12109',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2019-03-28',
    'densitylevel': '1',
    'nbedock': 30,
    'station': '{"code": "12109", "name": "Mairie du 12ème", "state": "Operative", "type": "yes", "dueDate": 1553814028, "gps": {"latitude": 48.84085531176338, "longitude": 2.3875549435615544}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Mairie du 12ème',
    'nbdock': 0,
    'geo': [48.84085531176338, 2.3875549435615544],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3875549435615544, 48.84085531176338]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '6dbd9375e212ed90fa4fab01a12ef72a39ca889b',
   'fields': {'nbfreeedock': 2,
    'station_state': 'Operative',
    'maxbikeoverflow': 45,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '5001',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2020-03-22',
    'densitylevel': '1',
    'nbedock': 7,
    'station': '{"code": "5001", "name": "Harpe - Saint-Germain", "state": "Operative", "type": "yes", "dueDate": 1584914400, "gps": {"latitude": 48.85151881501689, "longitude": 2.343670316040516}}',
    'nbfreedock': 28,
    'nbbike': 6,
    'station_name': 'Harpe - Saint-Germain',
    'nbdock': 38,
    'geo': [48.85151881501689, 2.343670316040516],
    'nbebike': 2,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.343670316040516, 48.85151881501689]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '54a9cc781667b081ce9c336397b5b8ba6ffb9702',
   'fields': {'nbfreeedock': 55,
    'station_state': 'Operative',
    'maxbikeoverflow': 60,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '14014',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2019-04-28',
    'densitylevel': '1',
    'nbedock': 60,
    'station': '{"code": "14014", "name": "Jourdan - Stade Charléty", "state": "Operative", "type": "yes", "dueDate": 1556488800, "gps": {"latitude": 48.81942833336948, "longitude": 2.343335375189781}}',
    'nbfreedock': 0,
    'nbbike': 2,
    'station_name': 'Jourdan - Stade Charléty',
    'nbdock': 0,
    'geo': [48.81942833336948, 2.343335375189781],
    'nbebike': 3,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.343335375189781, 48.81942833336948]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'bcbb7cf8ad20eba15d2381d8a215bcf374791224',
   'fields': {'nbfreeedock': 0,
    'station_state': 'Operative',
    'maxbikeoverflow': 41,
    'creditcard': 'yes',
    'station_type': 'no',
    'overflowactivation': 'no',
    'station_code': '17026',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-02-28',
    'densitylevel': '1',
    'nbedock': 0,
    'station': '{"code": "17026", "name": "Jouffroy d\'Abbans - Wagram", "state": "Operative", "type": "no", "dueDate": 1519858800, "gps": {"latitude": 48.881973298351625, "longitude": 2.301132157444954}}',
    'nbfreedock': 34,
    'nbbike': 3,
    'station_name': "Jouffroy d'Abbans - Wagram",
    'nbdock': 40,
    'geo': [48.881973298351625, 2.301132157444954],
    'nbebike': 3,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.301132157444954, 48.881973298351625]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'f296ab6f6f863fca099570ac3c29094c003ab081',
   'fields': {'nbfreeedock': 32,
    'station_state': 'Operative',
    'maxbikeoverflow': 39,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '17041',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-03-30',
    'densitylevel': '1',
    'nbedock': 38,
    'station': '{"code": "17041", "name": "Guersant - Gouvion-Saint-Cyr", "state": "Operative", "type": "yes", "dueDate": 1522447200, "gps": {"latitude": 48.88287775178599, "longitude": 2.287667370814871}}',
    'nbfreedock': 0,
    'nbbike': 2,
    'station_name': 'Guersant - Gouvion-Saint-Cyr',
    'nbdock': 1,
    'geo': [48.88287775178599, 2.287667370814871],
    'nbebike': 5,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.287667370814871, 48.88287775178599]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '15972a585cf351ca2736971ae5b0b88bd4604b96',
   'fields': {'nbfreeedock': 53,
    'station_state': 'Operative',
    'maxbikeoverflow': 60,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '10013',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-12-30',
    'densitylevel': '1',
    'nbedock': 59,
    'station': '{"code": "10013", "name": "Alibert - Jemmapes", "state": "Operative", "type": "yes", "dueDate": 1546210841, "gps": {"latitude": 48.8710440519842, "longitude": 2.366104461987773}}',
    'nbfreedock': 0,
    'nbbike': 1,
    'station_name': 'Alibert - Jemmapes',
    'nbdock': 1,
    'geo': [48.8710440519842, 2.366104461987773],
    'nbebike': 6,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.366104461987773, 48.8710440519842]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'}]}

Dans ce résultat, nous voyons qu'il y a 1393 enregistrements à récupérer (nhits), et que nous en avons récupérer ici uniquement 10 (parameters.rows). Pour avoir les 10 suivants, on doit faire comme ci-dessous

In [118]:
par = dict(
    dataset = "velib-disponibilite-en-temps-reel",
    start = 11
)
rsuite = requests.get(url = url, params = par)
rsuite.json()
Out[118]:
{'nhits': 1393,
 'parameters': {'dataset': 'velib-disponibilite-en-temps-reel',
  'timezone': 'UTC',
  'rows': 10,
  'start': 11,
  'format': 'json'},
 'records': [{'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'e890cd6e64fd5a1010f3d8b5b9e6ef57fd82d204',
   'fields': {'nbfreeedock': 23,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '14111',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2019-07-28',
    'densitylevel': '1',
    'nbedock': 25,
    'station': '{"code": "14111", "name": "Cassini - Denfert-Rochereau", "state": "Operative", "type": "yes", "dueDate": 1564347600, "gps": {"latitude": 48.83752583906732, "longitude": 2.336035408079624}}',
    'nbfreedock': 0,
    'nbbike': 2,
    'station_name': 'Cassini - Denfert-Rochereau',
    'nbdock': 0,
    'geo': [48.83752583906732, 2.336035408079624],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.336035408079624, 48.83752583906732]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'd3397a0698823e9ae348ac569cd2c12506de0863',
   'fields': {'nbfreeedock': 13,
    'station_state': 'Operative',
    'maxbikeoverflow': 21,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '6003',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2019-01-20',
    'densitylevel': '1',
    'nbedock': 21,
    'station': '{"code": "6003", "name": "Saint-Sulpice", "state": "Operative", "type": "yes", "dueDate": 1547999940, "gps": {"latitude": 48.85165383178419, "longitude": 2.3308077827095985}}',
    'nbfreedock': 0,
    'nbbike': 4,
    'station_name': 'Saint-Sulpice',
    'nbdock': 0,
    'geo': [48.85165383178419, 2.3308077827095985],
    'nbebike': 4,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3308077827095985, 48.85165383178419]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '41c8ae0bad5d6eaf8fea3566fd948971c5e76b09',
   'fields': {'nbfreeedock': 46,
    'station_state': 'Operative',
    'maxbikeoverflow': 49,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '13007',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2020-01-20',
    'densitylevel': '1',
    'nbedock': 48,
    'station': '{"code": "13007", "name": "Le Brun - Gobelins", "state": "Operative", "type": "yes", "dueDate": 1579561200, "gps": {"latitude": 48.835092787823875, "longitude": 2.353468135133752}}',
    'nbfreedock': 0,
    'nbbike': 1,
    'station_name': 'Le Brun - Gobelins',
    'nbdock': 0,
    'geo': [48.835092787823875, 2.353468135133752],
    'nbebike': 1,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.353468135133752, 48.835092787823875]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '405fb26903e20ee55b19a5c61962e19c0d74bfee',
   'fields': {'nbfreeedock': 21,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '5110',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-06-22',
    'densitylevel': '1',
    'nbedock': 23,
    'station': '{"code": "5110", "name": "Lacépède - Monge", "state": "Operative", "type": "yes", "dueDate": 1529654488, "gps": {"latitude": 48.84389286531899, "longitude": 2.3519663885235786}}',
    'nbfreedock': 0,
    'nbbike': 1,
    'station_name': 'Lacépède - Monge',
    'nbdock': 0,
    'geo': [48.84389286531899, 2.3519663885235786],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3519663885235786, 48.84389286531899]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '79de8080fec8fc9fbb4487bec5295943e7f6efe7',
   'fields': {'nbfreeedock': 16,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '6108',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-06-22',
    'densitylevel': '1',
    'nbedock': 17,
    'station': '{"code": "6108", "name": "Saint-Romain - Cherche-Midi", "state": "Operative", "type": "yes", "dueDate": 1529659445, "gps": {"latitude": 48.84708159081946, "longitude": 2.321374788880348}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Saint-Romain - Cherche-Midi',
    'nbdock': 0,
    'geo': [48.84708159081946, 2.321374788880348],
    'nbebike': 1,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.321374788880348, 48.84708159081946]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '7daa349a8a2731c4f8a2df9ea011a478ca2e178b',
   'fields': {'nbfreeedock': 29,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '33006',
    'overflow': 'no',
    'nbbikeoverflow': 1,
    'duedate': '2019-01-01',
    'densitylevel': '1',
    'nbedock': 31,
    'station': '{"code": "33006", "name": "André Karman - République", "state": "Operative", "type": "yes", "dueDate": 1546361972, "gps": {"latitude": 48.91039875761846, "longitude": 2.3851355910301213}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'André Karman - République',
    'nbdock': 0,
    'geo': [48.91039875761846, 2.3851355910301213],
    'nbebike': 2,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.3851355910301213, 48.91039875761846]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'c592b2a3691f63842837c3fa4a964ffc9af98efe',
   'fields': {'nbfreeedock': 23,
    'station_state': 'Operative',
    'maxbikeoverflow': 27,
    'creditcard': 'no',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '42016',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-04-02',
    'densitylevel': '1',
    'nbedock': 27,
    'station': '{"code": "42016", "name": "Pierre et Marie Curie - Maurice Thorez", "state": "Operative", "type": "yes", "dueDate": 1522706410, "gps": {"latitude": 48.81580226360801, "longitude": 2.376804985105991}}',
    'nbfreedock': 0,
    'nbbike': 1,
    'station_name': 'Pierre et Marie Curie - Maurice Thorez',
    'nbdock': 0,
    'geo': [48.81580226360801, 2.376804985105991],
    'nbebike': 3,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.376804985105991, 48.81580226360801]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '184a94d35d137c45a9a6788e0eb5ef8a23ef89e0',
   'fields': {'nbfreeedock': 45,
    'station_state': 'Operative',
    'maxbikeoverflow': 0,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '41301',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-06-18',
    'densitylevel': '0',
    'nbedock': 50,
    'station': '{"code": "41301", "name": "Bois de Vincennes - Gare", "state": "Operative", "type": "yes", "dueDate": 1529359231, "gps": {"latitude": 48.836022242886884, "longitude": 2.4708339950830287}}',
    'nbfreedock': 0,
    'nbbike': 3,
    'station_name': 'Bois de Vincennes - Gare',
    'nbdock': 0,
    'geo': [48.836022242886884, 2.4708339950830287],
    'nbebike': 2,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.4708339950830287, 48.836022242886884]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': 'e6b97d8acd72ba9e1fd12301be6dd056d84ce0a2',
   'fields': {'nbfreeedock': 25,
    'station_state': 'Close',
    'maxbikeoverflow': 0,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '10105',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2020-04-14',
    'densitylevel': '1',
    'nbedock': 25,
    'station': '{"code": "10105", "name": "Mazagran - Bonne Nouvelle", "state": "Close", "type": "yes", "dueDate": 1586901600, "gps": {"latitude": 48.870278, "longitude": 2.351281}}',
    'nbfreedock': 0,
    'nbbike': 0,
    'station_name': 'Mazagran - Bonne Nouvelle',
    'nbdock': 0,
    'geo': [48.870278, 2.351281],
    'nbebike': 0,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point', 'coordinates': [2.351281, 48.870278]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'},
  {'datasetid': 'velib-disponibilite-en-temps-reel',
   'recordid': '6b67a25a45d7766d33c49bff3193ecac1f210819',
   'fields': {'nbfreeedock': 16,
    'station_state': 'Operative',
    'maxbikeoverflow': 25,
    'creditcard': 'yes',
    'station_type': 'yes',
    'overflowactivation': 'no',
    'station_code': '21010',
    'overflow': 'no',
    'nbbikeoverflow': 0,
    'duedate': '2018-12-30',
    'densitylevel': '1',
    'nbedock': 25,
    'station': '{"code": "21010", "name": "Silly - Galliéni", "state": "Operative", "type": "yes", "dueDate": 1546210829, "gps": {"latitude": 48.835583838705936, "longitude": 2.2325500845909123}}',
    'nbfreedock': 0,
    'nbbike': 3,
    'station_name': 'Silly - Galliéni',
    'nbdock': 0,
    'geo': [48.835583838705936, 2.2325500845909123],
    'nbebike': 6,
    'kioskstate': 'yes',
    'nbebikeoverflow': 0},
   'geometry': {'type': 'Point',
    'coordinates': [2.2325500845909123, 48.835583838705936]},
   'record_timestamp': '2020-01-21T09:28:00.600000+00:00'}]}

Ce qui nous intéresse ici est le champ records, qui contient les enregistrements de 10 premières stations donc. En le transformant en data frame avec pandas, on voit que le tableau obtenu n'est pas très lisible.

In [119]:
records = r.json()['records']
df = pandas.DataFrame(records)

df
Out[119]:
datasetid recordid fields geometry record_timestamp
0 velib-disponibilite-en-temps-reel 091bd24dd13c5dffdef220b3dfdd1e353793a4fc {'nbfreeedock': 34, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.275725, 48... 2020-01-21T09:28:00.600000+00:00
1 velib-disponibilite-en-temps-reel fc19c1f92f9d1773a6aac42bc58bd11a0b2d0ba7 {'nbfreeedock': 47, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3390958085... 2020-01-21T09:28:00.600000+00:00
2 velib-disponibilite-en-temps-reel 74c886105e3c99d64eadc4853623a88e96c11ec0 {'nbfreeedock': 21, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3373600840... 2020-01-21T09:28:00.600000+00:00
3 velib-disponibilite-en-temps-reel a910f176f6cb15354a06c2f3ab237d9d65fad2cb {'nbfreeedock': 20, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3925706744... 2020-01-21T09:28:00.600000+00:00
4 velib-disponibilite-en-temps-reel 56f13e9fae2b5eef38243bbd2a8d88eedfd36647 {'nbfreeedock': 30, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3875549435... 2020-01-21T09:28:00.600000+00:00
5 velib-disponibilite-en-temps-reel 6dbd9375e212ed90fa4fab01a12ef72a39ca889b {'nbfreeedock': 2, 'station_state': 'Operative... {'type': 'Point', 'coordinates': [2.3436703160... 2020-01-21T09:28:00.600000+00:00
6 velib-disponibilite-en-temps-reel 54a9cc781667b081ce9c336397b5b8ba6ffb9702 {'nbfreeedock': 55, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3433353751... 2020-01-21T09:28:00.600000+00:00
7 velib-disponibilite-en-temps-reel bcbb7cf8ad20eba15d2381d8a215bcf374791224 {'nbfreeedock': 0, 'station_state': 'Operative... {'type': 'Point', 'coordinates': [2.3011321574... 2020-01-21T09:28:00.600000+00:00
8 velib-disponibilite-en-temps-reel f296ab6f6f863fca099570ac3c29094c003ab081 {'nbfreeedock': 32, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.2876673708... 2020-01-21T09:28:00.600000+00:00
9 velib-disponibilite-en-temps-reel 15972a585cf351ca2736971ae5b0b88bd4604b96 {'nbfreeedock': 53, 'station_state': 'Operativ... {'type': 'Point', 'coordinates': [2.3661044619... 2020-01-21T09:28:00.600000+00:00

On souhaite finalement se restreindre qu'aux informations contenues dans le champs fields. Pour cela, on utilise le mécanisme de dict comprehension.

In [120]:
pandas.DataFrame([s['fields'] for s in records])
Out[120]:
nbfreeedock station_state maxbikeoverflow creditcard station_type overflowactivation station_code overflow nbbikeoverflow duedate ... nbedock station nbfreedock nbbike station_name nbdock geo nbebike kioskstate nbebikeoverflow
0 34 Operative 0 no yes no 16107 no 0 2018-07-15 ... 35 {"code": "16107", "name": "Benjamin Godard - V... 0 0 Benjamin Godard - Victor Hugo 0 [48.865983, 2.275725] 1 yes 0
1 47 Operative 55 yes yes no 6015 no 0 2018-04-05 ... 52 {"code": "6015", "name": "André Mazet - Saint-... 3 3 André Mazet - Saint-André des Arts 3 [48.85375581057431, 2.3390958085656166] 2 yes 0
2 21 Operative 21 yes yes no 9020 no 0 2018-11-30 ... 21 {"code": "9020", "name": "Toudouze - Clauzel",... 0 0 Toudouze - Clauzel 0 [48.87929591733507, 2.3373600840568547] 0 yes 0
3 20 Operative 0 no yes no 11104 no 0 2018-10-22 ... 20 {"code": "11104", "name": "Charonne - Robert e... 0 0 Charonne - Robert et Sonia Delauney 0 [48.85590755596891, 2.3925706744194035] 0 yes 0
4 30 Operative 30 no yes no 12109 no 0 2019-03-28 ... 30 {"code": "12109", "name": "Mairie du 12ème", "... 0 0 Mairie du 12ème 0 [48.84085531176338, 2.3875549435615544] 0 yes 0
5 2 Operative 45 yes yes no 5001 no 0 2020-03-22 ... 7 {"code": "5001", "name": "Harpe - Saint-Germai... 28 6 Harpe - Saint-Germain 38 [48.85151881501689, 2.343670316040516] 2 yes 0
6 55 Operative 60 yes yes no 14014 no 0 2019-04-28 ... 60 {"code": "14014", "name": "Jourdan - Stade Cha... 0 2 Jourdan - Stade Charléty 0 [48.81942833336948, 2.343335375189781] 3 yes 0
7 0 Operative 41 yes no no 17026 no 0 2018-02-28 ... 0 {"code": "17026", "name": "Jouffroy d'Abbans -... 34 3 Jouffroy d'Abbans - Wagram 40 [48.881973298351625, 2.301132157444954] 3 yes 0
8 32 Operative 39 yes yes no 17041 no 0 2018-03-30 ... 38 {"code": "17041", "name": "Guersant - Gouvion-... 0 2 Guersant - Gouvion-Saint-Cyr 1 [48.88287775178599, 2.287667370814871] 5 yes 0
9 53 Operative 60 yes yes no 10013 no 0 2018-12-30 ... 59 {"code": "10013", "name": "Alibert - Jemmapes"... 0 1 Alibert - Jemmapes 1 [48.8710440519842, 2.366104461987773] 6 yes 0

10 rows × 21 columns

Ici, la colonne station ne nous est pas utile (les informations dedans sont déjà présentes). Toujours le même mécanisme de list comprehension, on peut la supprimer du résultat.

In [121]:
pandas.DataFrame([{i:s['fields'][i] for i in s['fields'] if i!='station'} for s in records])
Out[121]:
nbfreeedock station_state maxbikeoverflow creditcard station_type overflowactivation station_code overflow nbbikeoverflow duedate densitylevel nbedock nbfreedock nbbike station_name nbdock geo nbebike kioskstate nbebikeoverflow
0 34 Operative 0 no yes no 16107 no 0 2018-07-15 1 35 0 0 Benjamin Godard - Victor Hugo 0 [48.865983, 2.275725] 1 yes 0
1 47 Operative 55 yes yes no 6015 no 0 2018-04-05 1 52 3 3 André Mazet - Saint-André des Arts 3 [48.85375581057431, 2.3390958085656166] 2 yes 0
2 21 Operative 21 yes yes no 9020 no 0 2018-11-30 1 21 0 0 Toudouze - Clauzel 0 [48.87929591733507, 2.3373600840568547] 0 yes 0
3 20 Operative 0 no yes no 11104 no 0 2018-10-22 1 20 0 0 Charonne - Robert et Sonia Delauney 0 [48.85590755596891, 2.3925706744194035] 0 yes 0
4 30 Operative 30 no yes no 12109 no 0 2019-03-28 1 30 0 0 Mairie du 12ème 0 [48.84085531176338, 2.3875549435615544] 0 yes 0
5 2 Operative 45 yes yes no 5001 no 0 2020-03-22 1 7 28 6 Harpe - Saint-Germain 38 [48.85151881501689, 2.343670316040516] 2 yes 0
6 55 Operative 60 yes yes no 14014 no 0 2019-04-28 1 60 0 2 Jourdan - Stade Charléty 0 [48.81942833336948, 2.343335375189781] 3 yes 0
7 0 Operative 41 yes no no 17026 no 0 2018-02-28 1 0 34 3 Jouffroy d'Abbans - Wagram 40 [48.881973298351625, 2.301132157444954] 3 yes 0
8 32 Operative 39 yes yes no 17041 no 0 2018-03-30 1 38 0 2 Guersant - Gouvion-Saint-Cyr 1 [48.88287775178599, 2.287667370814871] 5 yes 0
9 53 Operative 60 yes yes no 10013 no 0 2018-12-30 1 59 0 1 Alibert - Jemmapes 1 [48.8710440519842, 2.366104461987773] 6 yes 0

Carte avec leaflet

Dans ce TP, nous allons aussi aborder l'aspect cartographie sous python avec le package folium (à installer donc). L'idée sera d'ajouter à un fond de carte des formes de couleurs, en fonction d'une information tierce. Le package folium est une interface entre python et la librairie leaflet. Elle s'utilise très facilement, comme nous pouvons le voir ci-dessous pour la création d'une carte. Les coordonnées indiquées sont la latitude et la longitude de Paris.

In [122]:
import folium
centre = [48.87, 2.35]
paris = folium.Map(location = centre, zoom_start = 12)
paris
Out[122]:

Avec des marqueurs

En reprenant le data frame vu juste avant (et en le stockant dans une variable), on peut créer une carte avec des marqueurs, qui affichent le nom de la station lorsqu'on clique dessus.

In [123]:
df = pandas.DataFrame([{i:s['fields'][i] for i in s['fields'] if i!='station'} for s in records])
paris = folium.Map(location = centre, zoom_start = 12)
for station in [s['fields'] for s in records]:
    folium.Marker(station["geo"], 
                        popup = station["station_name"]).add_to(paris)
paris
Out[123]:

A faire

  • Ecrire le code permettant d'importer les informations de toutes les stations
  • Stocker celles-ci dans un data frame pandas
  • Afficher les stations sur une carte
  • Bonus : afficher des cercles pour chaque station, avec une couleur en fonction du taux de remplissage
In [ ]: