Résumé du cours « Pandas » de Kaggle (1) – Leçons 1‑3
Résumé du cours Kaggle « Pandas »: créer/lire/écrire des données, indexer et sélectionner, fonctions de synthèse et maps. Leçons 1–3, avec compléments pratiques.
Je regroupe ici mes notes issues du cours Pandas de Kaggle.
Le volume étant conséquent, j’ai scindé l’article en deux parties.
- Partie 1 : Leçons 1–3 (billet actuel)
- Partie 2 : Leçons 4–6
Lesson 1. Creating, Reading and Writing
Importer pandas
1
import pandas as pd
Pandas expose deux objets centraux, les DataFrame et les Series.
DataFrame
Un DataFrame peut être vu comme un tableau ou une matrice. Il s’agit d’une matrice composée d’entrées (entries) indépendantes, où chaque entrée a une valeur (value) et correspond à une ligne (row) ou enregistrement (record) ainsi qu’à une colonne (column).
1
pd.DataFrame({'Yes': [50, 21], 'No': [131, 2]})
| Yes | No | |
|---|---|---|
| 0 | 50 | 131 |
| 1 | 21 | 2 |
Les entrées d’un DataFrame n’ont pas besoin d’être numériques; ci‑dessous un exemple avec des valeurs textuelles (des avis d’utilisateurs).
1
pd.DataFrame({'Bob': ['I liked it.', 'It was awful.'], 'Sue': ['Pretty good.', 'Bland.']})
| Bob | Sue | |
|---|---|---|
| 0 | I liked it. | Pretty good. |
| 1 | It was awful. | Bland. |
On crée un DataFrame avec le constructeur pd.DataFrame(), en utilisant la syntaxe du dictionnaire Python: la clé est le nom de colonne et la valeur est une liste d’éléments. C’est la manière standard d’instancier un nouveau DataFrame.
Lors de la création, on fournit des étiquettes de colonnes (noms de colonnes). Si l’on ne fournit pas d’étiquettes de lignes, pandas affecte 0, 1, 2, … par défaut. On peut les définir manuellement si besoin. La liste des étiquettes de lignes s’appelle l’index; on le fournit via le paramètre index du constructeur.
1
2
3
pd.DataFrame({'Bob': ['I liked it.', 'It was awful.'],
'Sue': ['Pretty good.', 'Bland.']},
index=['Product A', 'Product B'])
| Bob | Sue | |
|---|---|---|
| Product A | I liked it. | Pretty good. |
| Product B | It was awful. | Bland. |
Series
Une Series est une suite (sequence) de valeurs, ou encore un vecteur.
1
pd.Series([1, 2, 3, 4, 5])
Une Series est essentiellement une seule colonne d’un DataFrame. On peut donc lui attribuer un index ; elle a un simple nom (name) au lieu d’un « nom de colonne ».
1
pd.Series([30, 35, 40], index=['12015 Sales', '12016 Sales', '12017 Sales'], name='Product A')
1
2
3
4
12015 Sales 30
12016 Sales 35
12017 Sales 40
Name: Product A, dtype: int64
Series et DataFrame sont étroitement liés: on peut voir un DataFrame comme un regroupement de Series.
Lire des fichiers de données
Le plus souvent, on importe des données existantes au lieu de les saisir à la main. Le format le plus basique est le CSV. Un fichier CSV ressemble à ceci:
Product A,Product B,Product C,
30,21,9,
35,34,1,
41,11,11
Chaque valeur est séparée par une virgule (Comma-Separated Values, CSV).
Pour charger un CSV en DataFrame, utilisez pd.read_csv().
1
product_reviews = pd.read_csv("../input/product-reviews/example-data.csv")
La propriété shape donne la forme du DataFrame.
1
product_reviews.shape
1
(129971, 14)
Ici, le DataFrame contient 129971 enregistrements et 14 colonnes.
La méthode head() affiche les cinq premières lignes.
1
product_reviews.head()
pd.read_csv() accepte plus de 30 paramètres. Par exemple, si le CSV contient déjà une colonne d’index, on peut la réutiliser via index_col au lieu de laisser pandas créer un index numérique.
1
product_reviews = pd.read_csv("../input/product-reviews/example-data.csv", index_col=0)
Écrire des fichiers de données
Avec to_csv(), on peut exporter un DataFrame en CSV, par exemple:
1
product_reviews.to_csv("../output/product-reviews/example-data.csv")
Lesson 2. Indexing, Selecting & Assigning
Sélectionner des valeurs spécifiques dans un DataFrame ou une Series est une étape quasi incontournable de tout traitement de données; il est donc crucial de savoir le faire rapidement et efficacement.
Accesseurs natifs de Python
Les objets Python offrent de bons mécanismes d’indexation, que pandas reprend.
Attributs d’objet
En Python, on accède à la valeur d’un attribut via son nom. Si l’objet example_obj possède un attribut title, on l’appelle par example_obj.title. On peut faire de même avec les colonnes d’un DataFrame pandas.
1
reviews.country
1
2
3
4
5
6
0 Italy
1 Portugal
...
129969 France
129970 France
Name: country, Length: 129971, dtype: object
Indexation de dictionnaire
Les dictionnaires Python s’indexent avec l’opérateur []. Les colonnes d’un DataFrame s’obtiennent de la même manière.
1
reviews['country']
1
2
3
4
5
6
0 Italy
1 Portugal
...
129969 France
129970 France
Name: country, Length: 129971, dtype: object
Les deux approches sont valides, mais l’indexation type dictionnaire gère aussi les noms de colonnes contenant des espaces ou d’autres caractères spéciaux (par ex. reviews['country providence'] fonctionne, alors que reviews.country providence ne fonctionne pas).
On peut ensuite réappliquer [] sur la Series obtenue pour lire une valeur individuelle.
1
reviews['country'][0]
1
'Italy'
Accesseurs spécifiques à pandas
Outre les accesseurs compatibles avec l’écosystème Python, pandas fournit ses propres accesseurs, loc et iloc.
Sélection basée sur les indices
iloc réalise une sélection basée sur l’index numérique (index-based selection): on sélectionne par position entière.
Par exemple, pour la première ligne du DataFrame:
1
reviews.iloc[0]
1
2
3
4
5
6
country Italy
description Aromas include tropical fruit, broom, brimston...
...
variety White Blend
winery Nicosia
Name: 0, Length: 13, dtype: object
Contrairement aux approches « natives » qui sélectionnent d’abord les colonnes puis les lignes, iloc sélectionne d’abord les lignes puis les colonnes. La première colonne se sélectionne ainsi:
1
reviews.iloc[:, 0]
1
2
3
4
5
6
0 Italy
1 Portugal
...
129969 France
129970 France
Name: country, Length: 129971, dtype: object
Ici : signifie « toutes les lignes », puis on prend la première colonne. Pour ne prendre que les deuxième (1) et troisième (2) lignes de la première colonne:
1
reviews.iloc[1:3, 0]
1
2
3
1 Portugal
2 US
Name: country, dtype: object
On peut aussi fournir une liste d’indices:
1
reviews.iloc[[1, 2], 0]
1
2
3
1 Portugal
2 US
Name: country, dtype: object
Les indices négatifs permettent de partir de la fin; ci‑dessous, les 5 dernières lignes:
1
reviews.iloc[-5:]
Sélection basée sur les étiquettes
Avec loc, on fait une sélection basée sur les étiquettes (label-based selection): on sélectionne par valeur d’index.
Par exemple, pour l’entrée de la ligne d’index 0 et de la colonne ‘country’:
1
reviews.loc[0, 'country']
1
'Italy'
iloc ignore les valeurs d’index de l’objet et raisonne comme sur une grande matrice en se basant sur les positions. loc s’appuie au contraire sur les valeurs d’index, souvent porteuses d’information, ce qui en fait un choix plus intuitif dans bien des cas.
Différences de tranchage entre iloc et loc
iloc suit la convention Python standard: 0:10 signifie l’intervalle demi‑ouvert 0 inclus, 10 exclu, soit 0,...,9.
loc considère les tranches comme fermées: 0:10 signifie « de 0 à 10 inclus », soit 0,...,10.
Cette différence existe parce que loc accepte tout type standard comme indice, pas seulement les entiers. Par exemple, pour des indices lexicographiques Apples, ..., Potatoes, ..., sélectionner de ‘Apples’ à ‘Potatoes’ s’écrit naturellement df.loc['Apples':'Potatoes'] plutôt que df.loc['Apples':'Potatoet']. Pour des indices non entiers, ce comportement est généralement plus intuitif.
Hormis cela, le fonctionnement est similaire.
Personnellement, avec un index d’entiers triés par ordre croissant et un tranchage
:, j’utiliseilocpour éviter la confusion sur les bornes; sinon, je privilégieloc, souvent plus intuitif.
Manipuler l’index
On peut ajuster l’index au besoin. Par exemple, avec set_index(), on peut définir une colonne comme nouvel index:
1
reviews.set_index("title")
Sélection conditionnelle
Au‑delà des aspects structurels, on peut sélectionner les lignes vérifiant des conditions logiques.
Supposons un DataFrame d’informations sur des vins et que l’on veuille les vins italiens notés au moins 90.
1
reviews.country == 'Italy'
Cette condition renvoie une Series booléenne True/False.
1
2
3
4
5
6
0 True
1 False
...
129969 False
129970 False
Name: country, Length: 129971, dtype: bool
loc est label‑based, mais accepte aussi un masque booléen (array ou Series triable). On peut donc sélectionner les vins italiens ainsi:
1
reviews.loc[reviews.country == 'Italy']
On combine plusieurs conditions avec & (ET) ou | (OU). Vins italiens ET note ≥ 90:
1
reviews.loc[(reviews.country == 'Italy') & (reviews.points >= 90)]
Vins italiens OU note ≥ 90:
1
reviews.loc[(reviews.country == 'Italy') | (reviews.points >= 90)]
Pandas fournit aussi des sélecteurs intégrés, notamment isin et isna/notna.
isin renvoie un masque indiquant si la valeur « est dans » une liste donnée. Par exemple, vins italiens ou français:
1
reviews.loc[reviews.country.isin(['Italy', 'France'])]
isna/notna servent à filtrer selon la présence d’une valeur manquante (NaN). Par exemple, ne garder que les vins dont le prix n’est pas manquant:
1
reviews.loc[reviews.price.notna()]
À noter (non mentionné dans le cours Kaggle):
ilocaccepte aussi des tableaux booléens. Contrairement àloc, il ne prend pas les Series booléennes (seulement les arrays), ce qui limite ce type d’usage.
Affectation de données
On peut créer ou écraser des colonnes.
1
2
reviews['critic'] = 'everyone'
reviews['critic']
1
2
3
4
5
6
0 everyone
1 everyone
...
129969 everyone
129970 everyone
Name: critic, Length: 129971, dtype: object
1
2
reviews['index_backwards'] = range(len(reviews), 0, -1)
reviews['index_backwards']
1
2
3
4
5
6
0 129971
1 129970
...
129969 2
129970 1
Name: index_backwards, Length: 129971, dtype: int64
Lesson 3. Summary Functions and Maps
Obtenir un aperçu des données
La méthode describe() fournit un résumé de haut niveau pour une colonne donnée.
1
reviews.points.describe()
1
2
3
4
5
6
count 129971.000000
mean 88.447138
...
75% 91.000000
max 100.000000
Name: points, Length: 8, dtype: float64
La sortie dépend du type de données. Pour une colonne de chaînes:
1
reviews.taster_name.describe()
1
2
3
4
5
count 103727
unique 19
top Roger Voss
freq 25514
Name: taster_name, dtype: object
On peut aussi demander des statistiques ciblées:
1
reviews.points.mean()
1
88.44713820775404
1
reviews.taster_name.unique()
1
2
3
4
5
6
7
array(['Kerin O’Keefe', 'Roger Voss', 'Paul Gregutt',
'Alexander Peartree', 'Michael Schachner', 'Anna Lee C. Iijima',
'Virginie Boone', 'Matt Kettmann', nan, 'Sean P. Sullivan',
'Jim Gordon', 'Joe Czerwinski', 'Anne Krebiehl\xa0MW',
'Lauren Buzzeo', 'Mike DeSimone', 'Jeff Jenssen',
'Susan Kostrzewa', 'Carrie Dykes', 'Fiona Adams',
'Christina Pickard'], dtype=object)
Pour compter les occurrences de chaque valeur unique dans une colonne, utilisez value_counts().
1
reviews.taster_name.value_counts()
1
2
3
4
5
6
Roger Voss 25514
Michael Schachner 15134
...
Fiona Adams 27
Christina Pickard 6
Name: taster_name, Length: 19, dtype: int64
Applications (maps)
Une application (map) est, en mathématiques, une fonction qui associe les éléments d’un ensemble à ceux d’un autre. En data science, on transforme souvent les données vers d’autres représentations; ces opérations sont cruciales.
Deux méthodes sont particulièrement utilisées.
Series.map() prend une fonction transformant une valeur en une autre, l’applique à toutes les valeurs de la Series et renvoie la nouvelle Series. Par exemple, soustraire la moyenne aux notes de vin:
1
2
review_points_mean = reviews.points.mean()
reviews.points.map(lambda p: p - review_points_mean)
1
2
3
4
5
6
0 -1.447138
1 -1.447138
...
129969 1.552862
129970 1.552862
Name: points, Length: 129971, dtype: float64
DataFrame.apply() applique une fonction personnalisée à chaque ligne (ou, avec une option, à chaque colonne) du DataFrame.
1
2
3
4
5
def remean_points(row):
row.points = row.points - review_points_mean
return row
reviews.apply(remean_points, axis='columns')
Avec axis='index', apply() applique la fonction colonne par colonne.
Series.map() et DataFrame.apply() renvoient respectivement une nouvelle Series et un nouveau DataFrame; ils ne modifient pas les données d’origine.
| Méthode | Series.map() | DataFrame.apply() |
|---|---|---|
| Cible | Series | DataFrame |
| Granularité d’application | Par valeur (si l’on voit la Series comme un vecteur colonne, c’est « par ligne ») | Par défaut par ligne Option possible par colonne |
À noter qu’il existe aussi
Series.apply()etDataFrame.map().
Series.apply():
by_row='compat'(par défaut): se comporte commeSeries.map()by_row=False: passe la Series entière à la fonction (analogue àDataFrame.apply()avecaxis='index')DataFrame.map(): applique la fonction à chaque valeur du DataFrame (analogue àSeries.map(), mais sur un DataFrame)
Pandas fournit de nombreuses vectorisations usuelles. L’exemple précédent s’écrit bien plus simplement, pandas interprétant correctement l’intention:
1
2
review_points_mean = reviews.points.mean()
reviews.points - review_points_mean
1
2
3
4
5
6
0 -1.447138
1 -1.447138
...
129969 1.552862
129970 1.552862
Name: points, Length: 129971, dtype: float64
Pandas prend aussi en charge les opérations entre Series de même longueur. Dans l’exemple des vins, on peut concaténer les chaînes « pays – région »:
1
reviews.country + " - " + reviews.region_1
1
2
3
4
5
6
0 Italy - Etna
1 NaN
...
129969 France - Alsace
129970 France - Alsace
Length: 129971, dtype: object
Ces opérations vectorisées, accélérées par pandas, sont plus rapides que map() ou apply() et couvrent tous les opérateurs Python standards (>, <, ==, etc.). Cela dit, map() et apply() restent plus flexibles pour des transformations complexes et valent la peine d’être connues.

