Statistiques descriptives et tests de normalité
Calculez l’asymétrie et l’aplatissement avec scipy.stats, exécutez un test de Shapiro-Wilk pour la normalité et interprétez la p-valeur.
Statistiques descriptives et tests de normalité est une leçon Pandas & NumPy Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Pandas & NumPy Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Pandas & NumPy Academy comprend 4 leçons au total.
Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.
Descriptive vs Inferential Statistics
Descriptive statistics summarise what is in your data: mean, median, standard deviation, skewness. Inferential statistics make claims about a population based on a sample: hypothesis tests, confidence intervals, p-values. SciPy's scipy.stats module bridges these worlds — it provides both descriptive measures that go beyond Pandas' describe() and the full suite of classical hypothesis tests. Combining Pandas for data manipulation and SciPy for statistical testing is the standard Python data science workflow.
Extended Descriptive Statistics
Pandas describe() gives count, mean, std, min/max, and quartiles. scipy.stats adds skewness (asymmetry of the distribution) and kurtosis (tail heaviness). Skewness of 0 means symmetric; positive skewness means a longer right tail (many small values, few very large). Kurtosis of 3 (or 0 in excess form) is normal; higher values indicate heavier tails with more extreme outliers than a normal distribution would produce.
import pandas as pd
from scipy import stats
import numpy as np
np.random.seed(0)
data = np.concatenate([
np.random.exponential(scale=2, size=500), # right-skewed
np.random.normal(loc=5, scale=1, size=500)
])
print('Mean:', round(data.mean(), 3))
print('Std:', round(data.std(), 3))
print('Skewness:', round(stats.skew(data), 3))
print('Kurtosis (excess):', round(stats.kurtosis(data), 3))Understanding Skewness
Skewness matters for choosing statistical tests and transformations. A right-skewed (positive skew) distribution has its mean larger than its median, typical of income data, reaction times, and sales amounts. A left-skewed (negative skew) distribution has its mean smaller than its median. Rule of thumb: |skewness| < 0.5 is approximately symmetric; 0.5–1.0 is moderately skewed; > 1.0 is highly skewed and may benefit from a log or square-root transformation before applying tests that assume normality.
import numpy as np
from scipy import stats
# Right-skewed: income-like data
right_skewed = np.random.lognormal(mean=1, sigma=1, size=1000)
print('Right skew:', round(stats.skew(right_skewed), 3))
# After log transform
print('After log transform:', round(stats.skew(np.log(right_skewed)), 3))
# Symmetric normal
normal_data = np.random.normal(0, 1, 1000)
print('Normal skew:', round(stats.skew(normal_data), 3))Why Normality Testing Matters
Many statistical tests — t-tests, ANOVA, Pearson correlation — assume that the data (or the residuals) follow a normal (Gaussian) distribution. If this assumption is violated with small samples, the test's p-values may be inaccurate. Normality testing checks whether the assumption holds. For large samples (n > 100), normality tests become very sensitive and will almost always reject normality for trivial deviations — in that case, rely on the Central Limit Theorem (sample means are approximately normal) rather than testing the raw data.
The Shapiro-Wilk Test
The Shapiro-Wilk test (scipy.stats.shapiro(data)) is the most powerful normality test for sample sizes up to about 5,000. It returns a W-statistic and a p-value. If p > 0.05, you fail to reject normality — the data is consistent with a normal distribution. If p ≤ 0.05, you reject normality. Remember: failing to reject normality does not prove the data is normal; it just means you do not have enough evidence to say it is not.
import numpy as np
from scipy import stats
np.random.seed(42)
# Normal data
normal = np.random.normal(0, 1, 100)
stat, p = stats.shapiro(normal)
print(f'Normal data: W={stat:.4f}, p={p:.4f}')
print('Conclusion:', 'Looks normal' if p > 0.05 else 'Not normal')
# Skewed data
skewed = np.random.exponential(1, 100)
stat2, p2 = stats.shapiro(skewed)
print(f'Skewed data: W={stat2:.4f}, p={p2:.4f}')
print('Conclusion:', 'Looks normal' if p2 > 0.05 else 'Not normal')The Kolmogorov-Smirnov Test
The Kolmogorov-Smirnov (K-S) test (scipy.stats.kstest(data, 'norm', args)) tests whether a sample follows a specified distribution. Unlike Shapiro-Wilk, it works for any distribution (not just normal) and for large samples. It computes the maximum difference between the empirical CDF of your data and the theoretical CDF. A variation, the two-sample K-S test (stats.ks_2samp(a, b)), tests whether two samples come from the same distribution — useful for comparing before/after distributions.
import numpy as np
from scipy import stats
np.random.seed(0)
data = np.random.normal(loc=5, scale=2, size=200)
# One-sample K-S test against N(5, 2) distribution
stat, p = stats.kstest(data, 'norm', args=(5, 2))
print(f'K-S test: stat={stat:.4f}, p={p:.4f}')
print('Follows N(5,2)?', 'Yes' if p > 0.05 else 'No')
# Two-sample K-S test
data2 = np.random.normal(loc=5.5, scale=2, size=200)
stat2, p2 = stats.ks_2samp(data, data2)
print(f'Two-sample K-S: stat={stat2:.4f}, p={p2:.4f}')Visual Normality Check: QQ Plot
The Q-Q (quantile-quantile) plot is a visual normality check: it plots the quantiles of your data against the quantiles of a normal distribution. If the data is normal, the points fall on a straight diagonal line. Deviations from the line indicate departure from normality — S-curves indicate kurtosis, bends indicate skewness. Statistical tests tell you if deviations are significant; Q-Q plots show you the nature and location of deviations. Use scipy.stats.probplot() to generate Q-Q plot data.
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.random.exponential(2, 200)
# Q-Q plot
(osm, osr), (slope, intercept, r) = stats.probplot(data, dist='norm')
print(f'R-squared of Q-Q fit: {r**2:.4f}') # Close to 1 = normal
# Visual inspection: if plotting, points on the line = normal
# plt.figure()
# stats.probplot(data, dist='norm', plot=plt)
# plt.show()The Central Limit Theorem in Practice
The Central Limit Theorem (CLT) states that the distribution of sample means approaches normality as sample size grows, regardless of the underlying distribution. For n ≥ 30, the sample mean is approximately normal even if individual observations are skewed. This is why t-tests are robust for large samples: you are testing whether the mean differs, and the mean is approximately normal even when the raw data is not. For small samples from non-normal populations, use non-parametric tests instead.
import numpy as np
from scipy import stats
np.random.seed(0)
# Population: heavily right-skewed (exponential)
population = np.random.exponential(scale=1, size=10000)
print('Population skewness:', round(stats.skew(population), 3))
# Sample means are approximately normal (CLT)
sample_means = [np.random.choice(population, 50).mean()
for _ in range(1000)]
print('Sample means skewness:', round(stats.skew(sample_means), 3))
_, p = stats.shapiro(sample_means)
print('Shapiro p-value for means:', round(p, 4))Normality by Group
In group comparison tests (t-test, ANOVA), normality is required within each group, not in the overall data. When testing whether sales differ by region, test the normality of sales within each region separately. A distribution that is not normal overall can still satisfy normality within subgroups. Apply stats.shapiro() to each group using Pandas groupby() and iterate over groups to check the assumption systematically.
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(0)
df = pd.DataFrame({
'region': ['N']*50 + ['S']*50 + ['E']*50,
'sales': np.concatenate([
np.random.normal(100, 20, 50),
np.random.normal(110, 25, 50),
np.random.normal(95, 15, 50)
])
})
for region, group in df.groupby('region'):
stat, p = stats.shapiro(group['sales'])
print(f'Region {region}: W={stat:.4f}, p={p:.4f} -> '
f'{"Normal" if p>0.05 else "Not normal"}')Non-Parametric Alternatives
When normality is violated, use non-parametric tests that make no distributional assumptions. The Mann-Whitney U test (stats.mannwhitneyu) replaces the independent t-test; the Wilcoxon signed-rank test (stats.wilcoxon) replaces the paired t-test; the Kruskal-Wallis test (stats.kruskal) replaces one-way ANOVA. These tests use ranks instead of raw values and are robust to outliers, but they have less statistical power than their parametric counterparts when normality actually holds.
import numpy as np
from scipy import stats
np.random.seed(0)
# Non-normal data
group_a = np.random.exponential(2, 40)
group_b = np.random.exponential(2.5, 40)
# Parametric would be wrong here; use non-parametric
stat, p = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')
print(f'Mann-Whitney U test: U={stat:.1f}, p={p:.4f}')
print('Groups differ?' , 'Yes' if p < 0.05 else 'No')Summarising Stats for Multiple Columns
In EDA, you often need to check normality and skewness for all numeric columns at once. Combine Pandas select_dtypes with a loop over columns calling stats.shapiro() and stats.skew(). Build a summary DataFrame with columns for skewness, kurtosis, and Shapiro p-value to get a birds-eye view of which columns are non-normal and need transformation before modelling. This is part of a systematic data quality checklist.
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(0)
df = pd.DataFrame({
'age': np.random.normal(35, 10, 200),
'income': np.random.lognormal(10, 1, 200),
'score': np.random.uniform(0, 100, 200)
})
rows = []
for col in df.select_dtypes(include='number').columns:
s = stats.skew(df[col])
_, p = stats.shapiro(df[col][:200])
rows.append({'column': col, 'skewness': round(s, 3),
'shapiro_p': round(p, 4), 'normal': p > 0.05})
print(pd.DataFrame(rows).to_string(index=False))Quick Check
Test your understanding of Data Analysis concepts from this lesson.
Lesson Recap
In this lesson you learned: scipy.stats.skew() and kurtosis() describe the shape of a distribution beyond what describe() provides, scipy.stats.shapiro() tests normality with p > 0.05 meaning no evidence against normality, and the Central Limit Theorem makes t-tests robust for large samples even with non-normal data. Next up we apply hypothesis testing with t-tests to compare group means.
Questions Fréquemment Posées
La leçon « Statistiques descriptives et tests de normalité » est-elle gratuite ?
Oui — le texte complet de « Statistiques descriptives et tests de normalité » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Pandas & NumPy Academy, passe à CoddyKit PRO. Le cours Pandas & NumPy Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Statistiques descriptives et tests de normalité » ?
Calculez l’asymétrie et l’aplatissement avec scipy.stats, exécutez un test de Shapiro-Wilk pour la normalité et interprétez la p-valeur. Tu pratiques Pandas & NumPy Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Pandas & NumPy Academy ?
Aucune expérience préalable n'est requise. Pandas & NumPy Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Statistiques descriptives et tests de normalité » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Pandas & NumPy Academy ?
Oui. Chaque leçon Pandas & NumPy Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Statistiques descriptives et tests de normalité
- Tests t pour comparer des moyennes
- Test du khi carré d’indépendance
- ANOVA et tests post-hoc