# đź“‹ Python et Data
> [!note] Objectifs du chapitre
> À la fin de ce chapitre, vous serez capable de :
> - Comprendre l'utilité des notebooks interactifs pour la Data Science.
> - Maîtriser les bases de la bibliothèque `pandas` pour la manipulation de données tabulaires.
> - Charger et créer des DataFrames.
> - Réaliser une première exploration et visualisation d'un DataFrame pour en comprendre la structure et le contenu.
> - Accéder et sélectionner des données spécifiques (colonnes et lignes).
# Introduction Ă Python pour la Data Science
Ce cours est conçu pour vous fournir les compétences fondamentales nécessaires pour manipuler, explorer et comprendre des données à l'aide de Python, le langage de programmation le plus populaire dans ce domaine.
Vous avez déjà acquis une base solide en programmation Python. Aujourd'hui, nous allons étendre ces connaissances en vous introduisant aux outils et concepts spécifiques à la Data Science. Nous allons explorer comment organiser vos analyses de manière efficace, comment structurer les données tabulaires et comment en obtenir un premier aperçu significatif.
# L'environnement de travail : Les Notebooks interactifs
Pour la Data Science, l'environnement de travail est crucial. Les *notebooks* (comme Jupyter Notebook ou Google Colab) sont devenus le standard de facto grâce à leur capacité à combiner code, texte explicatif, visualisations et résultats dans un seul document interactif.
## Qu'est-ce qu'un Notebook ?
Un notebook est un document interactif qui contient :
- **Cellules de code** : Où vous écrivez et exécutez du code Python (ou d'autres langages).
- **Cellules Markdown** : Où vous écrivez du texte formaté (titres, listes, gras, italique) pour documenter votre travail.
- **Sorties** : Les résultats de l'exécution du code, y compris les tableaux, les graphiques et les messages d'erreur.
> [!note] Avantages des Notebooks pour la Data Science
> - **Interactivité** : Exécutez le code cellule par cellule, explorez les données pas à pas.
> - **Reproductibilité** : Le code et son explication sont au même endroit, facilitant la compréhension et la reproduction des analyses.
> - **Communication** : Partagez vos analyses complètes avec des collègues, y compris les explications et les visualisations.
> - **Exploration itérative** : Modifiez rapidement le code et observez les effets sans relancer tout le script.
## Utilisation basique d'un Notebook (Jupyter)
Un notebook est composé de "cellules". Vous pouvez ajouter, supprimer, déplacer et exécuter des cellules.
- **Exécuter une cellule** : Sélectionnez la cellule et appuyez sur `Shift + Entrée`.
- **Ajouter une cellule** : Utilisez le menu `Insert` ou les boutons `+` dans la barre d'outils.
- **Changer le type de cellule** : Utilisez le menu déroulant dans la barre d'outils (Code, Markdown).
> [!tip] Choisir son environnement
> - **Jupyter Notebook / JupyterLab** : Idéal pour une installation locale, offre une grande flexibilité. Nécessite une installation Python (`pip install jupyterlab`).
> - **Google Colab** : Excellent pour débuter, ne nécessite aucune installation locale, fonctionne dans le navigateur, offre un accès gratuit aux GPU/TPU (limité). Parfait pour partager et collaborer.
> - **VS Code** : Intègre nativement le support des notebooks Jupyter, offrant une expérience de développement complète.
# La bibliothèque `pandas` : Le couteau suisse de la Data Science
`pandas` est une bibliothèque Python open-source qui fournit des structures de données et des outils d'analyse de données de haute performance et faciles à utiliser. C'est l'outil indispensable pour travailler avec des données tabulaires (similaires à des feuilles de calcul ou des tables de bases de données).
## Pourquoi `pandas` ?
`pandas` excelle dans la manipulation de données structurées. Ses deux structures de données principales, `Series` et `DataFrame`, sont optimisées pour :
- Le chargement et l'enregistrement de données depuis divers formats (CSV, Excel, SQL, JSON, etc.).
- La gestion des données manquantes.
- La sélection, le filtrage et la manipulation de données.
- L'agrégation et la transformation de données.
- La fusion et la jointure de jeux de données.
## Installation et importation
Si vous utilisez un environnement comme Google Colab ou Anaconda, `pandas` est généralement déjà installé. Sinon, vous pouvez l'installer via pip :
```bash
pip install pandas
```
Pour utiliser `pandas` dans votre code, il est de coutume de l'importer avec l'alias `pd` :
```python
import pandas as pd
```
## Structures de données fondamentales
`pandas` introduit deux structures de données clés : `Series` et `DataFrame`.
> [!definition] Series
> Une `Series` est un tableau unidimensionnel étiqueté capable de contenir n'importe quel type de données (entiers, chaînes de caractères, nombres flottants, objets Python, etc.). Elle est similaire à une colonne dans une feuille de calcul ou une base de données, ou à un tableau NumPy unidimensionnel avec des étiquettes d'index.
>
> $ S = [s_0, s_1, s_2, ..., s_n] $
> Chaque élément $s_i$ est associé à un index.
> [!definition] DataFrame
> Un `DataFrame` est une structure de données tabulaire à deux dimensions, avec des colonnes et des lignes étiquetées. Il peut être vu comme un dictionnaire de `Series` partageant le même index. C'est la structure de données la plus couramment utilisée en Data Science avec `pandas`.
>
> $ DF = \begin{pmatrix}
> C_1 & C_2 & \dots & C_m \\
> r_1 & \dots & \dots & \dots \\
> r_2 & \dots & \dots & \dots \\
> \vdots & \vdots & \ddots & \vdots \\
> r_n & \dots & \dots & \dots
> \end{pmatrix} $
>
> OĂą $C_j$ sont les colonnes (chaque $C_j$ est une `Series`) et $r_i$ sont les lignes.
# Création et chargement de DataFrames
Avant de pouvoir analyser des données, il faut les charger ou les créer.
## Créer un DataFrame à partir de données existantes
Vous pouvez créer un DataFrame à partir de diverses structures Python, comme des dictionnaires de listes ou des listes de dictionnaires.
> [!example] Créer un DataFrame à partir d'un dictionnaire
> ```python
> import pandas as pd
>
> data = {
> 'Nom': ['Alice', 'Bob', 'Charlie', 'David'],
> 'Âge': [24, 27, 22, 32],
> 'Ville': ['Paris', 'Lyon', 'Marseille', 'Toulouse'],
> 'Salaire': [50000, 60000, 45000, 75000]
> }
>
> df_personnes = pd.DataFrame(data)
> print(df_personnes)
> ```
>
> **Résultat :**
> ```
> Nom Âge Ville Salaire
> 0 Alice 24 Paris 50000
> 1 Bob 27 Lyon 60000
> 2 Charlie 22 Marseille 45000
> 3 David 32 Toulouse 75000
> ```
## Charger un DataFrame depuis un fichier
La méthode la plus courante pour obtenir des données est de les charger depuis un fichier. `pandas` supporte de nombreux formats. Le format CSV (Comma Separated Values) est l'un des plus répandus.
> [!example] Charger un fichier CSV
> Supposons que vous ayez un fichier `ventes.csv` avec le contenu suivant :
> ```csv
> Date,Produit,Quantite,PrixUnitaire
> 2023-01-01,A,10,12.5
> 2023-01-01,B,5,20.0
> 2023-01-02,A,7,12.5
> 2023-01-03,C,3,50.0
> ```
>
> Pour le charger :
> ```python
> import pandas as pd
>
> # Créez un fichier CSV temporaire pour l'exemple
> with open("ventes.csv", "w") as f:
> f.write("Date,Produit,Quantite,PrixUnitaire\n")
> f.write("2023-01-01,A,10,12.5\n")
> f.write("2023-01-01,B,5,20.0\n")
> f.write("2023-01-02,A,7,12.5\n")
> f.write("2023-01-03,C,3,50.0\n")
>
> df_ventes = pd.read_csv('ventes.csv')
> print(df_ventes)
> ```
>
> **Résultat :**
> ```
> Date Produit Quantite PrixUnitaire
> 0 2023-01-01 A 10 12.5
> 1 2023-01-01 B 5 20.0
> 2 2023-01-02 A 7 12.5
> 3 2023-01-03 C 3 50.0
> ```
`pd.read_csv()` accepte de nombreux paramètres pour gérer les spécificités des fichiers :
- `sep` : Le séparateur de colonnes (par défaut `,`).
- `header` : L'indice de la ligne à utiliser comme en-tête (par défaut 0).
- `index_col` : La colonne Ă utiliser comme index de ligne.
- `dtype` : Spécifier explicitement les types de données des colonnes.
- `na_values` : Liste de chaînes de caractères à interpréter comme des valeurs manquantes (NaN).
# Explorer un DataFrame : Les premières visualisations
Une fois le DataFrame chargé, la première étape cruciale est de l'explorer pour comprendre sa structure, son contenu et identifier d'éventuels problèmes.
> [!note] L'importance de l'exploration initiale
> Une exploration initiale approfondie permet de :
> - Vérifier que les données ont été chargées correctement.
> - Identifier les types de données de chaque colonne.
> - Détecter les valeurs manquantes ou aberrantes.
> - Obtenir un premier aperçu statistique des données.
> - Formuler des hypothèses pour des analyses ultérieures.
> C'est une étape indispensable avant tout pré-traitement ou modélisation.
Nous allons utiliser le DataFrame `df_personnes` créé précédemment pour les exemples.
```python
import pandas as pd
data = {
'Nom': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
'Âge': [24, 27, 22, 32, 29, None], # Ajout d'une valeur manquante
'Ville': ['Paris', 'Lyon', 'Marseille', 'Toulouse', 'Paris', 'Lyon'],
'Salaire': [50000, 60000, 45000, 75000, 55000, 62000],
'Anciennete': [2, 5, 1, 8, 4, 6]
}
df_personnes = pd.DataFrame(data)
```
## Dimensions et forme du DataFrame
- `.shape` : Tuple représentant le nombre de lignes et de colonnes.
- `.size` : Nombre total d'éléments dans le DataFrame (lignes * colonnes).
- `.ndim` : Nombre de dimensions (toujours 2 pour un DataFrame).
```python
print(f"Forme du DataFrame (lignes, colonnes) : {df_personnes.shape}")
print(f"Nombre total d'éléments : {df_personnes.size}")
print(f"Nombre de dimensions : {df_personnes.ndim}")
```
**Résultat :**
```
Forme du DataFrame (lignes, colonnes) : (6, 5)
Nombre total d'éléments : 30
Nombre de dimensions : 2
```
## Aperçu des données
- `.head(n)` : Affiche les `n` premières lignes (par défaut 5).
- `.tail(n)` : Affiche les `n` dernières lignes (par défaut 5).
- `.sample(n)` : Affiche `n` lignes aléatoires, utile pour les grands DataFrames.
```python
print("Premières 3 lignes :\n", df_personnes.head(3))
print("\nDernières 2 lignes :\n", df_personnes.tail(2))
print("\nUne ligne aléatoire :\n", df_personnes.sample(1))
```
## Informations générales sur le DataFrame
- `.info()` : Fournit un résumé concis du DataFrame, incluant le nombre d'entrées non nulles pour chaque colonne, les types de données (dtypes) et l'utilisation de la mémoire. C'est une méthode très utile pour un premier diagnostic.
```python
df_personnes.info()
```
**Résultat :**
```
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Nom 6 non-null object
1 Âge 5 non-null float64
2 Ville 6 non-null object
3 Salaire 6 non-null int64
4 Anciennete 6 non-null int64
dtypes: float64(1), int64(2), object(2)
memory usage: 368.0+ bytes
```
On remarque ici que la colonne 'Âge' a 5 valeurs non nulles sur 6, indiquant une valeur manquante. Son type est `float64` car `None` est converti en `NaN` (Not a Number), qui est de type flottant.
## Statistiques descriptives
- `.describe()` : Génère des statistiques descriptives pour les colonnes numériques (nombre, moyenne, écart-type, min, max, quartiles).
```python
print(df_personnes.describe())
```
**Résultat :**
```
Âge Salaire Anciennete
count 5.000000 6.000000 6.000000
mean 26.800000 57833.333333 4.333333
std 3.962322 9949.891668 2.657370
min 22.000000 45000.000000 1.000000
25% 24.000000 51250.000000 2.500000
50% 27.000000 57500.000000 4.500000
75% 29.000000 61500.000000 5.750000
max 32.000000 75000.000000 8.000000
```
> [!note] Comprendre les statistiques descriptives
> - `count` : Nombre de valeurs non nulles.
> - `mean` : Moyenne arithmétique. $\bar{x} = \frac{1}{n}\sum_{i=1}^n x_i$
> - `std` : Écart-type, mesure la dispersion des données.
> - `min`, `max` : Valeurs minimale et maximale.
> - `25%`, `50%` (médiane), `75%` : Les quartiles, qui divisent les données ordonnées en quatre parties égales.
## Noms des colonnes et types de données
- `.columns` : Liste des noms de toutes les colonnes.
- `.dtypes` : Renvoie les types de données de chaque colonne.
```python
print("Noms des colonnes :", df_personnes.columns)
print("\nTypes de données :\n", df_personnes.dtypes)
```
**Résultat :**
```
Noms des colonnes : Index(['Nom', 'Âge', 'Ville', 'Salaire', 'Anciennete'], dtype='object')
Types de données :
Nom object
Âge float64
Ville object
Salaire int64
Anciennete int64
dtype: object
```
> [!warning] Attention aux types de données
> Des types de données incorrects sont une source fréquente d'erreurs. Par exemple, une colonne de dates lue comme une chaîne de caractères (`object`) empêchera toute opération temporelle. Les colonnes numériques avec des valeurs manquantes sont souvent lues comme `float64` même si elles ne contiennent que des entiers. Le pré-traitement consistera souvent à corriger ces types.
## Valeurs uniques et dénombrements
- `.unique()` (sur une Series) : Renvoie un tableau des valeurs uniques d'une colonne.
- `.value_counts()` (sur une Series) : Renvoie le nombre d'occurrences de chaque valeur unique. Très utile pour les colonnes catégorielles.
```python
print("Villes uniques :", df_personnes['Ville'].unique())
print("\nRépartition des villes :\n", df_personnes['Ville'].value_counts())
```
**Résultat :**
```
Villes uniques : ['Paris' 'Lyon' 'Marseille' 'Toulouse']
Répartition des villes :
Ville
Paris 2
Lyon 2
Marseille 1
Toulouse 1
Name: count, dtype: int64
```
> [!tip] Bonnes pratiques d'exploration
> - Commencez toujours par `.head()`, `.info()`, `.describe()`.
> - Vérifiez les `value_counts()` pour les colonnes catégorielles.
> - Visualisez les distributions des colonnes numériques (histogrammes, boxplots) dès que possible (nous verrons cela plus en détail dans un chapitre dédié à la visualisation).
> - Prenez des notes sur vos observations et les problèmes potentiels identifiés.
# Accéder aux données : Colonnes et Lignes
Après avoir exploré la structure, il est essentiel de savoir comment sélectionner des sous-ensembles de données spécifiques.
## Sélection de colonnes
### Sélection d'une seule colonne
Vous pouvez sélectionner une colonne en utilisant son nom comme clé de dictionnaire. Le résultat est une `Series`.
```python
# Sélectionner la colonne 'Nom'
noms = df_personnes['Nom']
print("Type de 'noms' :", type(noms))
print(noms)
```
**Résultat :**
```
Type de 'noms' : <class 'pandas.core.series.Series'>
0 Alice
1 Bob
2 Charlie
3 David
4 Eve
5 Frank
Name: Nom, dtype: object
```
Vous pouvez également accéder à une colonne comme un attribut si le nom de la colonne est un identifiant Python valide (pas d'espaces, pas de caractères spéciaux). Cependant, la notation `df['colonne']` est préférée car elle est plus robuste.
```python
# Equivalent, mais moins robuste
ages = df_personnes.Âge
print(ages)
```
### Sélection de plusieurs colonnes
Pour sélectionner plusieurs colonnes, passez une liste de noms de colonnes. Le résultat est un `DataFrame`.
```python
# Sélectionner les colonnes 'Nom' et 'Salaire'
noms_salaires = df_personnes[['Nom', 'Salaire']]
print("Type de 'noms_salaires' :", type(noms_salaires))
print(noms_salaires)
```
**Résultat :**
```
Type de 'noms_salaires' : <class 'pandas.core.frame.DataFrame'>
Nom Salaire
0 Alice 50000
1 Bob 60000
2 Charlie 45000
3 David 75000
4 Eve 55000
5 Frank 62000
```
## Sélection de lignes
La sélection de lignes se fait principalement avec les accesseurs `.loc[]` et `.iloc[]`.
> [!theorem] Différence entre `.loc[]` et `.iloc[]`
> - **`.loc[]`** : Sélectionne les données par **étiquettes** (labels) des lignes et des colonnes.
> - **`.iloc[]`** : Sélectionne les données par **position entière** (integer location) des lignes et des colonnes (comme les indices dans les listes Python, à partir de 0).
### Sélection avec `.loc[]` (par étiquettes)
Syntaxe : `df.loc[row_labels, column_labels]`
```python
# Sélectionner la ligne avec l'index 0
print("Ligne 0 avec .loc :\n", df_personnes.loc[0])
# Sélectionner les lignes avec les index 1 et 3
print("\nLignes 1 et 3 avec .loc :\n", df_personnes.loc[[1, 3]])
# Sélectionner les lignes de l'index 0 à 2 (inclus) et les colonnes 'Nom' et 'Ville'
print("\nLignes 0 Ă 2, colonnes Nom et Ville avec .loc :\n", df_personnes.loc[0:2, ['Nom', 'Ville']])
```
**Résultat :**
```
Ligne 0 avec .loc :
Nom Alice
Âge 24.0
Ville Paris
Salaire 50000
Anciennete 2
Name: 0, dtype: object
Lignes 1 et 3 avec .loc :
Nom Âge Ville Salaire Anciennete
1 Bob 27 Lyon 60000 5
3 David 32 Toulouse 75000 8
Lignes 0 Ă 2, colonnes Nom et Ville avec .loc :
Nom Ville
0 Alice Paris
1 Bob Lyon
2 Charlie Marseille
```
### Sélection avec `.iloc[]` (par position entière)
Syntaxe : `df.iloc[row_positions, column_positions]`
```python
# Sélectionner la première ligne (indice 0)
print("Première ligne avec .iloc :\n", df_personnes.iloc[0])
# Sélectionner les deuxième et quatrième lignes (indices 1 et 3)
print("\nDeuxième et quatrième lignes avec .iloc :\n", df_personnes.iloc[[1, 3]])
# Sélectionner les lignes de 0 à 2 (exclusif pour les lignes, comme un slice Python)
# et les colonnes d'indice 0 et 2
print("\nLignes 0 Ă 1, colonnes 0 et 2 avec .iloc :\n", df_personnes.iloc[0:2, [0, 2]])
```
**Résultat :**
```
Première ligne avec .iloc :
Nom Alice
Âge 24.0
Ville Paris
Salaire 50000
Anciennete 2
Name: 0, dtype: object
Deuxième et quatrième lignes avec .iloc :
Nom Âge Ville Salaire Anciennete
1 Bob 27 Lyon 60000 5
3 David 32 Toulouse 75000 8
Lignes 0 Ă 1, colonnes 0 et 2 avec .iloc :
Nom Ville
0 Alice Paris
1 Bob Lyon
```
Notez la différence pour les slices : `loc[0:2]` inclut l'index 2, tandis que `iloc[0:2]` l'exclut (comme les slices Python classiques).
## Filtrage conditionnel (Boolean Indexing)
C'est une technique très puissante pour sélectionner des lignes basées sur des conditions logiques appliquées aux valeurs des colonnes.
```python
# Sélectionner les personnes dont l'âge est supérieur à 25
personnes_agees = df_personnes[df_personnes['Âge'] > 25]
print("Personnes de plus de 25 ans :\n", personnes_agees)
# Sélectionner les personnes de Paris ET ayant un salaire supérieur à 50000
personnes_paris_haut_salaire = df_personnes[(df_personnes['Ville'] == 'Paris') & (df_personnes['Salaire'] > 50000)]
print("\nPersonnes de Paris avec un salaire > 50000 :\n", personnes_paris_haut_salaire)
# Sélectionner les personnes dont la ville est 'Paris' OU 'Lyon'
personnes_paris_lyon = df_personnes[df_personnes['Ville'].isin(['Paris', 'Lyon'])]
print("\nPersonnes de Paris ou Lyon :\n", personnes_paris_lyon)
```
**Résultat :**
```
Personnes de plus de 25 ans :
Nom Âge Ville Salaire Anciennete
1 Bob 27.0 Lyon 60000 5
3 David 32.0 Toulouse 75000 8
4 Eve 29.0 Paris 55000 4
Personnes de Paris avec un salaire > 50000 :
Nom Âge Ville Salaire Anciennete
4 Eve 29.0 Paris 55000 4
Personnes de Paris ou Lyon :
Nom Âge Ville Salaire Anciennete
0 Alice 24.0 Paris 50000 2
1 Bob 27.0 Lyon 60000 5
4 Eve 29.0 Paris 55000 4
5 Frank NaN Lyon 62000 6
```
> [!warning] Opérateurs logiques en filtrage
> Pour combiner des conditions, utilisez les opérateurs bit à bit :
> - `&` pour ET logique (et non `and`)
> - `|` pour OU logique (et non `or`)
> - `~` pour NON logique
> N'oubliez pas les parenthèses autour de chaque condition !
# Premiers pas vers le pré-traitement (Transition)
L'exploration initiale que nous venons de pratiquer est la première étape vers un pré-traitement efficace. En effet, c'est en explorant que nous identifions les problèmes dans nos données :
- **Valeurs manquantes** : Comme la valeur `NaN` dans la colonne 'Âge' de notre exemple.
- **Types de données incorrects** : Une colonne numérique lue comme une chaîne de caractères.
- **Valeurs aberrantes** : Des données qui sortent des plages attendues.
- **Duplicata** : Des enregistrements identiques qui peuvent fausser les analyses.
Chacun de ces problèmes nécessitera des techniques de pré-traitement spécifiques pour nettoyer et préparer les données avant l'analyse ou la modélisation. Ce sera le sujet de notre prochain chapitre.
> [!note] Vers le pré-traitement
> Ce chapitre vous a donné les outils pour *voir* les données. Le prochain chapitre vous donnera les outils pour les *corriger* et les *transformer*. C'est un cycle itératif : exploration -> pré-traitement -> exploration -> analyse.
# ➡️ C'est la fin !
Nous avons couvert les points essentiels pour démarrer :
- **L'environnement des Notebooks** : Un cadre idéal pour l'expérimentation et la documentation.
- **Les bases de `pandas`** : Les structures `Series` et `DataFrame`, et leur importance capitale.
- **Chargement et création de DataFrames** : Comment faire entrer vos données dans `pandas`.
- **Exploration initiale** : Les méthodes clés (`.head()`, `.info()`, `.describe()`, `.value_counts()`) pour comprendre vos données.
- **Accès aux données** : Comment sélectionner des colonnes, des lignes spécifiques (`.loc`, `.iloc`) et filtrer avec des conditions.
---
- Cours précèdent: `cours-de-depart`
- Prochain cours: [[Cours 2 - Python et Data]]
- Page d'accueil de la compétence: [[Python et Data]]
# 🗓️ Historique
- Dernière MAJ: `13-Octobre-2025`
- Rédigé par: [[Hamilton DE ARAUJO]]