Vous pouvez lire le billet sur le blog La Minute pour plus d'informations sur les RSS !
Feeds
1610 items (12 unread) in 55 feeds

-
Décryptagéo, l'information géographique
-
Cybergeo
-
Revue Internationale de Géomatique (RIG)
-
SIGMAG & SIGTV.FR - Un autre regard sur la géomatique (1 unread)
-
Mappemonde

-
Imagerie Géospatiale
-
Toute l’actualité des Geoservices de l'IGN
-
arcOrama, un blog sur les SIG, ceux d ESRI en particulier (5 unread)
-
arcOpole - Actualités du Programme
-
Géoclip, le générateur d'observatoires cartographiques
-
Blog GEOCONCEPT FR

-
Géoblogs (GeoRezo.net)
-
Conseil national de l'information géolocalisée
-
Geotribu
-
Les cafés géographiques (1 unread)
-
UrbaLine (le blog d'Aline sur l'urba, la géomatique, et l'habitat)
-
Icem7
-
Séries temporelles (CESBIO)
-
Datafoncier, données pour les territoires (Cerema) (1 unread)
-
Cartes et figures du monde
-
SIGEA: actualités des SIG pour l'enseignement agricole
-
Data and GIS tips
-
Neogeo Technologies
-
ReLucBlog
-
L'Atelier de Cartographie
-
My Geomatic
-
archeomatic (le blog d'un archéologue à l’INRAP)
-
Cartographies numériques (1 unread)
-
Veille cartographie (2 unread)
-
Makina Corpus (1 unread)
-
Oslandia
-
Camptocamp
-
Carnet (neo)cartographique
-
Le blog de Geomatys
-
GEOMATIQUE
-
Geomatick
-
CartONG (actualités)
Icem7
-
13:54
3 explorations bluffantes avec DuckDB – Butiner des API JSON (2/3)
sur Icem7DuckDB saurait-il rivaliser avec JavaScript pour exploiter des données JSON ? Ce n’est pas le terrain sur lequel j’attendais ce moteur SQL. Quelle ne fut pas ma surprise, pourtant, de le voir se jouer des imbrications les plus retorses, des modèles de données les plus échevelés, auxquels JSON accorde volontiers son flexible habillage.
Après le premier épisode consacré aux formats Parquet et CSV dans DuckDB, voici donc à nouveau deux exemples concrets de jeux avec des données formattées en JSON.
A - API recherche d'entreprises : celles autour de chez moi, quelles sont-elles ?
C’est en explorant les belles ressources du portail api.gouv.fr que l’idée de cet article a pris forme. De multiples API de données sont désormais proposées de façon ouverte, sans identification : professionnels bio (Agence bio), base adresses (BAN), demandes de valeurs foncières (DVF), annuaire de l’Éducation nationale, annuaire des entreprises…
Je me suis arrêté sur cette dernière source, interrogeable par une API très simple, construite par la Dinum.
Vous pouvez lui poser deux questions :
• quelles infos peux-tu me donner sur une entreprise ? Ex. : icem7, et si je tape icem77 j’aurai tout de même la bonne réponse ;
• quelles sont les entreprises autour d’un point GPS ?Alors, j’ai voulu regarder les entreprises près de chez moi. Les bureaux d’icem7 jouxtent mon domicile, j’ai donc bien retrouvé ma société et constaté que nous étions cernés par les sociétés civiles immobilières – le quartier est, il est vrai, plutôt résidentiel.
Voyons gentiment comment arriver à un tel résultat, avec DuckDB et un outil web simple.
L’URL suivante construite d’après la doc prend comme paramètres un point GPS (longitude et latitude) et un rayon (en km) ; j’ai choisi 300 m, le rayon maximum est de 50 km.
https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3
Dans un navigateur (ici Chrome), vous pouvez facilement consulter la réponse dans un affichage confortable et flexible. Quelques données numériques encadrent un « array » JavaScript de results, avec ici 10 entreprises (sur 89 annoncées, dans total_results).
Si je déplie le premier résultat (d’indice 0), se révèle une structure hiérarchique, avec bon nombre de rubriques :
On devine déjà l’intérêt de cette API, pour laquelle la Dinum collationne en temps réel de multiples sources de données : immatriculation et statut juridique, certifications, résultats financiers, siège et établissements. Les entreprises listées ici ont au moins un établissement dans le rayon de ma requête (matching_etablissements).
Que sait lire DuckDB ? Je vais l’utiliser ici dans sa version la plus dépouillée et la plus rapide, l’exécutable de 20 Mo, qui présente les résultats de façon toujours lisible, quelle que soit la largeur de votre écran.
FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3');
Cette simple requête constitue un tableau d’une seule ligne, dont la première colonne reprend results, c’est-à-dire un array (ou liste) de données structurées. Là où JavaScript parle d’array et d’objets, DuckDB évoquera des listes et des structures. Le format Parquet a aussi cette capacité d’accueillir de telles colonnes complexes.
Comment déplier ce tableau en autant de lignes que d’entreprises ?
Découvrons le pouvoir magique (et proprement bluffant, oui) de la fonction unnest() :
SELECT unnest(results, recursive := true) FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3');
Avec le paramètre recursive := true, unnest() va continuer à déplier les structures cachées dans les colonnes de chaque « result », si bien que le tableau ci-dessus contient désormais 10 lignes et 78 colonnes, autant de pépites d’information sur chaque entreprise.
Affichons-en quelques-unes en clair ; je vais réduire mon champ géographique à 10 m autour de chez moi :
SELECT unnest(results, recursive := true) FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.01');
Et en effet, je retrouve bien icem7, et même ma précédente entreprise, dont icem7 a pris la suite. Emc3 n’existe plus à cette adresse, l’API renvoie, je le découvre, des entreprises dont le statut administratif a pu évoluer.
Je vais donc affiner mes requêtes ultérieures en demandant à voir, près de chez moi, les seuls établissements actifs :
FROM ( SELECT unnest(results, recursive := true) FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.01') ) WHERE list_filter(matching_etablissements, d -> d. etat_administratif = 'A').len() >= 1 ;
Avec list_filter(), je vous laisse goûter à l’une des nombreuses fonctions de manipulations de listes, qui donnent à DuckDB toute facilité pour explorer des structures imbriquées à la JSON.
Il reste plusieurs colonnes de type liste que unnest() n’a opportunément pas dépliées, car cela aurait encore multiplié le nombre de lignes.
Déplions manuellement la colonne dirigeants, ce qui fait apparaitre une sous-table, où je me reconnais avec mon associée. On y trouve des infos étonnamment personnelles comme l’identité, la nationalité et même la date de naissance (que je ne dévoile pas ici, par galanterie) !
WITH tb1 AS ( SELECT unnest(results, recursive := true) FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.01') ) SELECT unnest(dirigeants, recursive := true) FROM tb1 WHERE list_filter(matching_etablissements, d -> d. etat_administratif = 'A').len() >= 1 ;
Sur les 78 colonnes de la table dépliée, je vais donc choisir quelques infos parmi les plus parlantes, afin de localiser ces entreprises et d’en savoir plus sur ce qu’elles font et qui les dirige. Je peux identifier des certifiés « bio » (producteurs ou revendeurs / préparateurs), des organismes de formation, par exemple ceux certifiés Qualiopi comme icem7.
Par commodité, je m’en tiens au seul premier établissement près de chez moi d’une même entreprise. DuckDB utilise des indices qui commencent par 1 (et non 0 comme dans JavaScript).
SELECT siret, nom_complet, activite_principale, dirigeants, matching_etablissements[1].longitude::float AS lon, matching_etablissements[1].latitude::float AS lat, est_qualiopi FROM ( SELECT unnest(results, recursive := true) FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3') ) WHERE list_filter(matching_etablissements, d -> d. etat_administratif = 'A').len() >= 1 ;
Avec ce premier résultat, je pourrais déjà cartographier ces entreprises et produire une analyse coloriée par code activité (NAF).
Dépasser les contraintes de l'APIBien sûr, le problème est que je n’ai reçu d’infos que sur 10 entreprises. Voyons comment relever le curseur. La doc de l’API indique que je peux pousser jusqu’à 25 entreprises par appel, et lancer 7 appels par seconde.
Pour avoir les 89 établissements à 300 m de chez moi, je m’attends donc à m’y reprendre à plusieurs fois.
SELECT total_results, total_pages FROM read_json_auto('https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25');
Avec le paramètre per_page=25, l’API me renvoie désormais une première « page » de 25 résultats, et me précise le nombre de pages nécessaires (4). Pour avoir la page 2, je dois ajouter à l’URL un paramètre &page=2.
DuckDB permet commodément de charger dans le même mouvement toute une collection d’URL :
SELECT unnest(results, recursive := true) FROM read_json_auto([ 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25', 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25&page=2', 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25&page=3', 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25&page=4' ]) ;
Mais une telle écriture reste bien lourde. Il y a plus élégant, plus « vectoriel » :
SELECT unnest(results, recursive := true) FROM read_json_auto( list_transform(generate_series(1, 4), n -> 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25&page=' || n) ) ;
generate_series(1, 4) produit un vecteur [1,2,3,4], lequel se voit transformer avec list_transform() en un vecteur d’URL.
Je suis donc en mesure de récupérer en une seule requête tous mes voisins entrepreneurs, et pour ne pas solliciter l’API à chaque raffinement de mes écritures, je vais stocker ce premier résultat dans une table.
CREATE OR replace TABLE etab_proches_icem7 AS FROM ( SELECT unnest(results, recursive := true) FROM read_json_auto( list_transform( generate_series(1, 4), n -> 'https://recherche-entreprises.api.gouv.fr/near_point?lat=43.69875&long=1.46158&radius=0.3&per_page=25&page=' || n) ) ) WHERE list_filter(matching_etablissements, d -> d. etat_administratif = 'A').len() >= 1 ;
Voici un aperçu de cette table de 89 établissements, liste que j’ai triée pour montrer en premier les organismes de formation certifiés Qualiopi ; icem7 apparait bien.
SELECT nom_complet, activite_principale, matching_etablissements[1].longitude::float AS lon, matching_etablissements[1].latitude::float AS lat, est_organisme_formation, est_qualiopi FROM etab_proches_icem7 ORDER BY est_organisme_formation DESC, nom_complet ;
Désormais, je peux cartographier ce résultat à partir d’un export GeoJSON de cette table :
LOAD SPATIAL ; COPY ( SELECT nom_complet, activite_principale, ST_Point(matching_etablissements[1].longitude::float, matching_etablissements[1].latitude::float) AS geometry FROM etab_proches_icem7 ) TO 'C:/.../entr_proches_icem7.json' WITH (FORMAT GDAL, DRIVER 'GeoJSON') ;
L’export GeoJSON repose sur l’extension SPATIAL, que je charge au préalable (cette procédure devrait prochainement se simplifier dans DuckDB, avec un chargement automatique déclenché dès l’appel d’une fonction spatiale).
La table doit ensuite comprendre une colonne de géométrie (nommée obligatoirement geometry), ici une colonne ponctuelle générée avec la fonction ST_Point().
Je fais glisser le fichier ainsi créé dans l’interface de geojson.io pour découvrir – enfin – cette carte des entreprises près de chez moi – et elles sont nombreuses !
Joindre une nomenclature
Le code activité principale, issu de la NAF, me parle peu, je vais donc chercher des libellés clairs.
Grâce à SQL, le pouvoir des jointures, et la capacité de DuckDB à piocher directement sur le web, je récupère, par une table de passage constituée par mes soins au format parquet, les différents niveaux d’agrégation de la NAF et leurs dénominations.
SELECT nom_complet, activite_principale, ST_Point(matching_etablissements[1].longitude::float, matching_etablissements[1].latitude::float) AS geometry, n.LIB_NIV5,n.LIB_NIV1 FROM etab_proches_icem7 LEFT JOIN 'https://static.data.gouv.fr/resources/naf-1/20231123-121750/nafr2.parquet' n ON etab_proches_icem7.activite_principale = n.NIV5 ;
Cartographier selon l'activité principale
Pour produire une cartographie plus analytique, avec des punaises coloriées selon l’activité, j’utilise le service MyMaps de Google, seul outil en ligne que j’aie pu identifier pour ce faire et afficher en arrière-plan une couche de rues.
Maps ne lit pas de GeoJSON mais accepte le CSV, pourvu qu’on y insère les deux colonnes longitude et latitude (nommées comme on veut).
COPY ( SELECT nom_complet, activite_principale, matching_etablissements[1].longitude AS lon, matching_etablissements[1].latitude AS lat, n.LIB_NIV5,n.LIB_NIV1 FROM etab_proches_icem7 LEFT JOIN 'https://static.data.gouv.fr/resources/naf-1/20231123-121750/nafr2.parquet' n ON etab_proches_icem7.activite_principale = n.NIV5 ) TO 'C:/.../etab_proches_icem7.csv' ;
Comme il me reste, même au niveau le plus agrégé de la NAF, beaucoup de modalités différentes, je termine en créant un code activité simplifié, avec une catégorie « Autres » regroupant les effectifs les plus faibles :
COPY ( WITH tb1 AS ( SELECT nom_complet, activite_principale, matching_etablissements[1].longitude AS lon, matching_etablissements[1].latitude AS lat, n.LIB_NIV5,n.LIB_NIV1, count(*) OVER (PARTITION BY LIB_NIV1) AS eft_naf FROM etab_proches_icem7 LEFT JOIN 'https://static.data.gouv.fr/resources/naf-1/20231123-121750/nafr2.parquet' n ON etab_proches_icem7.activite_principale = n.NIV5 ) SELECT CASE WHEN eft_naf >= 3 THEN LIB_NIV1 ELSE 'Autres' END AS activite, * FROM tb1 ) TO 'C:/.../etab_proches_icem7.csv' ;
Et voici une carte interactive, dont vous pouvez déplier la légende (bouton en haut à gauche) ou cliquer les punaises :
B - DataTourisme et le hackaviz DataGrandEstMon second exemple sera plus concis, et il a surtout servi à résoudre un problème concret. Mon collègue Alain Roan, qui prépare le Hackaviz du 13 décembre 2023 de DataGrandEst, s’est déclaré lui-même proprement « bluffé » par l’agilité de DuckDB.
Le concours s’appuie sur des données touristiques ; il s’agit de mettre en regard des nuitées et des points d’intérêt (POI) touristiques, c’est-à-dire de tenter d’expliquer la venue des touristes par l’existence de ressources attractives : monuments, spectacles, itinéraires, etc.
Alain et l’agence régionale du tourisme voulaient constituer un fichier de POI avec une double localisation longitude/latitude et code commune Insee. Selon les sources, ils disposaient soit de l’un, soit de l’autre, mais pas des deux.
Ils le savaient, la réponse se trouve dans le portail DataTourisme. Il s’agit d’une superbe initiative, formalisée en 2017, rassemblant en temps réel toutes les sources de données disponibles. Toutefois, son « API web moderne » évoque plutôt les années 1980, époque regrettée des programmes lancés « en batch » sur « grosse machine ». Comme quand j’ai commencé à l’Insee, il faut attendre le lendemain pour disposer des résultats d’une demande d’extraction. Les « flux » ainsi mis à disposition sont d’une fluidité toute relative, celle d’une grosse goutte se détachant toutes les 24 heures.
Plus enthousiasmant encore, la requête en partie décrite dans l’image suivante (région Grand Est) conduit à récupérer une archive de plus de 20 000 fichiers JSON :
Ces 21 211 fiches JSON représentent environ 500 Mo de données, qu’il faut décompresser sur son disque dur pour envisager de les exploiter. Elles s’organisent dans une arborescence à deux niveaux. Les noms de fichier sont peu évocateurs et chaque document présente un contenu assez dense.
Voilà un court extrait d’une fiche pour vous donner une petite idée :
Armé de DuckDB, ma première entreprise fut d’extraire d’une fiche au hasard les variables essentielles pour le hackaviz. Les noms de champ sont un peu pénibles à manier, mais le bonheur de toucher du doigt l’univers enchanté des « ontologies » et du « web sémantique » l’emporte sur le désagrément d’avoir à multiplier les quotes et les crochets :
SELECT isLocatedAt[1]['schema:address'][1]['hasAddressCity']['insee'] AS code_com, "rdfs:label"['fr'][1] AS label, array_to_string("@type", ',') AS types, array_to_string(isLocatedAt[1]['schema:address'][1]['schema:streetAddress'], ' ') AS streetAddress, isLocatedAt[1]['schema:address'][1]['schema:postalCode'] AS postalCode, isLocatedAt[1]['schema:address'][1]['schema:addressLocality'] AS addressLocality, isLocatedAt[1]['schema:geo']['schema:latitude']::float AS latitude, isLocatedAt[1]['schema:geo']['schema:longitude']::float AS longitude FROM read_json_auto('C:\...\objects\2\23\3-23a1b563-5c8c-32e0-b0fc-e0fcbb077b29.json');
Et voici l’apparition du dernier effet bluffant DuckDB, la lecture en une seule passe et moins de 10 secondes des 21 211 fiches JSON :
CREATE OR replace TABLE poi_grandest AS SELECT isLocatedAt[1]['schema:address'][1]['hasAddressCity']['insee'] AS code_com, "rdfs:label"['fr'][1] AS label, array_to_string("@type", ',') AS types, array_to_string(isLocatedAt[1]['schema:address'][1]['schema:streetAddress'], ' ') AS streetAddress, isLocatedAt[1]['schema:address'][1]['schema:postalCode'] AS postalCode, isLocatedAt[1]['schema:address'][1]['schema:addressLocality'] AS addressLocality, isLocatedAt[1]['schema:geo']['schema:latitude']::float AS latitude, isLocatedAt[1]['schema:geo']['schema:longitude']::float AS longitude FROM read_json_auto('C:\...\objects\*\*\*.json') ;
L’API DataTourisme est tellement monumentale et raffinée que la plupart des professionnels du tourisme sont incapables de l’utiliser (mais des simplifications sont annoncées pour la fin de l’année). Qu’à cela ne tienne, un marché de développeurs affutés a pu naitre, auxquels les acteurs du tourisme en besoin de données sur mesure commandent des extractions, réalisées en Python, R ou autres langages d’académique facture.
Bienvenue donc au trublion DuckDB, qui sait rendre l’open data véritablement open.
FROM poi_grandest LIMIT 10;
J’ai voulu montrer avec ces deux exemples comment interroger des ressources JSON avec DuckDB, en les manipulant comme des tables. J’ai même pu apparier avec un fichier distant au format Parquet.
L’annuaire des entreprises gagnerait sans doute à être mis à disposition aussi au format parquet. L’API offre de son côté des services spécifiques complémentaires (recherche textuelle, recherche de proximité). Mais le format GeoParquet, dès qu’il aura intégré les index spatiaux, saura rivaliser avec une API par ailleurs contrainte en volume et nombre d’appels.
Dans le prochain épisode (3/3), je parlerai de cartographie, de requêtes spatiales et précisément du format GeoParquet.
Pour en savoir plus- 3 explorations bluffantes avec DuckDB – Interroger des fichiers distants (1/3), icem7, 2023
- Parquet devrait remplacer le format CSV, icem7, 2022
- DuckDB redonne une belle jeunesse au langage SQL, icem7, 2023
- Why parquet files are my preferred API for bulk open data, Robin Linacre, 2023
- DuckDB – nested functions
- Annuaire des entreprises
L’article 3 explorations bluffantes avec DuckDB – Butiner des API JSON (2/3) est apparu en premier sur Icem7.
-
15:46
3 explorations bluffantes avec DuckDB – Interroger des fichiers distants (1/3)
sur Icem7DuckDB révolutionne notre approche des données. En dépit de sa console austère, fleurant bon l’antique terminal, ce petit programme de moins de 20 Mo butine allègrement les bases les plus retorses, les plus lourdes ; qu’elles se présentent en CSV, (Geo)JSON, parquet ou en SGBD classique.
Vous êtes nombreux déjà à avoir entendu parler de cet ovni, à savoir que DuckDB est véloce, qu’il repose sur ce bon vieux langage SQL. Je veux vous présenter dans cette série de trois articles des possibilités que vous n’imaginez même pas. J’ai dû moi-même, parfois, me secouer la tête et retester soigneusement pour vérifier que je ne me trompais pas.
Commençons dans ce premier article par le travail direct avec des bases de données distantes, compressées, en open data sur Internet. Je prendrai deux exemples.
A - La base Insee du recensement de la population 2020
Premier exemple, l’Insee, l’institut statistique français, vient de mettre en ligne la base détaillée du recensement, niveau individus et logements, au format parquet. Dans ce format parquet, chaque fichier pèse tout de même 500 Mo. Mais vous n’avez pas besoin de les télécharger pour travailler avec.
Je me pose la question suivante : à Paris, quels sont les arrondissements où la part des ménages ayant plus de 2 voitures est la plus forte ? Inversement on pourra s’intéresser aux arrondissements qui privilégient le ‘sans voiture’.
Je vous livre sans ménagement la requête SQL, le graphique exposant le résultat, et juste après, je vous explique. Pour le moment, retenez que vous pouvez exécuter cette requête vous-même, avec DuckDB, qu’elle consomme 12 Mo de bande passante, et prend en gros 2 secondes pour s’exécuter.
WITH tb1 AS ( SELECT ARM, VOIT, sum(IPONDI/NPERR::int) AS eft, sum(eft) OVER (PARTITION BY ARM) AS tot, round(1000 * eft / tot)/10 AS pct FROM 'https://static.data.gouv.fr/resources/recensement-de-la-population-fichiers-detail-individus-localises-au-canton-ou-ville-2020-1/20231023-122841/fd-indcvi-2020.parquet' WHERE dept = '75' AND NPERR 'Z' GROUP BY GROUPING SETS((ARM, VOIT),(VOIT)) ), tb2 AS ( PIVOT_WIDER (SELECT arm, voit, pct FROM tb1) ON VOIT USING first(pct) ) SELECT CASE WHEN ARM = '75001' THEN '1er' WHEN ARM IS NULL THEN 'Paris' ELSE CONCAT(RIGHT(ARM,2)::int, 'e') END AS 'Arrondt', "0" AS 'pas de voiture', "1" AS '1 voiture', "2" AS '2 voitures', "2" + "3" AS '2 voitures ou +', "3" AS '3 voitures ou +' FROM tb2 ORDER BY (Arrondt = 'Paris')::int, "2 voitures ou +" DESC ;
À l’opposé, les 2e et 3e arrondissements sont ceux où la part de ménages sans voiture est la plus élevée (huit ménages sur dix).
DécortiquonsComme l’URL de la base est longue, pour simplifier mon exposé, je crée d’abord une vue SQL, qui n’est qu’un alias vers ce fichier distant :
CREATE OR REPLACE VIEW fd_indcvi_2020 AS FROM 'https://static.data.gouv.fr/resources/recensement-de-la-population-fichiers-detail-individus-localises-au-canton-ou-ville-2020-1/20231023-122841/fd-indcvi-2020.parquet';
Ne vous étonnez pas de l’absence d’un SELECT * avant le FROM, DuckDB permet de s’en passer – et c’est bien pratique – si l’on veut lire toutes les colonnes de la table.
J’utilise DuckDB soit en lançant le petit exécutable DuckDB.exe, soit à l’intérieur de DBeaver, un environnement gratuit de connexion à de multiples sources de données. DBeaver me permet de gérer de vrais scripts, de les documenter pour les retrouver plus tard. L’affichage et l’export des résultats (en CSV par exemple, ou vers le presse-papier) sont aussi plus sympas.
Une première commande simple nous donne une info minimale, la liste des colonnes et leur type :
DESCRIBE FROM fd_indcvi_2020 ;
Elle est quasi instantanée (200 ms), et j’ai mis du temps à comprendre ce qui se passait. J’analyse tout de même la structure d’un fichier de 500 Mo, sur data.gouv.fr , et je ne l’ai pas téléchargé. Comment diantre est-il possible d’avoir une info de structure aussi vite ?
Cela tient à deux facteurs :
- Le format parquet stocke dans son en-tête des métadonnées, par exemple la liste des colonnes et leur type ;
- DuckDB envoie un requête HTTP particulière, de type « range-request », qui demande à data.gouv.fr de ne lui servir qu’une petite plage de bytes, une mini-tranche du fichier parquet. Seuls 700 bytes ont transité par le réseau pour nous livrer la structure de ce fichier parquet.
Je repère les variables dont j’aurai besoin : DEPT pour retenir Paris, ARM pour les n° d’arrondissements, VOIT pour caractériser les personnes selon le nombre de voitures du ménage, IPONDI pour calculer un effectif, NPPER pour prendre en compte le nombre de personnes dans le ménage. La documentation du fichier m’offre toute la compréhension nécessaire.
Voici un comptage de ménages, selon leur nombre de voitures, par arrondissement parisien :
SELECT ARM, VOIT, round(sum(IPONDI/NPERR::int)) AS eft FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY ARM, VOIT ;
Pour calculer un nombre de ménages, je divise la population par le nombre de personnes dans le ménage. NPPER (tout comme VOIT) n’est pas exactement numérique : 6 veut dire 6 personnes ou plus (3 veut dire 3 voitures ou +) : on s’en accommodera. La modalité Z correspond à des logements « non ordinaires », qu’on laisse ici de côté.
Je produis ensuite un tableau croisé, avec PIVOT_WIDER (qu’on peut aussi écrire, plus simplement, PIVOT) :
WITH tb1 AS ( SELECT ARM, VOIT, round(sum(IPONDI/NPERR::int)) AS eft FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY ARM, VOIT ) PIVOT_WIDER (FROM tb1) ON VOIT USING first(eft) ORDER BY ARM ;
Notez la nouvelle syntaxe que j’utilise pour enchainer deux opérations dans la même requête. Elle est élégante et m’évite de créer une table physique intermédiaire. Ce qui figure dans le WITH () est comme une table temporaire, disponible le temps de la requête.
J’aimerais maintenant calculer le total pour Paris. Je n’ai pour cela qu’à aménager la clause GROUP BY. Le complément GROUPING SETS permet de spécifier ensemble différents niveaux d’agrégation.
WITH tb1 AS ( SELECT ARM, VOIT, round(sum(IPONDI/NPERR::int)) AS eft FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY GROUPING SETS ((ARM, VOIT), (VOIT)) ) PIVOT_WIDER (FROM tb1) ON VOIT USING first(eft) ORDER BY ARM ;
On découvre en bas de tableau la nouvelle ligne ajoutée. On pourra plus tard remplacer ce disgracieux NULL par la mention ‘Paris’.
Maintenant, ce que je voudrais pour répondre à ma question initiale, c’est calculer des pourcentages, pour chaque arrondissement : % de ménages du 12e qui ont 0 voiture, plus de 2 voitures, etc. Pour cela, il me faut le total des ménages pour chaque arrondissement. Il y a plusieurs façons de le faire, plus ou moins manuelles. La plus élégante consiste à utiliser les mots clés OVER et PARTITION.
Revenons à notre premier calcul, avant le PIVOT. Je lui rajoute une ligne, après la première :
SELECT ARM, VOIT, sum(IPONDI) AS eft, sum(eft) OVER (PARTITION BY ARM) AS tot, FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY GROUPING SETS((ARM, VOIT),(VOIT)) ;
Cette instruction a bien pour effet de calculer un total par arrondissement. PARTITION fonctionne comme un nouveau GROUP BY, mais qui ne change pas le nombre de lignes, il ajoute simplement une colonne calculée. Cette nouvelle instruction relève de la catégorie des « WINDOW functions », très puissantes, dont je ne vais pas décrire toutes les finesses ici.
Une autre des charmantes spécificités du SQL dans DuckDB, c’est que les colonnes calculées sont immédiatement utilisables pour le calcul d’autres nouvelles colonnes.
Ainsi, je peux produire le pourcentage dans le même mouvement :
SELECT ARM, VOIT, sum(IPONDI/NPERR::int) AS eft, sum(eft) OVER (PARTITION BY ARM) AS tot, round(1000 * eft / tot) / 10 AS pct FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY GROUPING SETS((ARM, VOIT),(VOIT)) ;
Il ne me reste plus qu’à pivoter et arranger la présentation du résultat final :
WITH tb1 AS ( SELECT ARM, VOIT, sum(IPONDI/NPERR::int) AS eft, sum(eft) OVER (PARTITION BY ARM) AS tot, round(1000 * eft / tot)/10 AS pct FROM fd_indcvi_2020 WHERE dept = '75' AND NPERR 'Z' GROUP BY GROUPING SETS((ARM, VOIT),(VOIT)) ), tb2 AS ( PIVOT_WIDER (SELECT arm, voit, pct FROM tb1) ON VOIT USING first(pct) ) SELECT CASE WHEN ARM = '75001' THEN '1er' WHEN ARM IS NULL THEN 'Paris' ELSE CONCAT(RIGHT(ARM,2)::int, 'e') END AS 'Arrondt', "0" AS 'pas de voiture', "1" AS '1 voiture', "2" AS '2 voitures', "2" + "3" AS '2 voitures ou +', "3" AS '3 voitures ou +' FROM tb2 ORDER BY (Arrondt = 'Paris')::int, "2 voitures ou +" DESC ;
Cette dernière écriture prend 2 secondes et consomme seulement 12 Mo de bande passante. Rappelons-le, elle attaque directement le fichier parquet de 500 Mo en ligne, que je n’ai pas téléchargé au préalable.
Parquet organise l’information par groupe de lignes et par colonne, je n’ai lu via des range-requests que les colonnes dont j’avais besoin pour le calcul, et uniquement pour les lignes correspondant à Paris.
B - Les faits de délinquance du ministère de l’IntérieurIntéressons-nous maintenant à la base statistique communale de la délinquance enregistrée par la police et la gendarmerie nationales.
Il ne s’agit pas – encore – de fichiers parquet, mais de CSV compressés (csv.gz). Pas de problème, DuckDB peut les lire directement. En revanche, les range-requests ne sont pas aussi puissantes qu’avec Parquet : il faudra lire tout le fichier (39 Mo) avant de pouvoir en tirer parti.
Je crée comme tout à l’heure une vue pour simplifier les écritures. En réalité, cette vue analyse déjà tout le fichier pour deviner la structure les colonnes (j’ai mesuré 1 seconde d’attente).
CREATE OR REPLACE VIEW faits_delinq AS FROM 'https://static.data.gouv.fr/resources/bases-statistiques-communale-et-departementale-de-la-delinquance-enregistree-par-la-police-et-la-gendarmerie-nationales/20230719-080535/donnee-data.gouv-2022-geographie2023-produit-le2023-07-17.csv.gz';
Ce qui fait qu’un DESCRIBE devient instantané :
DESCRIBE FROM faits_delinq ;
CODGEO_2023 est certainement le code commune, je me fabrique un aperçu de la table pour ma ville, Toulouse :
FROM faits_delinq WHERE CODGEO_2023 = '31555' LIMIT 10 ;
Les informations utiles sont : l’année (qu’il faudra arranger), la classe et le nombre de faits. On peut noter que l’unité des faits dépend de la classe d’infraction : victimes ou voitures par exemple.
Avec un PIVOT, la présentation devient plus claire, et distingue en colonnes une quinzaine de classes de faits de délinquance.
WITH faits_tls AS ( SELECT concat('20', annee) AS an, classe, faits, FROM faits_delinq WHERE CODGEO_2023 = '31555' ORDER BY an,classe ) PIVOT_WIDER faits_tls ON classe USING first(faits) ORDER BY an ;
Pour simplifier et construire un graphique, je vais m’en tenir aux seules classes dont le nombre de faits, en fin de période (2022), dépasse les 2 500. Une petite ligne additionnelle, utilisant la puissance des « WINDOW functions », me permet de calculer cette valeur terminale et de filtrer les classes que je veux retenir.
QUALIFY joue le rôle d’un WHERE, et arg_max() – encore une superbe petite fonction – cible le nombre de faits là où an est maximal (donc 2022) :
WITH faits_tls AS ( SELECT concat('20', annee) AS an, classe, faits, FROM faits_delinq WHERE CODGEO_2023 = '31555' QUALIFY arg_max(faits, an) OVER (PARTITION BY classe) > 2500 ORDER BY an,classe ) PIVOT_WIDER faits_tls ON classe USING first(faits) ORDER BY an ;
Avec un copier-coller du résultat, je peux produire, avec Datawrapper, cet éclairant graphique :
J’ai donc montré avec ces deux exemples comment interroger directement des bases distantes avec DuckDB, et avec beaucoup de souplesse et d’élégance.
J’espère aussi avoir convaincu un peu plus de diffuseurs de bases d’utiliser le format Parquet pour mettre à disposition leurs données.
Dans le prochain épisode (2/3), je parlerai d’API web et du format JSON.
Pour en savoir plus- 3 explorations bluffantes avec DuckDB – Butiner des API JSON (2/3), icem7, 2023
- Parquet devrait remplacer le format CSV, icem7, 2022
- DuckDB redonne une belle jeunesse au langage SQL, icem7, 2023
- Guide d’utilisation des données du recensement de la population au format Parquet, Antoine Palazzolo, Lino Galiana, Insee, 2023
L’article 3 explorations bluffantes avec DuckDB – Interroger des fichiers distants (1/3) est apparu en premier sur Icem7.
-
8:20
Où sont les femmes dans les rues de Toulouse (et d’ailleurs) ?
sur Icem7L’autre jour, ma femme m’interpelle, tout à trac : « toi qui aimes jouer avec les données, pourrais-tu me faire une carte des rues de Toulouse portant un nom d’une femme ? »
C’est qu’elle coorganise la Transtoulousaine, une randonnée urbaine annuelle, en itinéraires convergeant vers le centre de la ville. Chaque édition comporte un thème : cette année les arbres, une autre fois peut-être bien les femmes. Un enjeu sera alors de définir des parcours qui célèbrent des personnalités féminines, si possible locales.
À cette demande en forme de défi, je réagis d’abord avec réserve : « Ça ne va pas être simple… Je peux sans doute trouver un répertoire des rues, mais comment détecter la présence d’une femme ? ». « Facile », me rétorque-t-elle, « tu n’as qu’à utiliser le fichier des prénoms ! ». Je dois le dire, elle avait bien préparé son affaire !
Et en effet, je connais bien cette source de l’Insee, recensant tous les prénoms donnés depuis au moins 1900, et les distinguant par genre.
La base nationale des prénoms ressemble à ceci, elle présente des effectifs par année de naissance :
Localiser un fichier des rues sur le site open data de Toulouse métropole ne m’a pris que quelques minutes. Il s’agit d’un filaire de voies, un fond de carte couvrant toute la métropole, qui renseigne naturellement le nom des voies, leur nature (rue, allée, boulevard, etc.) et leur commune d’appartenance. Voici un aperçu des données associées à chaque tronçon :
On pressent déjà que les femmes ne seront pas légion.
Pour manipuler des données, mon joujou favori ces derniers temps s’appelle DuckDB. C’est un petit programme tout simple qui permet d’exécuter des requêtes SQL avec une vélocité remarquable. Pour rapprocher les voies des prénoms, je vais joindre les deux bases en précisant une condition : le nom des voies doit contenir un prénom féminin.
Une première écriture ressemble à cela :
LOAD SPATIAL ; CREATE OR REPLACE TABLE filaire_femmes_toulouse AS ( WITH prenoms_feminins AS ( SELECT preusuel, nb_prf FROM ( SELECT strip_accents(preusuel) AS preusuel, sum(nombre)::int AS nb_prf FROM 'https://icem7.fr/data/prenoms_nat2022.parquet' WHERE sexe = 2 GROUP BY ALL HAVING nb_prf > 1000 ) ) , filaire_json AS ( FROM st_read('C:/.../datasets/filaire-de-voirie.geojson') WHERE code_insee = '31555' ) SELECT * FROM filaire_json JOIN prenoms_feminins ON contains(' ' || street || ' ', ' ' || preusuel || ' ') );
Cette requête crée une table en 3 étapes :
- Lecture de la base des prénoms, que j’ai convertie de CSV vers le format parquet, bien plus compact et efficace. Je ne retiens que les prénoms donnés à des filles, et plus de mille fois. Cela représente tout de même 1 800 prénoms.
- Lecture du filaire de voies en ne gardant que les données sur Toulouse, dont le code commune est 31555. La fonction st_read() pourrait lire directement l’URL du fichier, mais celui-ci fait 20 Mo et le serveur de téléchargement est assez lent, je l’ai donc stocké au préalable.
- Jointure sur la condition de présence du prénom dans le nom de la voie (les || permettent de coller des bouts de textes, rajoutant ici des blancs de part et d’autre des colonnes pour bien isoler les prénoms).
En moins de 2 secondes, j’obtiens un résultat qui décrit les tronçons de 372 voies, soit un dixième du total des voies toulousaines.
Impatient de les visualiser, je les exporte dans un format géographique passe-partout, le GeoJSON :
COPY filaire_femmes_toulouse TO 'C:/.../datasets/filaire-femmes-toulouse.json' WITH (FORMAT GDAL, DRIVER 'GeoJSON');
Notez au passage la souplesse de cet outil de requêtage DuckDB : il est à l’aise avec tous les formats, y compris géographiques.
Une première carte bruteIl me suffit enfin de faire glisser ce fichier généré dans mon navigateur, sur [https:]] par exemple, pour voir s’afficher ces tracés. Ils sont visibles ici en gris, sur un fond de plan classique :
© OpenStreetMap - geojson.io
C’est presque trop simple pour être tout à fait crédible ! Mais tout de même, ces 372 voies ne sont pas si loin de la réalité, qui correspond plutôt à 300 (soit 8 % des voies toulousaines), comme on va le voir par la suite.
Rassurez-vous, je vous épargne désormais les écritures SQL, que les curieux·ses pourront trouver dans ce classeur Observable.
Examinons de plus près ces rues de première extraction ; le début du fichier, classé par prénom, se présente plutôt bien :
Mais un peu plus loin, je constate qu’il y a du tri à faire :
Claude est en effet un des nombreux prénoms mixtes, comme Dominique, Dany ou Camille. Sur les 480 000 Claude né·es dans entre 1900 et 2020, 88 % étaient des garçons. Il est donc tentant de ne retenir pour notre recherche que les prénoms majoritairement féminins. Exit donc les Claude, Dominique ou Hyacinthe.
Le cas des Camille est intéressant et davantage épineux.
Ce prénom est devenu bien plus populaire pour les filles à partir des années 1980. Ce qui fait qu’il apparait dans notre sélection de prénoms : il est majoritairement féminin.
Source : Insee - outil interactif sur les prénoms
Pour l’ensemble de la France (données du répertoire national Fantoir), les voies reprenant ce prénom mettent en tête Camille Claudel, mais pour le reste citent exclusivement des hommes.
Ainsi, je vais devoir gérer dans ce cas une règle particulière : ne pas tenir compte des Camille dans ma recherche de voies féminines, sauf les « Camille Claudel ». Il en va de même pour George Sand.
D’autres prénoms féminins entrent en compétition avec des toponymes ou des articles : que l’on pense à l’occitan LOU (qui correspond à LE), à ETOILE, LORRAINE, NANCY, AVRIL, ALMA… Pour parfaire mon filtrage, je vais exclure de ma base de prénoms majoritairement féminins ces mots fréquemment rencontrés dans les noms de rues, mais probablement peu liés à des personnes. Dans le même temps, je veille à réintroduire quelques exceptions : outre Camille Claudel et George Sand précédemment évoqués, France Gall par exemple, ainsi que quelques rares prénoms locaux comme Géori ou Philadelphe.
Enfin, en l’absence de prénoms reconnus, certains titres comme COMTESSE (de Ségur), MADAME (de Sévigné), SOEUR fournissent de bons indices de la présence d’une femme.
300 voies féminines à Toulouse,
soit 8 % de l'ensemble des voiesC’est ainsi que j’en arrive à identifier 300 voies a priori évocatrices d’une femme, que l’on voit ici en rouge, sur cette carte interactive et zoomable :
/* [https:] */ .fullwidthx { width: 100vw; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; }© IGN - Toulouse métropole
import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@5/dist/runtime.js"; import define from "https://api.observablehq.com/@ericmauviere/cartographions-les-voies-se-referant-a-une-femme-a-toulouse@690.js?v=3"; new Runtime().module(define, name => { if (name === "viewof cat") return new Inspector(document.querySelector("#observablehq-viewof-cat-a40b5924")); if (name === "viewof search") return new Inspector(document.querySelector("#observablehq-viewof-search-a40b5924")); if (name === "viewof voies_femmes_sel2") return new Inspector(document.querySelector("#observablehq-viewof-voies_femmes_sel2-a40b5924")); if (name === "viewof map") return new Inspector(document.querySelector("#observablehq-viewof-map-a40b5924")); return ["map_p","fly_p"].includes(name); });Les autres catégories résultent d’un travail similaire mené sur les prénoms masculins. Une fois les voies féminines mises de côté, les voies restantes relèvent de 3 classes :
- Celles dont la dénomination comprend un prénom masculin ou un indice significatif (GENERAL, MAL, PRESIDENT, ABBE…)
- Celles ensuite qui excluent a priori la référence à une personne, par la présence, après le type de voie, de DU, DE, DES : BD DE STRASBOURG, CHE DE TUCAUT…
- Les autres au statut indéterminé, qui évoquent un homme, un lieu ou une profession : RUE LAFAYETTE, RUE MOLIERE, RUE MATABIAU, RUE PARGUAMINERES…
Ainsi, il apparait que les 8 % de femmes font face à une fourchette de 44 – 57 % d’hommes référencés, soit en gros 6 fois plus d’hommes que de femmes.
Source : Toulouse métropole - Insee © icem7
Il y a naturellement aussi quelques voies mixtes, Pierre et Marie Curie, Lucie et Raymond Aubrac par exemple, que je ne compte pas deux fois, les classant d’autorité dans la catégorie féminine !
Sur ces 300 voies féminines, une bonne vingtaine renvoient à un prénom seul dont certains sont identifiables (impasse Arletty, rue Colette, impasse Barbara) et d’autres non (impasse Matilda, rue Christiane, rue Sylvie, parc de Claire). 20 autres désignent une sainte.
Des voies aux caractéristiques particulièresToulouse, comme la plupart des grandes villes, conduit un effort de rééquilibrage. Depuis une dizaine d’années au moins, la commission de dénomination célèbre au moins autant de femmes que d’hommes. Il y a aussi ces professions emblématiques que je n’ai pas intégrées, comme les « Munitionnettes » de la Cartoucherie, ou les « Entoileuses » de Montaudran, chargées de recouvrir les avions de tissu.
Mais peu de voies sont débaptisées. Ce sont surtout les nouveaux quartiers, résidentiels ou d’activité, voire des aménagements routiers (bretelles) qui ouvrent des opportunités.
Ici, dans un nouveau quartier près du Zénith, la mixité des dénominations est assurée :
Source : Toulouse métropole - Insee © IGN
Mais des voies au statut plus incertain, sans adresse (points verts) identifiée, sont aussi utilisées :
Source : base adresse nationale © Etalab - OpenMapTiles - OpenStreetMap
J’ai relevé toutefois un cas de renommage en centre-ville : OpenStreetMap évoque toujours en 2023 la rue du Languedoc quand le début de celle-ci est devenu allée Gisèle Halimi en 2021.
© OpenStreetMap
© IGN Peu nombreuses, les voies féminines sont aussi plus courtes et plus modestes, plus représentées dans la catégorie des allées ou des ronds-points ; les boulevards ou les avenues sont quasiment inexistants.
Sources : Toulouse métropole - Insee La longueur moyenne d’une voie féminine est de 200 m, contre 300 m pour les masculines. La voie féminine la plus longue, le bd Florence Arthaud, parcourt 1,6 km, une trentaine de voies masculines sont plus longues, allant jusqu’à près de 4 km pour l’avenue du Général Eisenhower. Qu'en est-il ailleurs en France ?
Les données disponibles nationalement proviennent de deux sources :
- Fantoir, répertoire des voies et lieux-dits, produit par la direction générale des finances publiques (les Impôts), alimenté par la gestion du cadastre ;
- La base adresse nationale (BAN), qui référence toutes les adresses (soit x points par voie).
Les deux sont accessibles sous forme de fichiers ou d’API. La BAN est par nature bien plus lourde qu’un simple répertoire des voies. Et elle ne référence que celles qui ont des adresses. Par exemple, la rue Karen Blixen que nous avons rencontrée plus haut n’y figure pas.
Reste donc Fantoir, dont la base nationale est téléchargeable en open data depuis 2013, ce qui est à saluer ! En revanche, son format interne est difficile à décoder, j’ai donc converti la dernière version datée d’avril 2023 au format Parquet (ce qui permet aussi de réduire sa taille de 1 Go à 130 Mo).
En voici un extrait (pour Toulouse) :
Source : Fantoir/DGFIP
Son intérêt principal, c’est qu’il est national, il présente toutefois quelques limites par rapport au filaire de voies de Toulouse métropole :
- Ce n’est pas un fichier géographique, il ne comprend pas le tracé des voies ;
- Le champ libelle_voie est limité à 30 caractères, ce qui conduit à de fâcheuses abréviations qui peuvent affecter les prénoms. Comment deviner par exemple que les deux premières lignes évoquent une Anne-Marie et une Anne-Josèphe ?
- Il accuse, dans sa version open data, un retard d’environ une année.
Ceci explique qu’une vingtaine de voies manquent à l’appel quand je lui applique, pour Toulouse, mon programme d’identification des voies féminines (soit une sous-estimation de 7 %).
La moyenne nationale s’établit à 3,8 % de part de voies féminines. Je ne m’étends pas sur les comparaisons départementales, tant le degré d’urbanisation parait influer sur les résultats.
En revanche, certaines petites communes se détachent spectaculairement. J’ai par exemple repéré La Ville-aux-Dames, dans l’agglomération de Tours. Cette commune de 5 000 habitants a décidé en 1974 que toutes les rues porteraient des noms de femme, sauf exception, par exemple la place du 8 mai ou celle du 11 novembre.
La commune de Lisores, dans le pays d’Auge, berceau du peintre Fernand Léger, est devenue la cité des peintres, hommes et femmes. Son conseil a décidé en 2018 non seulement de respecter strictement la parité, mais de donner des noms de peintres à toutes ses voies (sauf exceptions toponymiques).
L’examen des plus grandes villes confirme que la taille a un effet sur la féminisationLe taux moyen monte en effet de 3,6 % à 5,5 % pour l’ensemble des villes de plus de 100 000 habitants, et 6,7 % pour les plus de 200 000.
Et dans cette dernière catégorie, Toulouse se classe plutôt bien, 3e derrière Rennes et Nantes.
Sources : Fantoir/DGFIP - Insee
Paris est proche de la moyenne, mais les disparités sont grandes entre ses arrondissements : moins de cinq références significatives dans le 8e ou le 9e, mais près de 10 % de voies féminines dans le 13e.
Quelles sont les personnalités les plus citées
dans nos rues ?Pour dresser ces tableaux pour les femmes et les hommes, j’ai dû prendre en compte de subtiles variations orthographiques. J’ai aussi choisi d’affecter PIERRE ET MARIE CURIE à MARIE CURIE (idem pour LUCIE AUBRAC, souvent associée à RAYMOND). Par ailleurs, Pasteur, Gambetta ou Foch sont souvent cités sans prénom, il faut donc les prendre en compte manuellement après examen du palmarès de tous les noms de voies en France.
Simone Veil est la personnalité la plus contemporaine à être honorée sur les plaques de nos rues, devant même François Mitterrand.
Quelles sont enfin les tendances récentes ?
Pour répondre à cette question, la présence dans Fantoir d’une colonne date_creation m’a paru prometteuse. Mais à l’examen, elle ressemble plus à une date de modification de l’enregistrement (elle est toujours supérieure à 1987), et celles-ci peuvent intervenir pour tout un tas de raisons liées aux évolutions du cadastre.
J’ai donc considéré les noms de voies qui n’étaient que peu représentés à la création du fichier, mais qui sont apparus depuis 2010. Là encore, il a fallu prendre en compte des variations orthographiques, voire des coquilles (Mitterrand étant par exemple parfois écrit avec un seul r).
Commençons donc par les hommes. Quand une personnalité éminente disparait, il est de coutume de la célébrer, entre autres sous la forme d’un odonyme. Et s’il s’agit d’un président, on n’hésitera pas à rebaptiser une voie prestigieuse. L’avenue Jacques Chirac remplace ainsi depuis peu, à Toulouse le boulevard des Crêtes. Toutefois, Jacques Chirac (40 voies à ce jour) et Valéry Giscard d’Estaing (25) n’ont pour l’heure pas connu de succès comparable à la seule année qui a suivi le décès de François Mitterrand.
Nelson Mandela illustre un cas différent et intéressant, il était déjà admiré et célébré de son vivant. Je ne m’attendais pas, enfin, à voir apparaitre dans ce palmarès le colonel Arnaud Beltrame, en 4e position.
Sources : Fantoir/DGFIP - Insee
Pour les femmes, Simone Veil domine ce palmarès des tendances depuis 2010, mais elle était déjà honorée avant son décès. Simone de Beauvoir et Olympe de Gouges restent des valeurs sûres, icônes du féminisme, gagnant même en popularité ces dix dernières années. La disparition brutale de Florence Arthaud, enfin, a provoqué une réelle émotion.
Sources : Fantoir/DGFIP - Insee
On le voit, tester la présence de prénoms permet de bien dégrossir le sujet, mais il reste pas mal de travail manuel pour ne pas rater ce qui apparait après coup comme des évidences. Les bases ont aussi leurs petits défauts, quand ce sont d’abord des bases de gestion, qu’il faut connaitre et savoir contourner.
Le prénom des gens est important, il les identifie et les humanise. Il devrait apparaitre systématiquement, sur les plaques comme dans les fichiers.
En dépit de mes réserves initiales, je dois remercier mon épouse de m’avoir plongé dans cette instructive exploration. Et tout autant les concepteurs de ce fabuleux outil qu’est DuckDB : sa souplesse et sa vélocité m’ont permis de pousser sans entraves tous mes questionnements et souhaits de vérification. Et enfin toutes celles et ceux qui œuvrent à mettre à disposition libre ces précieuses bases de données.
Pour en savoir plus- Classeur Observable avec requêtes et programmation de la cartographie
- La féminisation des noms de rue à Toulouse, c’est pour quand?? Médiacités
- Toulouse : place aux femmes dans les nouveaux noms de rue – France 3
- Archipel Citoyen propose de nouveaux noms de femmes pour les rues de Toulouse – Le journal toulousain
- Parité dans les noms donnés aux rues et son évolution ces dernières années – Christian Quest
- Vers l’automatisation d’une visualisation des hommages aux femmes dans la ville – Philippe Gambette
- Ces 200 personnalités sont les stars des rues françaises – Slate
- Comment nos rues se féminisent et s’internationalisent (lentement) – Slate
L’article Où sont les femmes dans les rues de Toulouse (et d’ailleurs) ? est apparu en premier sur Icem7.
-
13:57
Récréation – recréation sémiologique*
sur Icem7Les publications statistiques de la Drees sont très intéressantes sur le fond, mais j’ai parfois un peu de mal à comprendre rapidement le message des graphiques qu’elles présentent…
Cet article de juillet 2023 sur « les mesures socio-fiscales 2017-2022 » évoque un sujet majeur, celui du pouvoir d’achat, et la contribution des prestations sociales comme le RSA, les aides au logement ou la prime d’activité, à son évolution récente. Les impôts et les taxes sont également pris en compte : quand ils baissent, ils augmentent le « revenu disponible ».
Nous allons examiner deux graphiques de cette publication et voir comment les reconstruire de façon plus expressive.
Ce premier diagramme expose les différentes composantes du revenu disponible : salaire (quand la personne travaille), prestations sociales, impôts et taxes. Le graphique considère un « cas-type », celui de personnes seules locataires, à différents niveaux de salaire, y compris celles sans activité (pas de salaire).
Bien qu’évoquant une « décomposition », le graphique de la Drees met surtout l’accent, par des aplats de couleur tranchés, sur la différence entre salaire (ligne rouge) et revenu disponible (ligne noire). Cette représentation dérivée élève d’emblée le niveau d’exigence requis pour la bonne compréhension des concepts et de leur articulation.
La zone de croisement des courbes est un peu floue, il faut saisir que la surface bleue (impôts) vient se soustraire du salaire pour aboutir au revenu disponible.
Reconstruire posément avec un outil simple type DatawrapperCommençons par mettre à plat, de façon homogène, toutes les composantes du revenu disponible :
La Drees fournit les données détaillées avec l’article (elles vont même jusqu’à 2 smic), et j’utilise l’outil web Datawrapper dans sa version gratuite, par copier/coller du jeu de données.
Les aires (colorées de façon plus douce) traduisent clairement toutes les contributions, positives (salaire et prestations) ou négatives (impôt). Les trois grandes catégories se distinguent aisément par leur opposition chromatique (oranges, vert, gris).
Le seul tracé linéaire dessine la résultante, le revenu disponible. L’on devine intuitivement qu’il exprime la soustraction entre contributions positives et négative.
L’impôt apparait un peu avant (1,1 smic) que les prestations ne s’effacent (1,45 smic).
En inversant le placement de la prime d’activité et des aides au logement (AL), je mets mieux en évidence la quasi-constance des AL de 0 à 0,4 smic.
D’une façon générale, ramenant à une base horizontale un maximum de contributions (salaire, RSA, AL et impôt), leur évolution devient précisément perceptible.
Légende intégrée (les aires sont nommées au plus près), suppression du grisé d’arrière-plan, atténuation des grillages contribuent à la hiérarchisation des éléments du graphique, et donc à la lisibilité d’ensemble.
Pour parfaire le résultat, je réintègre les mentions obligatoires (source, définitions), ajoute un titre informatif et annote quelques points clés.
Ce graphique de synthèse traduit l’essentiel à retenir des mécanismes de compensation et d’amortissement à l’œuvre. Il est plus facile à mémoriser.
En réalisant ce travail sémiologique, j’ai enfin saisi l’articulation de concepts que je n’avais compris que partiellement jusqu’alors. Ces vagues qui se déploient et se succèdent deviennent tout naturellement esthétiques : la beauté nait de l’évidence.
C’est un autre graphique que la Drees a mis en avant sur les réseaux sociaux, car il relaie le titre et donc le message essentiel de l’étude : comment le pouvoir d’achat a-t-il évolué ces 5 dernières années selon le niveau de salaire ?
Si j’ai assez vite épinglé ce graphique dans mes « favoris », c’est qu’au bout de 10 secondes je n’avais toujours rien saisi, même pas un début de fil à tirer ! Ces empilements colorés flottaient devant mes yeux sans qu’aucune porte ne s’ouvre.
J’ai donc suivi la même démarche que précédemment, partant des données détaillées et jouant avec Datawrapper. La première action clarifiante consiste à ramener les données à comparer à une base (verticale) commune : les éléments ne flottent plus, le diagramme gagne en structure.
Après transposition, cette représentation quasi-brute proposée par l’outil graphique a commencé à me parler, des motifs et des regroupements naturels se laissent deviner.
Jacques Bertin : « Comprendre, c'est catégoriser »Ainsi, suivant le précepte bertinien du reclassement optimal des colonnes (les lignes sont déjà naturellement ordonnées), j’aboutis après quelques permutations à ceci :
Je place en premier l’indicateur de synthèse, l’évolution du revenu disponible. Ensuite, deux groupes de composantes s’ordonnent dans un sens que le choix des couleurs rehausse.
Il est d’abord manifeste que le revenu disponible a évolué en 5 ans de façon fort différente sous le smic et à partir d’un smic :
- Le maximum, près de 9 % de progression, s’observe pour les personnes seules au niveau du smic ; il est porté par la prime d’activité (forte revalorisation du bonus individuel en 2019).
- Au-dessus du smic, la progression du salaire net et surtout la diminution de la taxe d’habitation et de l’impôt sur le revenu prennent le relais : elles assurent une augmentation de près de 5 %.
- Sous le smic, la baisse sensible des aides au logement n’a pas été compensée par les « aides exceptionnelles » : il en résulte une diminution de pouvoir d’achat de l’ordre de -1,5 %.
Ces trois constats sont bien plus aisément perceptibles qu’avec le graphique originel. Un titre informatif peut les introduire, dans cette version finale :
Pour aller plus loin* : comme quoi parfois un accent fait toute la différence
L’article Récréation – recréation sémiologique* est apparu en premier sur Icem7.