= "01450"
city_code = 100
size = "https://koumoul.com/data-fair/api/v1/datasets/dpe-france/lines"
api_root = (
url_api f"{api_root}?format=json&q_mode=simple&qs=code_insee_commune_actualise"
+ "%3A%22"
+ f"{city_code}"
+ "%22"
+ f"&size={size}&select="
+ "%2A&sampling=neighbors"
)
1 Introduction : Qu’est-ce qu’une API ?
1.1 Définition
Pour expliquer le principe d’une API, nous allons reprendre le début de
la fiche dédiée aux API dans la documentation collaborative sur le logiciel R
(un logiciel de traitement statistique proche de Python
)
utilitR que je recommande de lire :
Une Application Programming Interface (ou API) est une interface de programmation qui permet d’utiliser une application existante pour restituer des données. Le terme d’API peut être paraître intimidant, mais il s’agit simplement d’une façon de mettre à disposition des données : plutôt que de laisser l’utilisateur consulter directement des bases de données (souvent volumineuses et complexes), l’API lui propose de formuler une requête qui est traitée par le serveur hébergeant la base de données, puis de recevoir des données en réponse à sa requête.
D’un point de vue informatique, une API est une porte d’entrée clairement identifiée par laquelle un logiciel offre des services à d’autres logiciels (ou utilisateurs). L’objectif d’une API est de fournir un point d’accès à une fonctionnalité qui soit facile à utiliser et qui masque les détails de la mise en oeuvre. Par exemple, l’API Sirene permet de récupérer la raison sociale d’une entreprise à partir de son identifiant Siren en interrogeant le référentiel disponible sur Internet directement depuis un script R, sans avoir à connaître tous les détails du répertoire Sirene.
À l’Insee comme ailleurs, la connexion entre les bases de données pour les nouveaux projets tend à se réaliser par des API. L’accès à des données par des API devient ainsi de plus en plus commun et est amené à devenir une compétence de base de tout utilisateur de données.
1.2 Avantages des API
A nouveau, citons la documentation utilitR :
Les API présentent de multiples avantages :
- Les API rendent les programmes plus reproductibles. En effet, grâce aux API, il est possible de mettre à jour facilement les données utilisées par un programme si celles-ci évoluent. Cette flexibilité accrue pour l’utilisateur évite au producteur de données d’avoir à réaliser de multiples extractions, et réduit le problème de la coexistence de versions différentes des données.
- Grâce aux API, l’utilisateur peut extraire facilement une petite partie d’une base de données plus conséquente.
- Les API permettent de mettre à disposition des données tout en limitant le nombre de personnes ayant accès aux bases de données elles-mêmes.
- Grâce aux API, il est possible de proposer des services sur mesure pour les utilisateurs (par exemple, un accès spécifique pour les gros utilisateurs).
L’utilisation accrue d’API dans le cadre de stratégies open-data est l’un des piliers des 15 feuilles de route ministérielles en matière d’ouverture, de circulation et de valorisation des données publiques.
1.3 Utilisation des API
Citons encore une fois
la documentation utilitR
:
Une API peut souvent être utilisée de deux façons : par une interface Web, et par l’intermédiaire d’un logiciel (R, Python…). Par ailleurs, les API peuvent être proposées avec un niveau de liberté variable pour l’utilisateur :
- soit en libre accès (l’utilisation n’est pas contrôlée et l’utilisateur peut utiliser le service comme bon lui semble) ;
- soit via la génération d’un compte et d’un jeton d’accès qui permettent de sécuriser l’utilisation de l’API et de limiter le nombre de requêtes.
De nombreuses API nécessitent une authentification, c’est-à-dire un
compte utilisateur afin de pouvoir accéder aux données.
Dans un premier temps,
nous regarderons exclusivement les API ouvertes sans restriction d’accès.
Certains exercices et exemples permettront néanmoins d’essayer des API
avec restrictions d’accès.
2 Requêter une API
2.1 Principe général
L’utilisation de l’interface Web est utile dans une démarche exploratoire mais trouve rapidement ses limites, notamment lorsqu’on consulte régulièrement l’API. L’utilisateur va rapidement se rendre compte qu’il est beaucoup plus commode d’utiliser une API via un logiciel de traitement pour automatiser la consultation ou pour réaliser du téléchargement de masse. De plus, l’interface Web n’existe pas systématiquement pour toutes les API.
Le mode principal de consultation d’une API consiste à adresser une requête à cette API via un logiciel adapté (R, Python, Java…). Comme pour l’utilisation d’une fonction, l’appel d’une API comprend des paramètres qui sont détaillées dans la documentation de l’API.
Voici les éléments importants à avoir en tête sur les requêtes (j’emprunte encore
à utilitR
) :
- Le point d’entrée d’un service offert par une API se présente sous la forme d’une URL (adresse web).
Chaque service proposé par une API a sa propre URL. Par exemple, dans le cas de l’OpenFood Facts,
l’URL à utiliser pour obtenir des informations sur un produit particulier (l’identifiant
737628064502
) est https://world.openfoodfacts.org/api/v0/product/737628064502.json - Cette URL doit être complétée avec différents paramètres qui précisent la requête (par exemple l’identifiant Siren). Ces paramètres viennent s’ajouter à l’URL, souvent à la suite de
?
. Chaque service proposé par une API a ses propres paramètres, détaillés dans la documentation. - Lorsque l’utilisateur soumet sa requête, l’API lui renvoie une réponse structurée contenant l’ensemble des informations demandées. Le résultat envoyé par une API est majoritairement aux formats
JSON
ouXML
(deux formats dans lesquels les informations sont hiérarchisées de manière emboitée). Plus rarement, certains services proposent une information sous forme plate (de type csv).
Du fait de la dimension hiérarchique des formats JSON
ou XML
,
le résultat n’est pas toujours facile à récupérer mais
Python
propose d’excellents outils pour cela (meilleurs que ceux de R
).
Certains packages, notamment json
, facilitent l’extraction de champs d’une sortie d’API.
Dans certains cas, des packages spécifiques à une API ont été créés pour simplifier l’écriture d’une requête ou la récupération du résultat. Par exemple, le package
pynsee
propose des options qui seront retranscrites automatiquement dans l’URL de
requête pour faciliter le travail sur les données Insee.
2.2 Illustration avec une API de l’Ademe pour obtenir des diagnostics énergétiques
Le diagnostic de performance énergétique (DPE) renseigne sur la performance énergétique d’un logement ou d’un bâtiment, en évaluant sa consommation d’énergie et son impact en termes d’émissions de gaz à effet de serre.
Les données des performances énergétiques des bâtiments sont mises à disposition par l’Ademe. Comme ces données sont relativement volumineuses, une API peut être utile lorsqu’on ne s’intéresse qu’à un sous-champ des données. Une documentation et un espace de test de l’API sont disponibles sur le site API GOUV1.
Supposons qu’on désire récupérer une centaine de valeurs pour la commune de Villieu-Loyes-Mollon dans l’Ain (code Insee 01450).
L’API comporte plusieurs points d’entrée. Globalement, la racine commune est :
https://koumoul.com/data-fair/api/v1/datasets/dpe-france
Ensuite, en fonction de l’API désirée, on va ajouter des éléments
à cette racine. En l’occurrence, on va utiliser
l’API field
qui permet de récupérer des lignes en fonction d’un
ou plusieurs critères (pour nous, la localisation géographique):
L’exemple donné dans la documentation technique est
GET https://koumoul.com/data-fair/api/v1/datasets/dpe-france/values/{field}
ce qui en Python
se traduira par l’utilisation de la méthode get
du
package Request
sur un url dont la structure est la suivante :
- il commencera par
https://koumoul.com/data-fair/api/v1/datasets/dpe-france/values/
; - il sera ensuite suivi par des paramètres de recherche. Le champ
{field}
commence ainsi généralement par un?
qui permet ensuite de spécifier des paramètres sous la formenom_parameter=value
A la lecture de la documentation, les premiers paramètres qu’on désire :
- Le nombre de pages, ce qui nous permet d’obtenir un certain nombre d’échos. On va seulement récupérer 10 pages ce qui correspond à une centaine d’échos. On va néanmoins préciser qu’on veut 100 échos
- Le format de sortie. On va privilégier le
JSON
qui est un format standard dans le monde des API.Python
offre beaucoup de flexibilité grâce à l’un de ses objets de base, à savoir le dictionnaire (typedict
), pour manipuler de tels fichiers - Le code commune des données qu’on désire obtenir. Comme on l’a évoqué,
on va récupérer les données dont le code commune est
01450
. D’après la doc, il convient de passer le code commune sous le format:code_insee_commune_actualise:{code_commune}
. Pour éviter tout risque de mauvais formatage, on va utiliser%3A
pour signifier:
,%2A
pour signifier*
et%22
pour signifier"
. - D’autres paramètres annexes, suggérés par la documentation
Cela nous donne ainsi un URL dont la structure est la suivante :
Si vous introduisez cet URL dans votre navigateur, vous devriez aboutir
sur un JSON
non formaté2. En Python
,
on peut utiliser requests
pour récupérer les données3 :
import requests
import pandas as pd
= requests.get(url_api)
req = req.json() wb
Prenons par exemple les 1000 premiers caractères du résultat, pour se donner une idée du résultat et se convaincre que notre filtre au niveau communal est bien passé :
print(req.content[:1000])
b’{“total”: 121,“next”: “https://koumoul.com/data-fair/api/v1/datasets/dpe-france/lines?format=json&q_mode=simple&qs=code_insee_commune_actualise%3A%2201450%22&size=100&select=*&sampling=neighbors&after=102719%2C912454”,“results”: [\n {“classe_consommation_energie”: “E”,“tr001_modele_dpe_type_libelle”: “Vente”,“annee_construction”: 1,“_geopoint”: “45.927488,5.230195”,“latitude”: 45.927488,“surface_thermique_lot”: 106.87,“_i”: 2,“tr002_type_batiment_description”: “Maison Individuelle”,“geo_adresse”: “Rue du Chateau 01800 Villieu-Loyes-Mollon”,“_rand”: 959550,“code_insee_commune_actualise”: “01450”,“estimation_ges”: 9,“geo_score”: 0.58,“classe_estimation_ges”: “B”,“nom_methode_dpe”: “M9thode Facture”,“tv016_departement_code”: “01”,“consommation_energie”: 286,“date_etablissement_dpe”: “2013-04-15”,“longitude”: 5.230195,“_score”: null,’
Ici, il n’est même pas nécessaire en première approche
d’utiliser le package json
, l’information
étant déjà tabulée dans l’écho renvoyé (on a la même information pour tous les pays):
On peut donc se contenter de Pandas
pour transformer nos données en
DataFrame
et Geopandas
pour convertir en données
géographiques :
import pandas as pd
import geopandas as gpd
def get_dpe_from_url(url):
= requests.get(url)
req = req.json()
wb = pd.json_normalize(wb["results"])
df
= gpd.GeoDataFrame(
dpe =gpd.points_from_xy(df.longitude, df.latitude), crs=4326
df, geometry
)= dpe.dropna(subset=["longitude", "latitude"])
dpe
return dpe
= get_dpe_from_url(url_api)
dpe 2) dpe.head(
classe_consommation_energie | tr001_modele_dpe_type_libelle | annee_construction | _geopoint | latitude | surface_thermique_lot | _i | tr002_type_batiment_description | geo_adresse | _rand | ... | classe_estimation_ges | nom_methode_dpe | tv016_departement_code | consommation_energie | date_etablissement_dpe | longitude | _score | _id | version_methode_dpe | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | E | Vente | 1 | 45.927488,5.230195 | 45.927488 | 106.87 | 2 | Maison Individuelle | Rue du Chateau 01800 Villieu-Loyes-Mollon | 959550 | ... | B | Méthode Facture | 01 | 286.0 | 2013-04-15 | 5.230195 | None | HJt4TdUa1W0wZiNoQkskk | NaN | POINT (5.2302 45.92749) |
1 | G | Vente | 1960 | 45.931376,5.230461 | 45.931376 | 70.78 | 9 | Maison Individuelle | 552 Rue Royale 01800 Villieu-Loyes-Mollon | 681070 | ... | D | Méthode 3CL | 01 | 507.0 | 2013-04-22 | 5.230461 | None | UhMxzza1hsUo0syBh9DxH | 3CL-DPE, version 1.3 | POINT (5.23046 45.93138) |
2 rows × 23 columns
Essayons de représenter sur une carte ces DPE avec les années de construction des logements. Avec Folium
, on obtient la carte interactive suivante :
import seaborn as sns
import folium
= sns.color_palette("coolwarm", 8)
palette
def interactive_map_dpe(dpe):
# convert to number
"color"] = [
dpe[ord(dpe.iloc[i]["classe_consommation_energie"].lower()) - 96
for i in range(len(dpe))
]= dpe.loc[dpe["color"] <= 7]
dpe "color"] = [palette.as_hex()[x] for x in dpe["color"]]
dpe[
= dpe[["latitude", "longitude"]].mean().values.tolist()
center = dpe[["latitude", "longitude"]].min().values.tolist()
sw = dpe[["latitude", "longitude"]].max().values.tolist()
ne
= folium.Map(location=center, tiles="OpenStreetMap")
m
# I can add markers one by one on the map
for i in range(0, len(dpe)):
folium.Marker("latitude"], dpe.iloc[i]["longitude"]],
[dpe.iloc[i][=f"Year of construction: {dpe.iloc[i]['annee_construction']}, <br>DPE: {dpe.iloc[i]['classe_consommation_energie']}",
popup=folium.Icon(
icon="black", icon="home", icon_color=dpe.iloc[i]["color"]
color
),
).add_to(m)
m.fit_bounds([sw, ne])
return m
= interactive_map_dpe(dpe) m
/opt/conda/lib/python3.12/site-packages/geopandas/geodataframe.py:1819: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
# Display the map
m
2.3 Un catalogue incomplet d’API existantes
De plus en plus de sites mettent des API à disposition des développeurs et autres curieux.
Pour en citer quelques-unes très connues :
Twitter
: https://dev.twitter.com/rest/publicFacebook
: https://developers.facebook.com/Instagram
: https://www.instagram.com/developer/Spotify
: https://developer.spotify.com/web-api/
Cependant, il est intéressant de ne pas se restreindre à celles-ci dont les données ne sont pas toujours les plus intéressantes. Beaucoup de producteurs de données, privés comme publics, mettent à disposition leurs données sous forme d’API.
- API gouv : beaucoup d’API officielles de l’Etat français et accès à de la documentation
- Insee : https://api.insee.fr/catalogue/ et
pynsee
- Pôle Emploi : https://www.emploi-store-dev.fr/portail-developpeur-cms/home.html
- SNCF : https://data.sncf.com/api
- Banque Mondiale : https://datahelpdesk.worldbank.org/knowledgebase/topics/125589
3 L’API DVF : accéder à des données de transactions immobilières simplement
Cette partie nécessite une mise à jour pour privilégier l’API DVF du Cerema
Le site DVF
(demandes de valeurs foncières) permet de visualiser toutes les données relatives aux mutations à titre onéreux (ventes de maisons, appartements, garages…) réalisées durant les 5 dernières années.
Un site de visualisation est disponible sur https://app.dvf.etalab.gouv.fr/.
Ce site est très complet quand il s’agit de connaître le prix moyen au mètre carré d’un quartier ou de comparer des régions entre elles. L’API DVF permet d’aller plus loin afin de récupérer les résultats dans un logiciel de traitement de données. Elle a été réalisée par Christian Quest et le code source est disponible sur Github .
Les critères de recherche sont les suivants :
- code_commune
= code INSEE de la commune (ex: 94068)
- section
= section cadastrale (ex: 94068000CQ)
- numero_plan
= identifiant de la parcelle, (ex: 94068000CQ0110)
- lat
+ lon
+ dist
(optionnel): pour une recherche géographique, dist est par défaut un rayon de 500m
- code_postal
Les filtres de sélection complémentaires :
- nature_mutation
(Vente, etc)
- type_local
(Maison, Appartement, Local, Dépendance)
Les requêtes sont de la forme : http://api.cquest.org/dvf?code_commune=29168
.
- Rechercher toutes les transactions existantes dans DVF à Plogoff (code commune
29168
, en Bretagne). Afficher les clés du JSON et en déduire le nombre de transactions répertoriées. - N’afficher que les transactions portant sur des maisons.
- Utiliser l’API geo pour récupérer le découpage communal de la ville de Plogoff.
- Représenter l’histogramme des prix de vente.
N’hésitez pas à aller plus loin en jouant sur des variables de groupes par exemple.
Le résultat de la question 2 devrait ressembler au DataFrame
suivant :
L’histogramme des prix de vente (question 4) aura l’aspect suivant :
On va faire une carte des ventes en affichant le prix de l’achat. La cartographie réactive sera présentée dans les chapitres consacrés à la visualisation de données.
Supposons que le DataFrame des ventes s’appelle ventes
. Il faut d’abord le
convertir
en objet geopandas
.
code_service_ch | reference_document | articles_1 | articles_2 | articles_3 | articles_4 | articles_5 | numero_disposition | date_mutation | nature_mutation | ... | nombre_pieces_principales | nature_culture | nature_culture_speciale | surface_terrain | lat | lon | geom.type | geom.coordinates | geom | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | None | None | None | None | None | None | None | 000001 | 2017-09-29 | Vente | ... | 0.0 | None | None | NaN | 48.037810 | -4.717967 | Point | [-4.717967, 48.03781] | NaN | POINT (-4.71797 48.03781) |
1 | None | None | None | None | None | None | None | 000001 | 2018-07-29 | Vente | ... | 0.0 | None | None | NaN | 48.037810 | -4.717967 | Point | [-4.717967, 48.03781] | NaN | POINT (-4.71797 48.03781) |
2 | None | None | None | None | None | None | None | 000001 | 2014-10-30 | Vente | ... | NaN | T | None | 1240.0 | 48.042296 | -4.709488 | Point | [-4.709488, 48.042296] | NaN | POINT (-4.70949 48.0423) |
3 | None | None | None | None | None | None | None | 000001 | 2014-10-30 | Vente | ... | NaN | T | None | 630.0 | 48.043125 | -4.706963 | Point | [-4.706963, 48.043125] | NaN | POINT (-4.70696 48.04312) |
4 | None | None | None | None | None | None | None | 000001 | 2015-06-25 | Vente | ... | NaN | J | None | 78.0 | 48.042232 | -4.705553 | Point | [-4.705553, 48.042232] | NaN | POINT (-4.70555 48.04223) |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
434 | None | None | None | None | None | None | None | 000001 | 2015-09-01 | Vente | ... | NaN | T | None | 1595.0 | 48.037084 | -4.712427 | Point | [-4.712427, 48.037084] | NaN | POINT (-4.71243 48.03708) |
435 | None | None | None | None | None | None | None | 000001 | 2015-09-01 | Vente | ... | 4.0 | S | None | 555.0 | 48.037312 | -4.712316 | Point | [-4.712316, 48.037312] | NaN | POINT (-4.71232 48.03731) |
436 | None | None | None | None | None | None | None | 000001 | 2015-09-01 | Vente | ... | 0.0 | S | None | 555.0 | 48.037312 | -4.712316 | Point | [-4.712316, 48.037312] | NaN | POINT (-4.71232 48.03731) |
437 | None | None | None | None | None | None | None | 000001 | 2015-09-01 | Vente | ... | NaN | T | None | 595.0 | 48.037271 | -4.711856 | Point | [-4.711856, 48.037271] | NaN | POINT (-4.71186 48.03727) |
438 | None | None | None | None | None | None | None | 000001 | 2014-10-30 | Vente | ... | NaN | L | None | 850.0 | 48.033956 | -4.716009 | Point | [-4.716009, 48.033956] | NaN | POINT (-4.71601 48.03396) |
431 rows × 49 columns
Avant de faire une carte, on va convertir
les limites de la commune de Plogoff en geoJSON pour faciliter
sa représentation avec folium
(voir la doc Geopandas
à ce propos):
Pour représenter graphiquement, on peut utiliser le code suivant (essayez de le comprendre et pas uniquement de l’exécuter).
4 Géocoder des données grâce aux API officielles
Pour pouvoir faire cet exercice
!pip install xlrd
Jusqu’à présent, nous avons travaillé sur des données où la dimension géographique était déjà présente ou relativement facile à intégrer.
Ce cas idéal ne se rencontre pas nécessairement dans la pratique. On dispose parfois de localisations plus ou moins précises et plus ou moins bien formattées pour déterminer la localisation de certains lieux.
Depuis quelques années, un service officiel de géocodage a été mis en place. Celui-ci est gratuit et permet de manière efficace de coder des adresses à partir d’une API. Cette API, connue sous le nom de la Base d’Adresses Nationale (BAN) a bénéficié de la mise en commun de données de plusieurs acteurs (collectivités locales, Poste) et de compétences d’acteurs comme Etalab. La documentation de celle-ci est disponible à l’adresse https://api.gouv.fr/les-api/base-adresse-nationale.
Pour illustrer la manière de géocoder des données avec Python
, nous
allons partir de la base
des résultats des auto-écoles à l’examen du permis sur l’année 2018.
Ces données nécessitent un petit peu de travail pour être propres à une analyse statistique. Après avoir renommé les colonnes, nous n’allons conserver que les informations relatives au permis B (permis voiture classique) et les auto-écoles ayant présenté au moins 20 personnes à l’examen.
import pandas as pd
import xlrd
import geopandas as gpd
= pd.read_excel(
df "https://www.data.gouv.fr/fr/datasets/r/d4b6b072-8a7d-4e04-a029-8cdbdbaf36a5",
=[0, 1],
header
)
# The Excel file has nested column names,
# we clean it
= [
index_0 "" if df.columns[i][0].startswith("Unnamed") else df.columns[i][0]
for i in range(len(df.columns))
]= [df.columns[i][1] for i in range(len(df.columns))]
index_1 = [True if el in ("", "B") else False for el in index_0]
keep_index = [index_0[i] + " " + index_1[i].replace("+", "_") for i in range(len(df.columns))]
cols = cols
df.columns = df.loc[:, keep_index]
df = df.columns.str.replace("(^ |°)", "", regex=True).str.replace(" ", "_")
df.columns
# We keep the subsample of interest
= df.dropna(subset=["B_NB"])
df = df.loc[~df["B_NB"].astype(str).str.contains(r"(\%|\.)", regex=True), :]
df "B_NB"] = df["B_NB"].astype(int)
df["B_TR"] = (
df["B_TR"].str.replace(",", ".").str.replace("%", "", regex=True).astype(float)
df[
)= df.loc[df["B_NB"] > 20] df
/tmp/ipykernel_1565/453192817.py:19: UserWarning:
This pattern is interpreted as a regular expression, and has match groups. To actually get the groups, use str.extract.
In this sample, the average success rate in 2018 was 58.02%
Nos informations géographiques prennent la forme suivante :
"Adresse", "CP", "Ville"]].head(5) df.loc[:, [
Adresse | CP | Ville | |
---|---|---|---|
0 | 56 RUE CHARLES ROBIN | 01000 | BOURG EN BRESSE |
2 | 7, avenue Revermont | 01250 | Ceyzeriat |
3 | 72 PLACE DE LA MAIRIE | 01000 | SAINT-DENIS LES BOURG |
4 | 6 RUE DU LYCEE | 01000 | BOURG EN BRESSE |
5 | 9 place Edgard Quinet | 01000 | BOURG EN BRESSE |
Autrement dit, nous disposons d’une adresse, d’un code postal et d’un nom de ville. Ces informations peuvent servir à faire une recherche sur la localisation d’une auto-école puis, éventuellement, de se restreindre à un sous-échantillon.
4.1 Utiliser l’API BAN
La documentation officielle de l’API propose un certain nombre d’exemples de manière de géolocaliser des données. Dans notre situation, deux points d’entrée paraissent intéressants:
- L’API
/search/
qui représente un point d’entrée avec des URL de la formehttps://api-adresse.data.gouv.fr/search/?q=\<adresse\>&postcode=\<codepostal\>&limit=1
- L’API
/search/csv
qui prend un CSV en entrée et retourne ce même CSV avec les observations géocodées. La requête prend la forme suivante, en apparence moins simple à mettre en oeuvre :curl -X POST -F data=@search.csv -F columns=adresse -F columns=postcode https://api-adresse.data.gouv.fr/search/csv/
La tentation serait forte d’utiliser la première méthode avec une boucle sur les lignes de notre DataFrame
pour géocoder l’ensemble de notre jeu de données. Cela serait néanmoins une mauvaise idée car les communications entre notre session Python
et les serveurs de l’API seraient beaucoup trop nombreuses pour offrir des performances satisfaisantes.
Pour vous en convaincre, vous pouvez exécuter le code suivant sur un petit échantillon de données (par exemple 100 comme ici) et remarquer que le temps d’exécution est assez important
import time
= df.loc[:, ["Adresse", "CP", "Ville"]].apply(
dfgeoloc lambda s: s.str.lower().str.replace(",", " ")
)"url"] = (
dfgeoloc["Adresse"] + "+" + dfgeoloc["Ville"].str.replace("-", "+")
dfgeoloc[str.replace(" ", "+")
)."url"] = (
dfgeoloc["https://api-adresse.data.gouv.fr/search/?q="
+ dfgeoloc["url"]
+ "&postcode="
+ df["CP"]
+ "&limit=1"
)= dfgeoloc.dropna()
dfgeoloc
= time.time()
start_time
def get_geoloc(i):
print(i)
return gpd.GeoDataFrame.from_features(
"url"].iloc[i]).json()["features"]
requests.get(dfgeoloc[
)
= [get_geoloc(i) for i in range(len(dfgeoloc.head(10)))]
local print("--- %s seconds ---" % (time.time() - start_time))
Comme l’indique la documentation, si on désire industrialiser notre processus de géocodage, on va privilégier l’API CSV.
Pour obtenir une requête CURL
cohérente avec le format désiré par l’API on va à nouveau utiliser Requests
mais cette fois avec des paramètres supplémentaires:
data
va nous permettre de passer des paramètres àCURL
(équivalents aux-F
de la requêteCURL
) :columns
: Les colonnes utilisées pour localiser une donnée. En l’occurrence, on utilise l’adresse et la ville (car les codes postaux n’étant pas uniques, un même nom de voirie peut se trouver dans plusieurs villes partageant le même code postal) ;postcode
: Le code postal de la ville. Idéalement nous aurions utilisé le code Insee mais nous ne l’avons pas dans nos données ;result_columns
: on restreint les données échangées avec l’API aux colonnes qui nous intéressent. Cela permet d’accélérer les processus (on échange moins de données) et de réduire l’impact carbone de notre activité (moins de transferts = moins d’énergie dépensée). En l’occurrence, on ne ressort que les données géolocalisées et un score de confiance en la géolocalisation ;
files
: permet d’envoyer un fichier viaCURL
.
Les données sont récupérées avec request.post
. Comme il s’agit d’une chaîne de caractère, nous pouvons directement la lire avec Pandas
en utilisant io.StringIO
pour éviter d’écrire des données intermédiaires.
Le nombre d’échos semblant être limité, il est proposé de procéder par morceaux (ici, le jeu de données est découpé en 5 morceaux).
import requests
import io
import numpy as np
import time
= {
params "columns": ["Adresse", "Ville"],
"postcode": "CP",
"result_columns": ["result_score", "latitude", "longitude"],
}
"Adresse", "CP", "Ville"]] = df.loc[:, ["Adresse", "CP", "Ville"]].apply(
df[[lambda s: s.str.lower().str.replace(",", " ")
)
def geoloc_chunk(x):
= x.loc[:, ["Adresse", "CP", "Ville"]]
dfgeoloc "datageocodage.csv", index=False)
dfgeoloc.to_csv(= requests.post(
response "https://api-adresse.data.gouv.fr/search/csv/",
=params,
data={"data": ("datageocodage.csv", open("datageocodage.csv", "rb"))},
files
)= pd.read_csv(io.StringIO(response.text), dtype={"CP": "str"})
geoloc return geoloc
= time.time()
start_time = [geoloc_chunk(dd) for dd in np.array_split(df, 10)]
geodata print("--- %s seconds ---" % (time.time() - start_time))
Cette méthode est beaucoup plus rapide et permet ainsi, une fois retourné à nos données initiales, d’avoir un jeu de données géolocalisé.
# Retour aux données initiales
= pd.concat(geodata, ignore_index=True)
geodata = df.merge(geodata, on=["Adresse", "CP", "Ville"])
df_xy = df_xy.dropna(subset=["latitude", "longitude"])
df_xy
# Mise en forme pour le tooltip
"text"] = (
df_xy["Raison_Sociale"]
df_xy[+ "<br>"
+ df_xy["Adresse"]
+ "<br>"
+ df_xy["Ville"]
+ "<br>Nombre de candidats:"
+ df_xy["B_NB"].astype(str)
)filter(
df_xy."Raison_Sociale", "Adresse", "CP", "Ville", "latitude", "longitude"],
[="columns",
axis10) ).sample(
Raison_Sociale | Adresse | CP | Ville | latitude | longitude | |
---|---|---|---|---|---|---|
1427 | CENTRE STC | za croix fort | 17220 | st christophe | 46.135597 | -1.007321 |
6971 | BUISSONNIERE | 138 cours lafayette | 69003 | lyon | 45.763567 | 4.854055 |
10129 | AUTO ECOLE DE LA GARE | 12 boulevard de la république | 93130 | noisy-le-sec | 48.895540 | 2.461533 |
11580 | L'EQUATEUR JEAN | 2 rue bsma combani | 97680 | tsingoni | -12.788554 | 45.135682 |
3226 | BLEU DEPART | 37 rue du colombier | 34670 | baillargues | 43.657818 | 4.011326 |
8575 | CFC Vernouillet | 16 place du général de gaulle | 78450 | verenouillet | 48.830738 | 2.004435 |
7481 | LES PORTIQUES Chambéry | 591 faubourg montmelian | 73000 | chambery | 45.566217 | 5.933546 |
9716 | E.C.E EVASION | 1 bd henri iv | 91150 | etampes | 48.436327 | 2.158636 |
3150 | STYLE CONDUITE | 94 place du jeu de paume | 34290 | montblanc | 43.396620 | 3.367968 |
7438 | STEPHANIE auto-école | 6 place de la république | 72220 | ecommoy | 47.828389 | 0.273118 |
Il ne reste plus qu’à utiliser Geopandas
et nous serons en mesure de faire une carte des localisations des auto-écoles :
# Transforme en geopandas pour les cartes
import geopandas as gpd
= gpd.GeoDataFrame(
dfgeo =gpd.points_from_xy(df_xy.longitude, df_xy.latitude)
df_xy, geometry )
Nous allons représenter les stations dans l’Essonne avec un zoom initialement sur les villes de Massy et Palaiseau. Le code est le suivant :
import folium
# Représenter toutes les autoécoles de l'Essonne
= df_xy.loc[df_xy["Dept"] == "091"]
df_91
# Centrer la vue initiale sur Massy-Palaiseau
= df_xy.loc[df_xy["Ville"].isin(["massy", "palaiseau"])]
df_pal = df_pal[["latitude", "longitude"]].mean().values.tolist()
center = df_pal[["latitude", "longitude"]].min().values.tolist()
sw = df_pal[["latitude", "longitude"]].max().values.tolist()
ne
= folium.Map(location=center, tiles="OpenStreetMap")
m
# I can add marker one by one on the map
for i in range(0, len(df_91)):
folium.Marker("latitude"], df_91.iloc[i]["longitude"]],
[df_91.iloc[i][=df_91.iloc[i]["text"],
popup=folium.Icon(icon="car", prefix="fa"),
icon
).add_to(m)
m.fit_bounds([sw, ne])
# Afficher la carte
m
Vous pouvez aller plus loin avec l’exercice suivant.
On va supposer que vous cherchez, dans un rayon donné autour d’un centre ville, les auto-écoles disponibles.
Fonction nécessaire pour cet exercice
Cet exercice nécessite une fonction pour créer un cercle autour d’un point (source ici). La voici :
from functools import partial
import pyproj
from shapely.ops import transform
from shapely.geometry import Point
= pyproj.Proj("+proj=longlat +datum=WGS84")
proj_wgs84
def geodesic_point_buffer(lat, lon, km):
# Azimuthal equidistant projection
= "+proj=aeqd +lat_0={lat} +lon_0={lon} +x_0=0 +y_0=0"
aeqd_proj = partial(
project format(lat=lat, lon=lon)), proj_wgs84
pyproj.transform, pyproj.Proj(aeqd_proj.
)= Point(0, 0).buffer(km * 1000) # distance in metres
buf return transform(project, buf).exterior.coords[:]
- Pour commencer, utiliser l’API Geo pour la ville de Palaiseau.
- Appliquer la fonction
geodesic_point_buffer
au centre ville de Palaiseau - Ne conserver que les auto-écoles dans ce cercle et les ordonner
Si vous avez la réponse à la question 3, n’hésitez pas à la soumettre sur Github
afin que je complète la correction 😉 !
ERROR 1: PROJ: proj_create_from_database: Open of /opt/conda/share/proj failed
Skipping field codesPostaux: unsupported OGR type: 5
/opt/conda/lib/python3.12/site-packages/shapely/ops.py:276: FutureWarning:
This function is deprecated. See: https://pyproj4.github.io/pyproj/stable/gotchas.html#upgrading-to-pyproj-2-from-pyproj-1
Pour se convaincre, de notre cercle constitué lors de la question 2, on peut représenter une carte. On a bien un cercle centré autour de Palaiseau :
5 Exercices supplémentaires
5.1 Découvrir l’API d’OpenFoodFacts
Pour vous aidez, vous pouvez regarder une exemple de structure du JSON ici :
https://world.openfoodfacts.org/api/v0/product/3274080005003.json en particulier la catégorie nutriments
.
Voici une liste de code-barres:
3274080005003, 5449000000996, 8002270014901, 3228857000906, 3017620421006, 8712100325953
Utiliser l’API d’openfoodfacts (l’API, pas depuis le CSV !) pour retrouver les produits correspondants et leurs caractéristiques nutritionnelles.
Le panier paraît-il équilibré ? 🍫
Récupérer l’URL d’une des images et l’afficher dans votre navigateur.
product_name | code | categories | categories_tags | nutriments.energy_100g | nutriments.salt_100g | nutriments.sodium_100g | nutriments.sugars_100g | nutriments.proteins_100g | nutriments.fat_100g | nutriments.carbohydrates_100g | nutriments.saturated-fat_100g | nutriments.calcium_100g | nutriments.fiber_100g | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3274080005003 | Boissons et préparations de boissons,Boissons,... | [en:beverages-and-beverages-preparations, en:b... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
0 | Coca-Cola | 5449000000996 | Getränke und Getränkezubereitungen,Getränke,Ko... | [en:beverages-and-beverages-preparations, en:b... | 180.0000 | 0.0 | 0.0 | 10.6 | 0.0 | 0.0 | 10.6 | 0.0 | NaN | NaN |
0 | S. Pellegrino Water | 8002270014901 | Boissons et préparations de boissons,Boissons,... | [en:beverages-and-beverages-preparations, en:b... | 0.0001 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.164 | NaN |
For example, here is the photo of the product with the barcode 5449000000996
. Do you recognize it?
Informations additionnelles
environment files have been tested on.
Latest built version: 2024-11-09
Python version used:
'3.12.6 | packaged by conda-forge | (main, Sep 30 2024, 18:08:52) [GCC 13.3.0]'
Package | Version |
---|---|
affine | 2.4.0 |
aiobotocore | 2.15.1 |
aiohappyeyeballs | 2.4.3 |
aiohttp | 3.10.8 |
aioitertools | 0.12.0 |
aiosignal | 1.3.1 |
alembic | 1.13.3 |
altair | 5.4.1 |
aniso8601 | 9.0.1 |
annotated-types | 0.7.0 |
appdirs | 1.4.4 |
archspec | 0.2.3 |
asttokens | 2.4.1 |
attrs | 24.2.0 |
babel | 2.16.0 |
bcrypt | 4.2.0 |
beautifulsoup4 | 4.12.3 |
black | 24.8.0 |
blinker | 1.8.2 |
blis | 0.7.11 |
bokeh | 3.5.2 |
boltons | 24.0.0 |
boto3 | 1.35.23 |
botocore | 1.35.23 |
branca | 0.7.2 |
Brotli | 1.1.0 |
cachetools | 5.5.0 |
cartiflette | 0.0.2 |
Cartopy | 0.24.1 |
catalogue | 2.0.10 |
cattrs | 24.1.2 |
certifi | 2024.8.30 |
cffi | 1.17.1 |
charset-normalizer | 3.3.2 |
click | 8.1.7 |
click-plugins | 1.1.1 |
cligj | 0.7.2 |
cloudpathlib | 0.20.0 |
cloudpickle | 3.0.0 |
colorama | 0.4.6 |
comm | 0.2.2 |
commonmark | 0.9.1 |
conda | 24.9.1 |
conda-libmamba-solver | 24.7.0 |
conda-package-handling | 2.3.0 |
conda_package_streaming | 0.10.0 |
confection | 0.1.5 |
contextily | 1.6.2 |
contourpy | 1.3.0 |
cryptography | 43.0.1 |
cycler | 0.12.1 |
cymem | 2.0.8 |
cytoolz | 1.0.0 |
dask | 2024.9.1 |
dask-expr | 1.1.15 |
databricks-sdk | 0.33.0 |
debugpy | 1.8.6 |
decorator | 5.1.1 |
Deprecated | 1.2.14 |
diskcache | 5.6.3 |
distributed | 2024.9.1 |
distro | 1.9.0 |
docker | 7.1.0 |
duckdb | 0.10.1 |
en-core-web-sm | 3.7.1 |
entrypoints | 0.4 |
et_xmlfile | 2.0.0 |
exceptiongroup | 1.2.2 |
executing | 2.1.0 |
fastexcel | 0.11.6 |
fastjsonschema | 2.20.0 |
fiona | 1.10.1 |
Flask | 3.0.3 |
folium | 0.17.0 |
fontawesomefree | 6.6.0 |
fonttools | 4.54.1 |
frozendict | 2.4.4 |
frozenlist | 1.4.1 |
fsspec | 2023.12.2 |
funcy | 2.0 |
gensim | 4.3.2 |
geographiclib | 2.0 |
geopandas | 1.0.1 |
geoplot | 0.5.1 |
geopy | 2.4.1 |
gitdb | 4.0.11 |
GitPython | 3.1.43 |
google-auth | 2.35.0 |
graphene | 3.3 |
graphql-core | 3.2.4 |
graphql-relay | 3.2.0 |
graphviz | 0.20.3 |
great-tables | 0.12.0 |
greenlet | 3.1.1 |
gunicorn | 22.0.0 |
h2 | 4.1.0 |
hpack | 4.0.0 |
htmltools | 0.6.0 |
hyperframe | 6.0.1 |
idna | 3.10 |
imageio | 2.36.0 |
importlib_metadata | 8.5.0 |
importlib_resources | 6.4.5 |
inflate64 | 1.0.0 |
ipykernel | 6.29.5 |
ipython | 8.28.0 |
itsdangerous | 2.2.0 |
jedi | 0.19.1 |
Jinja2 | 3.1.4 |
jmespath | 1.0.1 |
joblib | 1.4.2 |
jsonpatch | 1.33 |
jsonpointer | 3.0.0 |
jsonschema | 4.23.0 |
jsonschema-specifications | 2024.10.1 |
jupyter-cache | 1.0.0 |
jupyter_client | 8.6.3 |
jupyter_core | 5.7.2 |
kaleido | 0.2.1 |
kiwisolver | 1.4.7 |
langcodes | 3.4.1 |
language_data | 1.2.0 |
lazy_loader | 0.4 |
libmambapy | 1.5.9 |
locket | 1.0.0 |
lxml | 5.3.0 |
lz4 | 4.3.3 |
Mako | 1.3.5 |
mamba | 1.5.9 |
mapclassify | 2.8.1 |
marisa-trie | 1.2.1 |
Markdown | 3.6 |
markdown-it-py | 3.0.0 |
MarkupSafe | 2.1.5 |
matplotlib | 3.9.2 |
matplotlib-inline | 0.1.7 |
mdurl | 0.1.2 |
menuinst | 2.1.2 |
mercantile | 1.2.1 |
mizani | 0.11.4 |
mlflow | 2.16.2 |
mlflow-skinny | 2.16.2 |
msgpack | 1.1.0 |
multidict | 6.1.0 |
multivolumefile | 0.2.3 |
munkres | 1.1.4 |
murmurhash | 1.0.10 |
mypy-extensions | 1.0.0 |
narwhals | 1.13.3 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.9.1 |
numexpr | 2.10.1 |
numpy | 1.26.4 |
opencv-python-headless | 4.10.0.84 |
openpyxl | 3.1.5 |
opentelemetry-api | 1.16.0 |
opentelemetry-sdk | 1.16.0 |
opentelemetry-semantic-conventions | 0.37b0 |
OWSLib | 0.28.1 |
packaging | 24.1 |
pandas | 2.2.3 |
paramiko | 3.5.0 |
parso | 0.8.4 |
partd | 1.4.2 |
pathspec | 0.12.1 |
patsy | 0.5.6 |
Pebble | 5.0.7 |
pexpect | 4.9.0 |
pickleshare | 0.7.5 |
pillow | 10.4.0 |
pip | 24.2 |
platformdirs | 4.3.6 |
plotly | 5.24.1 |
plotnine | 0.13.6 |
pluggy | 1.5.0 |
polars | 1.8.2 |
preshed | 3.0.9 |
prometheus_client | 0.21.0 |
prometheus_flask_exporter | 0.23.1 |
prompt_toolkit | 3.0.48 |
protobuf | 4.25.3 |
psutil | 6.0.0 |
ptyprocess | 0.7.0 |
pure_eval | 0.2.3 |
py7zr | 0.20.8 |
pyarrow | 17.0.0 |
pyarrow-hotfix | 0.6 |
pyasn1 | 0.6.1 |
pyasn1_modules | 0.4.1 |
pybcj | 1.0.2 |
pycosat | 0.6.6 |
pycparser | 2.22 |
pycryptodomex | 3.21.0 |
pydantic | 2.9.2 |
pydantic_core | 2.23.4 |
Pygments | 2.18.0 |
pyLDAvis | 3.4.1 |
PyNaCl | 1.5.0 |
pynsee | 0.1.8 |
pyogrio | 0.10.0 |
pyOpenSSL | 24.2.1 |
pyparsing | 3.1.4 |
pyppmd | 1.1.0 |
pyproj | 3.7.0 |
pyshp | 2.3.1 |
PySocks | 1.7.1 |
python-dateutil | 2.9.0 |
python-dotenv | 1.0.1 |
python-magic | 0.4.27 |
pytz | 2024.1 |
pyu2f | 0.1.5 |
pywaffle | 1.1.1 |
PyYAML | 6.0.2 |
pyzmq | 26.2.0 |
pyzstd | 0.16.2 |
querystring_parser | 1.2.4 |
rasterio | 1.4.2 |
referencing | 0.35.1 |
regex | 2024.9.11 |
requests | 2.32.3 |
requests-cache | 1.2.1 |
retrying | 1.3.4 |
rich | 13.9.4 |
rpds-py | 0.21.0 |
rsa | 4.9 |
ruamel.yaml | 0.18.6 |
ruamel.yaml.clib | 0.2.8 |
s3fs | 2023.12.2 |
s3transfer | 0.10.2 |
scikit-image | 0.24.0 |
scikit-learn | 1.5.2 |
scipy | 1.13.0 |
seaborn | 0.13.2 |
setuptools | 74.1.2 |
shapely | 2.0.6 |
shellingham | 1.5.4 |
six | 1.16.0 |
smart-open | 7.0.5 |
smmap | 5.0.0 |
sortedcontainers | 2.4.0 |
soupsieve | 2.5 |
spacy | 3.7.5 |
spacy-legacy | 3.0.12 |
spacy-loggers | 1.0.5 |
SQLAlchemy | 2.0.35 |
sqlparse | 0.5.1 |
srsly | 2.4.8 |
stack-data | 0.6.2 |
statsmodels | 0.14.4 |
tabulate | 0.9.0 |
tblib | 3.0.0 |
tenacity | 9.0.0 |
texttable | 1.7.0 |
thinc | 8.2.5 |
threadpoolctl | 3.5.0 |
tifffile | 2024.9.20 |
toolz | 1.0.0 |
topojson | 1.9 |
tornado | 6.4.1 |
tqdm | 4.66.5 |
traitlets | 5.14.3 |
truststore | 0.9.2 |
typer | 0.13.0 |
typing_extensions | 4.12.2 |
tzdata | 2024.2 |
Unidecode | 1.3.8 |
url-normalize | 1.4.3 |
urllib3 | 1.26.20 |
wasabi | 1.1.3 |
wcwidth | 0.2.13 |
weasel | 0.4.1 |
webdriver-manager | 4.0.2 |
websocket-client | 1.8.0 |
Werkzeug | 3.0.4 |
wheel | 0.44.0 |
wordcloud | 1.9.3 |
wrapt | 1.16.0 |
xgboost | 2.1.1 |
xlrd | 2.0.1 |
xyzservices | 2024.9.0 |
yarl | 1.13.1 |
yellowbrick | 1.5 |
zict | 3.0.0 |
zipp | 3.20.2 |
zstandard | 0.23.0 |
View file history
SHA | Date | Author | Description |
---|---|---|---|
e56a219 | 2024-10-30 17:13:03 | Lino Galiana | Intro partie modélisation & typo geopandas (#571) |
9d8e69c | 2024-10-21 17:10:03 | lgaliana | update badges shortcode for all manipulation part |
47a0770 | 2024-08-23 07:51:58 | linogaliana | fix API notebook |
1953609 | 2024-08-12 16:18:19 | linogaliana | One button is enough |
783a278 | 2024-08-12 11:07:18 | Lino Galiana | Traduction API (#538) |
580cba7 | 2024-08-07 18:59:35 | Lino Galiana | Multilingual version as quarto profile (#533) |
101465f | 2024-08-07 13:56:35 | Lino Galiana | regex, webscraping and API chapters in 🇬🇧 (#532) |
065b0ab | 2024-07-08 11:19:43 | Lino Galiana | Nouveaux callout dans la partie manipulation (#513) |
06d003a | 2024-04-23 10:09:22 | Lino Galiana | Continue la restructuration des sous-parties (#492) |
8c316d0 | 2024-04-05 19:00:59 | Lino Galiana | Fix cartiflette deprecated snippets (#487) |
005d89b | 2023-12-20 17:23:04 | Lino Galiana | Finalise l’affichage des statistiques Git (#478) |
3fba612 | 2023-12-17 18:16:42 | Lino Galiana | Remove some badges from python (#476) |
a06a268 | 2023-11-23 18:23:28 | Antoine Palazzolo | 2ème relectures chapitres ML (#457) |
b68369d | 2023-11-18 18:21:13 | Lino Galiana | Reprise du chapitre sur la classification (#455) |
889a71b | 2023-11-10 11:40:51 | Antoine Palazzolo | Modification TP 3 (#443) |
04ce567 | 2023-10-23 19:04:01 | Lino Galiana | Mise en forme chapitre API (#442) |
3eb0aeb | 2023-10-23 11:59:24 | Thomas Faria | Relecture jusqu’aux API (#439) |
a771183 | 2023-10-09 11:27:45 | Antoine Palazzolo | Relecture TD2 par Antoine (#418) |
a63319a | 2023-10-04 15:29:04 | Lino Galiana | Correction du TP numpy (#419) |
154f09e | 2023-09-26 14:59:11 | Antoine Palazzolo | Des typos corrigées par Antoine (#411) |
3bdf3b0 | 2023-08-25 11:23:02 | Lino Galiana | Simplification de la structure 🤓 (#393) |
130ed71 | 2023-07-18 19:37:11 | Lino Galiana | Restructure les titres (#374) |
f0c583c | 2023-07-07 14:12:22 | Lino Galiana | Images viz (#371) |
ef28fef | 2023-07-07 08:14:42 | Lino Galiana | Listing pour la première partie (#369) |
f21a24d | 2023-07-02 10:58:15 | Lino Galiana | Pipeline Quarto & Pages 🚀 (#365) |
62aeec1 | 2023-06-10 17:40:39 | Lino Galiana | Avertissement sur la partie API (#358) |
38693f6 | 2023-04-19 17:22:36 | Lino Galiana | Rebuild visualisation part (#357) |
3248633 | 2023-02-18 13:11:52 | Lino Galiana | Shortcode rawhtml (#354) |
3c880d5 | 2022-12-27 17:34:59 | Lino Galiana | Chapitre regex + Change les boites dans plusieurs chapitres (#339) |
f5f0f9c | 2022-11-02 19:19:07 | Lino Galiana | Relecture début partie modélisation KA (#318) |
2dc82e7 | 2022-10-18 22:46:47 | Lino Galiana | Relec Kim (visualisation + API) (#302) |
f10815b | 2022-08-25 16:00:03 | Lino Galiana | Notebooks should now look more beautiful (#260) |
494a85a | 2022-08-05 14:49:56 | Lino Galiana | Images featured ✨ (#252) |
d201e3c | 2022-08-03 15:50:34 | Lino Galiana | Pimp la homepage ✨ (#249) |
1239e3e | 2022-06-21 14:05:15 | Lino Galiana | Enonces (#239) |
bb38643 | 2022-06-08 16:59:40 | Lino Galiana | Répare bug leaflet (#234) |
5698e30 | 2022-06-03 18:28:37 | Lino Galiana | Finalise widget (#232) |
7b9f27b | 2022-06-03 17:05:15 | Lino Galiana | Essaie régler les problèmes widgets JS (#231) |
1ca1a8a | 2022-05-31 11:44:23 | Lino Galiana | Retour du chapitre API (#228) |
Notes de bas de page
JSON is a highly appreciated format in the field of big data because it allows stacking incomplete data. It is one of the preferred formats of the No-SQL paradigm, for which this excellent course offers a gentle introduction.↩︎
Depending on the API, we either need nothing more if we directly obtain a JSON, or we may need to use a parser like
BeautifulSoup
otherwise. Here, the JSON can be formatted relatively easily.↩︎
Citation
@book{galiana2023,
author = {Galiana, Lino},
title = {Python pour la data science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {fr}
}