En construisant une petite pipeline réutilisable, pas en empilant des df.describe() à la main. Je vous montre comment cadrer les statistiques descriptives Python, détecter les valeurs manquantes, intégrer les variables catégorielles et préparer des tableaux propres, partageables, sans perdre du temps à chaque nouveau dataset.
Pourquoi automatiser ces statistiques ?
J’automatise les statistiques descriptives parce que refaire à la main df.describe(), df.isna().sum() et groupby().agg() sur chaque dataset finit toujours par créer des oublis, des variantes et des tableaux pas comparables.
Au début, ça paraît simple. On ouvre un notebook, on tape trois commandes, on regarde deux moyennes, trois valeurs manquantes, un petit groupby, et on se dit que c’est réglé. Puis arrive le deuxième fichier. Puis le troisième. Il faut ajouter le taux de valeurs manquantes, séparer les variables numériques et catégorielles, vérifier les doublons, exporter un résumé propre, l’envoyer à un collègue. Et là, le petit bricolage rapide devient fragile.
Le vrai problème, ce n’est pas de calculer une moyenne. Python sait très bien le faire. Le problème, c’est de produire un diagnostic lisible, constant et partageable. Si chaque analyse a son propre bout de code, son propre format et ses propres exceptions, vous perdez du temps à relire, comparer, corriger. Et parfois vous prenez une décision sur un tableau incomplet, juste parce qu’une cellule de notebook n’a pas été relancée.
L’objectif ici, c’est de passer d’une logique de snippets ad hoc à une pipeline descriptive réutilisable en Python. Un snippet ad hoc, c’est un bout de code écrit pour un cas précis, souvent utile sur le moment, mais rarement robuste dans le temps. Une pipeline, c’est une suite d’étapes reproductibles qui prennent un DataFrame en entrée et sortent un résumé fiable.
Comme on dit à Brive, un bon plan de marquage vaut mieux qu’un bon reporting ! Si besoin, consultez moi - faites appel à un super consultant en tracking client et server side.
Je vais utiliser le dataset Palmer Penguins comme fil rouge. Il est pratique pour ça :
- Il est petit, donc on comprend vite ce qu’on manipule.
- Il est ouvert, donc tout le monde peut le récupérer facilement.
- Il mélange des variables numériques et catégorielles.
- Il contient des valeurs manquantes.
- Il permet de tester une variable de regroupement, comme l’espèce ou l’île.
Chez des clients, je vois souvent la même chose. Les équipes savent faire l’analyse, aucun souci là-dessus. Mais trois semaines plus tard, personne n’arrive à la reproduire proprement, parce que tout est resté dans un notebook improvisé, avec des cellules exécutées dans le désordre et deux versions du même tableau.
Avant d’automatiser le résumé, je dois donc savoir précisément ce que contient mon DataFrame.
Comment préparer le dataset ?
Je prépare le dataset en installant les librairies utiles, en chargeant les données, puis en inspectant la taille, les types de colonnes et les valeurs nulles.
Pour automatiser les statistiques descriptives en Python, je pars avec une petite stack assez simple. Pandas sert à manipuler les données. Seaborn permet de charger facilement le dataset Palmer Penguins. Skimpy donne des résumés rapides et lisibles. Tableone aide à produire des tableaux descriptifs structurés, souvent utiles en santé ou en analyse métier. Great-tables sert à mettre en forme des tableaux propres. Fg-data-profiling permet de générer un profil de données plus complet, avec distributions, valeurs manquantes et alertes.
Pour installer tout ça, je lance simplement : pip install pandas seaborn skimpy tableone great-tables fg-data-profiling.
Petite remarque importante sur le profiling. L’écosystème a changé de nom au fil du temps, avec pandas-profiling, puis ydata-profiling. Pour un nouveau projet, je peux partir sur fg-data-profiling si c’est le package retenu dans la stack. Le plus important, c’est d’être cohérent avec l’environnement du projet, surtout si le code doit tourner en équipe ou en production.
import seaborn as sns
df = sns.load_dataset('penguins')
Le dataset contient 344 lignes et 7 colonnes. Les colonnes species, island et sex sont catégorielles. Les colonnes bill_length_mm, bill_depth_mm, flipper_length_mm et body_mass_g sont numériques.
Je regarde ensuite les valeurs manquantes. Ici, on a species 0, island 0, bill_length_mm 2, bill_depth_mm 2, flipper_length_mm 2, body_mass_g 2, sex 11. Cette étape compte vraiment. Si je ne mesure pas les nulls dès le départ, mes statistiques peuvent être techniquement correctes, mais mal interprétées. J’ai déjà vu ça chez un client : une moyenne propre, un tableau nickel, mais basé sur un sous-ensemble incomplet sans que personne ne l’ait vu.
| Contrôle | Résultat attendu | Utilité |
| Taille du dataset | 344 lignes, 7 colonnes | Savoir sur quel volume je travaille |
| Types de colonnes | Catégorielles et numériques identifiées | Choisir les bonnes statistiques |
| Valeurs nulles | Nulls repérés par colonne | Éviter les mauvaises interprétations |
Que vaut df.describe au départ ?
df.describe() est un bon point de départ, mais ce n’est pas une vraie pipeline de statistiques descriptives.
Quand je lance df.describe().round(2) sur Palmer Penguins, Pandas me renvoie un résumé des colonnes numériques. Typiquement les mesures sur la longueur du bec, la profondeur du bec, la longueur des nageoires, la masse corporelle. Le round(2) arrondit à deux décimales, et franchement ça suffit dans 95% des cas pour lire vite sans se noyer dans des valeurs du genre 43.921929824.
Le résumé donne une première lecture assez saine des données :
- Le count indique le nombre de valeurs non manquantes.
- La mean, c’est la moyenne, donc une idée rapide de la tendance centrale.
- La std, pour standard deviation, mesure la dispersion autour de la moyenne.
- Le min et le max donnent l’étendue des valeurs observées.
- Les 25%, 50%, 75% sont les quartiles. Le 50%, c’est la médiane. Ça aide à comprendre où se situe le cœur de la distribution.
Donc oui, en une ligne, j’ai une vision rapide de la tendance centrale, de la dispersion et de l’étendue des valeurs. C’est pratique. C’est même souvent mon premier réflexe quand j’ouvre un dataset.
df.describe().round(2)
Mais il faut être lucide. Par défaut, df.describe() ignore les variables catégorielles comme l’espèce, l’île ou le sexe. Il ne montre pas directement le pourcentage de valeurs manquantes. Et il reste sur un résumé très standard.
Petit exemple concret. Si une colonne contient 344 lignes au total mais 2 valeurs manquantes, count va afficher 342. Techniquement l’info est là. Mais sans calcul explicite du taux de nulls, un lecteur pressé peut complètement passer à côté. Deux valeurs manquantes, ça fait environ 0,58%. Sur un petit dataset, ça se voit encore. Sur un gros dataset, ça disparaît vite dans le bruit.
Autre limite : df.describe() ne donne pas directement la skewness, c’est-à-dire l’asymétrie de la distribution, ni la kurtosis, qui aide à lire si les valeurs sont très concentrées ou avec des queues plus extrêmes. Il ne donne pas non plus une vraie lecture de la forme de distribution.
Moi, je garde df.describe() comme baseline, pas comme livrable final. C’est le premier réflexe, pas la fin du travail. Maintenant que cette base est claire, je peux élargir le résumé avec describe(include=’all’), puis passer aux agrégations personnalisées pour intégrer les catégories et les groupes.
Comment inclure les catégories ?
J’inclus les catégories avec df.describe(include=’all’), puis je complète avec des agrégations ciblées quand j’ai besoin d’un résumé plus métier. C’est souvent le bon compromis : une vue rapide pour explorer, puis une vue propre pour décider.
Avec df.describe(include=’all’).round(2), Pandas ne limite plus le résumé aux colonnes numériques. Les colonnes catégorielles apparaissent aussi, avec des champs comme count, unique, top et freq. Les colonnes numériques, elles, gardent les statistiques classiques : moyenne, écart-type, minimum, quartiles, maximum.
Pour les catégories, c’est simple. Count indique le nombre de valeurs non nulles. Unique indique le nombre de modalités différentes. Top donne la modalité la plus fréquente. Freq donne sa fréquence. Sur un dataset comme les pingouins, ça parle tout de suite : species montre l’espèce la plus présente, island l’île la plus représentée, sex le sexe le plus fréquent dans les données disponibles.
Le petit piège, c’est que ce tableau mélange tout. Les indicateurs numériques et catégoriels se retrouvent dans la même sortie. C’est pratique pour jeter un œil, je l’utilise souvent au début d’une analyse, mais ce n’est pas toujours le plus clair quand il faut prendre une décision ou partager un résultat.
Quand je veux un résumé plus maîtrisé, je passe par .agg(). Ça me permet de choisir exactement les contrôles que je veux afficher : moyenne, écart-type, minimum, maximum, nombre de valeurs non nulles, nombre de valeurs manquantes. Pour les valeurs manquantes, j’utilise souvent une petite fonction avec isna().sum().
df.agg({
'body_mass_g': ['count', 'mean', 'std', 'min', 'max'],
'bill_length_mm': ['count', 'mean', 'std', 'min', 'max']
}).round(2)
L’analyse devient vraiment utile avec groupby().agg(). Une moyenne globale peut masquer des écarts importants entre groupes. Regrouper par species, island ou sex, ça permet de voir si les comportements changent selon les catégories.
df.groupby('species').agg({
'body_mass_g': ['count', 'mean', 'std', 'min', 'max'],
'bill_length_mm': ['mean', 'std']
}).round(2)
Les colonnes choisies s’adaptent au dataset. Une bonne pipeline ne cherche pas à tout afficher. Elle affiche toujours les mêmes contrôles essentiels, puis elle laisse la place aux regroupements vraiment utiles.
Comment rendre le résumé partageable ?
Je rends le résumé partageable en standardisant la sortie, en séparant les types de variables, et en formatant les tableaux pour qu’ils puissent être relus hors du notebook.
Un bon résumé descriptif automatisé doit produire plusieurs blocs cohérents. Pas juste un describe() balancé au milieu d’un notebook. J’aime avoir les dimensions du dataset, les types de colonnes, les valeurs manquantes, les statistiques numériques, le résumé des variables catégorielles, et si besoin les statistiques par groupe.
C’est ce découpage qui rend le résultat lisible. Votre collègue peut ouvrir le fichier, comprendre ce qui a été contrôlé, et refaire la même analyse plus tard sur une nouvelle extraction.
Il y a quelques outils utiles, sans magie derrière. Skimpy donne une vue rapide plus riche qu’un describe brut. Tableone sert à construire des tableaux descriptifs structurés, souvent pratiques en santé ou en analyse métier. Great-tables aide à produire des tableaux mieux présentés. Fg-data-profiling peut générer un profil plus complet du dataset.
Ces outils ne remplacent pas la réflexion. Ils évitent surtout de recoder les mêmes contrôles à chaque projet. Le vrai gain, c’est la répétabilité. Chez un client, on avait trois analystes qui produisaient trois résumés différents sur le même fichier. Le problème n’était pas Python, c’était l’absence de format commun.
Je pars souvent sur une fonction simple comme ça :
def build_descriptive_summary(df, group_col=None):
numeric_cols = df.select_dtypes(include="number").columns
categorical_cols = df.select_dtypes(exclude="number").columns
overview = {
"rows": len(df),
"columns": df.shape[1]
}
missing_values = df.isna().sum().sort_values(ascending=False)
numeric_summary = df[numeric_cols].describe().T
categorical_summary = df[categorical_cols].describe().T
grouped_summary = None
if group_col:
grouped_summary = df.groupby(group_col)[numeric_cols].describe()
return {
"overview": overview,
"missing_values": missing_values,
"numeric_summary": numeric_summary,
"categorical_summary": categorical_summary,
"grouped_summary": grouped_summary
}
Je préfère souvent commencer simple avec pandas, puis ajouter la mise en forme quand le contenu est stable. Sinon on passe trop vite du temps sur un joli tableau qui résume mal les données.
| Besoin | Méthode pandas ou package | Sortie attendue |
| Valeurs manquantes | df.isna().sum() | Tableau des colonnes avec le nombre de valeurs manquantes |
| Statistiques numériques | df.describe() ou skimpy | Moyenne, médiane, écart-type, min, max, quartiles |
| Variables catégorielles | value_counts(), describe() ou tableone | Fréquences, modalités principales, nombre de catégories |
| Comparaison par groupe | groupby() ou tableone | Résumé séparé par segment, classe, pays ou cohorte |
| Rapport partageable | great-tables ou fg-data-profiling | Tableau propre ou rapport exportable et relisible |
On automatise ça dès le prochain dataset ?
Automatiser les statistiques descriptives Python, ce n’est pas compliquer l’analyse. C’est arrêter de refaire les mêmes commandes à la main et obtenir un résumé propre dès le chargement d’un dataset. Je pars d’une inspection simple, je garde df.describe() comme baseline, j’ajoute les catégories avec include=’all’, puis je structure les agrégations et les groupby selon le besoin. Avec Palmer Penguins, on voit bien l’intérêt : numériques, catégories, nulls, groupes, tout y est. Le bénéfice pour vous est très concret : moins d’oublis, des analyses plus reproductibles, et des tableaux plus faciles à partager.
FAQ
- Pourquoi automatiser les statistiques descriptives en Python ?
Pour éviter de refaire les mêmes commandes à la main sur chaque dataset. Une pipeline permet de produire toujours les mêmes contrôles : dimensions, types, valeurs manquantes, statistiques numériques, catégories et regroupements. C’est plus fiable, plus rapide et plus simple à partager. - df.describe() suffit-il pour analyser un dataset ?
df.describe() est utile pour démarrer, surtout sur les colonnes numériques. Mais seul, il reste limité. Par défaut, il ne résume pas les colonnes catégorielles, ne calcule pas directement le taux de valeurs manquantes et ne décrit pas vraiment la forme des distributions. - Comment inclure les variables catégorielles dans le résumé ?
Avec df.describe(include=’all’), pandas ajoute les colonnes catégorielles au résumé. On obtient notamment count, unique, top et freq. C’est pratique pour explorer vite des variables comme species, island ou sex dans le dataset Palmer Penguins. - Quel dataset utiliser pour tester une pipeline descriptive ?
Le dataset Palmer Penguins est un bon choix. Il est petit, ouvert, facile à charger avec seaborn, et contient à la fois des colonnes numériques, des colonnes catégorielles, des valeurs manquantes et des variables utiles pour faire des groupby. - Quels packages Python peuvent aider à produire un résumé partageable ?
pandas reste la base. seaborn permet de charger facilement le dataset d’exemple. skimpy, tableone, great-tables et fg-data-profiling peuvent aider à enrichir, structurer ou formater les sorties. Le bon réflexe, c’est de stabiliser d’abord les contrôles utiles, puis d’améliorer la présentation.
A propos de l’auteur
Je suis Franck Scandolera, expert et formateur en Data, IA, tracking avancé server-side, Analytics Engineering, automatisation No/Low Code avec n8n, SEO et GEO. J’accompagne des équipes qui veulent fiabiliser leurs données, automatiser leurs analyses et intégrer l’IA dans leurs process sans empiler des outils au hasard. Je dirige l’agence webAnalyste et l’organisme Formations Analytics. J’ai travaillé avec des références comme Logis Hôtel, Yelloh Village, BazarChic, la Fédération Française de Football ou Texdecor. Si vous voulez industrialiser vos analyses ou vos automatisations business, contactez-moi.
⭐ Data Analyst, Analytics Engineer et expert dans l’automatisation IA ⭐
Ref clients : Logis Hôtel, Yelloh Village, BazarChic, Fédération Football Français, Texdecor…
Mon terrain de jeu :
Data Analyst & Analytics engineering : tracking propre RGPD, entrepôt de données (GTM server, BigQuery…), modèles (dbt/Dataform), dashboards décisionnels (Looker, SQL, Python).
Automatisation IA des taches Data, Marketing, RH, compta etc : conception de workflows intelligents robustes (n8n, Make, App Script, scraping) connectés aux API de vos outils et LLM (OpenAI, Mistral, Claude…).
Engineering IA pour créer des applications et agent IA sur mesure : intégration de LLM (OpenAI, Mistral…), RAG, assistants métier, génération de documents complexes, APIs, backends Node.js/Python.





