Blog · Tutoriel · Outils

Interroger les données publiques avec DuckDB, sans serveur

Un fichier Parquet et un moteur SQL qui tient dans un exécutable : c'est assez pour analyser des millions de valeurs publiques sur un ordinateur portable, voire dans un navigateur. Mode d'emploi, avec le cube de Prisme comme terrain d'exercice.

Publié le 21 octobre 2026 · 7 min de lecture

Pourquoi Parquet plutôt que CSV

Un CSV est un texte : lisible partout, mais sans types, et à relire en entier à chaque requête. Un fichier Parquet range les données par colonnes, compressées et typées, avec des statistiques par bloc de lignes. Un moteur peut alors ne lire que les colonnes et les blocs dont il a besoin, y compris à distance, par requêtes HTTP partielles. Et un code commune comme 01001 y reste un texte : il ne perd pas son zéro initial, comme dans un tableur qui ouvre un CSV.

Sans rien installer : dans le navigateur

La console SQL de Prisme fait tourner DuckDB dans la page et lit les fichiers du portail de données. Chaque maille a sa table (cube_departement, cube_nuts2…), toutes en format long : une ligne par indicateur, territoire et année. Les dix départements au niveau de vie médian le plus élevé, par exemple (ouvrir dans la console) :

select geo_code, territoire, valeur as niveau_de_vie_median
from cube_departement
where indicateur_id = 'niveau_de_vie_median' and statut = 'ok'
  and annee = (select max(annee) from cube_departement where indicateur_id = 'niveau_de_vie_median')
order by valeur desc
limit 10

Dans le terminal

DuckDB s'installe en un seul exécutable. Il lit un fichier Parquet directement par son adresse : les versions récentes chargent seules l'extension qui gère le HTTP, et ne téléchargent que les parties utiles du fichier.

-- dans le terminal : duckdb
select territoire, annee, valeur, statut
from 'https://www.prisme-donnees.fr/donnees/cube-departement.parquet'
where indicateur_id = 'taux_pauvrete_60'
order by valeur desc nulls last
limit 10;

En Python et en R

Le même moteur existe en bibliothèque. En Python, le paquet duckdb renvoie un DataFrame pandas :

import duckdb

url = "https://www.prisme-donnees.fr/donnees/cube-departement.parquet"
tableau = duckdb.sql(f"""
    select geo_code, territoire, annee, valeur
    from '{url}'
    where indicateur_id = 'esperance_vie_femmes' and statut = 'ok'
""").df()  # un DataFrame pandas

En R, par l'interface DBI et le paquet duckdb :

library(DBI)
con <- dbConnect(duckdb::duckdb())
dbGetQuery(con, "
  select geo_code, territoire, annee, valeur
  from 'https://www.prisme-donnees.fr/donnees/cube-departement.parquet'
  where indicateur_id = 'part_logements_vacants' and statut = 'ok'
")

Trois règles pour des résultats justes

  • Filtrez sur le statut. Une case sans valeur porte la raison de son absence (secret statistique, trop peu de ventes, hors du champ de la source) : ne la comptez pas comme un zéro. Pour voir ce qui manque et pourquoi (ouvrir) :
select geo_niveau, statut, count(*) as cases
from cube
group by all
order by geo_niveau, cases desc
  • Choisissez l'année indicateur par indicateur. Tous ne sont pas publiés jusqu'à la même année : prenez la dernière année de chacun, comme dans les requêtes ci-dessus, plutôt qu'une année commune qui écarterait les plus récents.
  • Joignez sur les codes, pas sur les noms. Toutes les mailles françaises sont dans une même géographie de référence : un code département ou commune désigne le même territoire dans tous les indicateurs. Pour croiser deux indicateurs, une jointure sur geo_code suffit (exemple : années de revenu pour 50 m²).
with prix as (
  select geo_code, territoire, valeur as prix_m2
  from cube_departement
  where indicateur_id = 'prix_m2_median_appartement' and statut = 'ok'
    and annee = (select max(annee) from cube_departement where indicateur_id = 'prix_m2_median_appartement')
),
revenus as (
  select geo_code, valeur as niveau_de_vie
  from cube_departement
  where indicateur_id = 'niveau_de_vie_median' and statut = 'ok'
    and annee = (select max(annee) from cube_departement where indicateur_id = 'niveau_de_vie_median')
)
select prix.geo_code, territoire, prix_m2, niveau_de_vie,
  round(prix_m2 * 50 / niveau_de_vie, 1) as annees_pour_50m2
from prix inner join revenus using (geo_code)
order by annees_pour_50m2 desc

Chaque indicateur garde la licence de son producteur, indiquée dans le dictionnaire du portail : citez la source et le millésime. Les définitions des colonnes et des statuts sont sur la page du portail de données ; les mots du domaine, dans le glossaire.

Questions fréquentes

Peut-on lire un fichier Parquet sans le télécharger ?

Oui : DuckDB lit un fichier Parquet par son adresse web et ne récupère, par requêtes HTTP partielles, que les colonnes et les blocs de lignes dont la requête a besoin.

Faut-il un serveur de base de données pour analyser des données publiques ?

Non : DuckDB est un moteur SQL embarqué, sans serveur, qui fonctionne dans un terminal, dans Python ou R, et même dans un navigateur.

Pourquoi préférer Parquet au CSV ?

Parce qu'il est typé, compressé et rangé par colonnes : plus léger, plus rapide à interroger, et il ne déforme pas les codes, comme le zéro initial d'un code commune.