Series et DataFrame — les deux objets de pandas
Ce qu'est une colonne, ce qu'est une table, et pourquoi chaque valeur d'une colonne doit partager le même type. Inclut la différence entre df["col"] et df[["col"]].
Comment voulez-vous apprendre ?
Le problème que nous résolvons
Dans le chapitre précédent, une ligne a été écrite, et elle a fonctionné :
print(sales["quantity"].sum())19Cette ligne est facile à lire, mais une question s'y cache. Qu'est-ce que sales["quantity"] a renvoyé ? Une liste ? Les listes n'ont pas de .sum() — écrire [12, 5, 2].sum() en Python provoque une erreur.
La réponse vaut la peine d'être connue, car avec pandas, vous avez toujours l'une de ces deux choses sous la main, et presque toutes les confusions partent de la même question : laquelle ai-je sous la main en ce moment ? Une colonne, ou une table entière ?
Ce chapitre est consacré à ces deux objets : leurs noms, leurs structures et ce qui les sépare.
À la fin de ce chapitre, vous saurez
- Dire en une phrase ce qui sépare une
Seriesd'unDataFrame - Créer une
Serieset inspecter séparément son index, ses valeurs et son type - Expliquer pourquoi chaque valeur d'une colonne doit partager le même type
- Utiliser
df["col"]etdf[["col"]]en sachant lequel est lequel - Lire une
KeyErroret identifier exactement ce qui ne va pas avec le nom d'une colonne
Prérequis : installer pandas et votre première table.
Series — une colonne
Commençons par la plus petite unité. Une Series est une suite de valeurs, chacune associée à un nom :
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity)0 12
1 5
2 2
dtype: int64Trois valeurs ont été fournies en entrée, et trois paires sont ressorties. Les 0, 1, 2 sur la gauche forment l'index — un nom pour chaque valeur. Vous ne les avez pas fournis ; pandas a simplement compté à partir de zéro.
Et tout en bas, dtype: int64 — le type de toute la série. Un seul type, pas un par valeur. C'est ce qui rend pandas rapide, et c'est aussi sa contrainte principale.
Ces trois parties peuvent être observées séparément :
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity.index)
print(quantity.values)
print(quantity.dtype)RangeIndex(start=0, stop=3, step=1)
[12 5 2]
int64RangeIndex signifie que l'index n'est rien d'autre qu'un décompte — commençant à zéro et augmentant de un en un. values correspond aux valeurs sans leurs noms. Et dtype est le type de la colonne entière.
Un index n'a pas besoin d'être composé de nombres :
import pandas as pd
quantity = pd.Series([12, 5, 2], index=["pen", "notebook", "bag"])
print(quantity)
print(quantity["notebook"])pen 12
notebook 5
bag 2
dtype: int64
5Désormais, les lignes portent le nom des produits, et une valeur peut être récupérée par ce nom. Cela ressemble à un dictionnaire Python, et la ressemblance est bien réelle — avec une différence majeure :
import pandas as pd
quantity = pd.Series([12, 5, 2], index=["pen", "notebook", "bag"])
print(quantity * 2)pen 24
notebook 10
bag 4
dtype: int64Toute la colonne a été multipliée par deux en une seule fois, sans aucune boucle. Pour faire cela avec un dictionnaire, il faudrait parcourir chaque valeur soi-même. Appliquez une opération à une Series et pandas l'applique à chaque valeur — et tous les chapitres suivants reposent sur ce comportement.
DataFrame — plusieurs colonnes, un seul index
Un DataFrame est un ensemble de plusieurs Series côte à côte qui partagent un même index.
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales)
print(sales.shape)
print(sales.columns)product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
(3, 3)
Index(['product', 'quantity', 'price'], dtype='object')Cette expression « partagent un même index » en est l'essence même. Le product, la quantity et le price à l'index 0 parlent tous de la même chose, à savoir un stylo. L'index est ce qui relie trois colonnes pour former des lignes.
Une table, plusieurs types — mais une colonne, un seul type
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales.dtypes)product object
quantity int64
price float64
dtype: objectTrois colonnes, trois types. int64 représente les nombres entiers, float64 les décimaux, et object — qui désigne généralement du texte.
Remarquez qu'il n'y a pas de str. Les types dans pandas viennent de NumPy, où chaque valeur occupe le même espace en mémoire — ce qui est possible pour des nombres, mais pas pour du texte de longueur variable. Pour stocker du texte, pandas conserve donc des pointeurs vers des objets Python, et cet agencement s'appelle object.
Voici maintenant la règle importante : chaque valeur d'une colonne partage un seul et même type. Alors, que se passe-t-il si l'on mélange des nombres et du texte dans une même colonne ?
import pandas as pd
mixed = pd.Series([1, "two", 3.0])
print(mixed)
print(mixed.dtype)0 1
1 two
2 3.0
dtype: objectPandas ne renvoie pas d'erreur — il attribue le type object à toute la colonne, en se rabattant sur le type le plus souple possible. La conséquence n'est pas spectaculaire, elle est silencieuse : la colonne n'est plus numérique, si bien que .mean() échouera dessus et .sum() se mettra à concaténer du texte. Un simple N/A égaré sur une seule ligne d'un fichier CSV produit exactement cela, et c'est le sujet du chapitre six.
Qu'est-ce que j'ai sous la main — une Series ou un DataFrame ?
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
column = sales["quantity"]
print(type(column))
print(type(sales))
print(column.mean())<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
6.333333333333333Et voilà la réponse à la question posée au début de ce chapitre. sales["quantity"] n'est pas une liste, c'est une Series — et une Series possède .sum(), .mean(), .max() et tout le reste.
Il y a aussi deux syntaxes qui déroutent tout le monde la première fois :
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales["quantity"])
print("---")
print(sales[["quantity"]])0 12
1 5
2 2
Name: quantity, dtype: int64
---
quantity
0 12
1 5
2 2Une paire de crochets en plus, et le résultat est d'une nature totalement différente. Le premier est une Series — une colonne, avec son nom et son type affichés en dessous. Le second est un DataFrame — une table qui se trouve n'avoir qu'une seule colonne, si bien que l'en-tête de la colonne se trouve au-dessus.
La règle est que les crochets intérieurs représentent une liste de colonnes, et demander une liste renvoie une table. Ainsi, sales[["product", "price"]] donne une table à deux colonnes.
Pourquoi garder cette distinction en tête ? Parce que c'est là que les messages d'erreur divergent. UneSeriesdispose de.mean()mais n'a pas de noms de colonnes ; unDataFramepermet de sélectionner des colonnes mais ne vous renvoie pas un nombre unique. Quand un message d'erreur indique qu'un objet ne possède pas cet attribut, la première question à se poser est : lequel des deux ai-je réellement entre les mains ?
Un exemple complet
inventory.py :
import pandas as pd
# Product names become the index, so a row can be reached by name.
stock = pd.DataFrame(
{
"quantity": [12, 5, 2, 30],
"price": [15.0, 60.0, 850.0, 8.0],
},
index=["pen", "notebook", "bag", "eraser"],
)
print(stock)
print()
print("Shape :", stock.shape)
print("Columns :", list(stock.columns))
print("Index :", list(stock.index))
print()
# A column is a Series, so an operation applies to every value at once.
value = stock["quantity"] * stock["price"]
print("Value per product:")
print(value)
print()
print("Total value:", value.sum())quantity price
pen 12 15.0
notebook 5 60.0
bag 2 850.0
eraser 30 8.0
Shape : (4, 2)
Columns : ['quantity', 'price']
Index : ['pen', 'notebook', 'bag', 'eraser']
Value per product:
pen 180.0
notebook 300.0
bag 1700.0
eraser 240.0
dtype: float64
Total value: 2420.0Deux points méritent d'être soulignés.
Tout d'abord, stock["quantity"] * stock["price"] a multiplié deux Series et a produit une autre Series. Pandas les a associées par index — la quantité du stylo avec le prix du stylo, celle du sac avec le prix du sac. Par nom, et non par ordre de ligne. Ce comportement est le fondement même de la fusion de deux tables que nous verrons plus tard.
Ensuite, le type de la colonne résultante est float64, bien que quantity soit de type int64. Multiplier un nombre entier par un nombre décimal donne un décimal — c'est la même règle qu'en Python pur ; pandas n'invente rien ici.
Quand ça casse
KeyError: 'Quantity' Cette colonne n'existe pas. Les noms de colonnes sont sensibles à la casse, et les noms provenant d'un fichier CSV comportent souvent un espace au début ou à la fin. Exécutez print(list(df.columns)) pour voir exactement ce qui s'y trouve — les afficher sous forme de liste place des guillemets autour de chaque nom, ce qui fait apparaître le moindre espace.
AttributeError: 'DataFrame' object has no attribute 'mean_price' df.column_name permet aussi de récupérer une colonne, mais seulement pour des noms simples — pas quand le nom contient un espace, commence par un chiffre ou entre en conflit avec une méthode pandas telle que count. df["column_name"] fonctionne toujours, prenez donc l'habitude d'utiliser cette syntaxe.
TypeError: unsupported operand type(s) lors d'un calcul sur une colonne numérique La colonne n'est probablement pas numérique. Vérifiez df.dtypes ; un type object à cet endroit signifie que du texte s'est glissé quelque part.
.sum() concatène du texte au lieu d'additionner des nombres Même cause — la colonne est de type object. Un seul N/A ou une seule cellule vide dans une ligne suffit pour modifier le type de toute la colonne.
ValueError: All arrays must be of the same length Les listes fournies à pd.DataFrame({...}) ne font pas toutes la même longueur. Chaque colonne doit avoir le même nombre de valeurs, car les lignes doivent s'aligner sur un index partagé.
Étape 4 sur 6 — Prédire
Vérifiez votre compréhension
Trois valeurs ont été fournies. Qu'est-ce qui est exactement affiché ?
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity)- A12 5 2
- B[12, 5, 2]
- C12 5 2 dtype: int64
- D0 12 1 5 2 2 dtype: int64
Les deux lignes ne diffèrent que par une paire de crochets supplémentaire. Que renvoie type() ?
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(type(sales["quantity"]))
print(type(sales[["quantity"]]))- AToutes deux une Series
- BToutes deux un DataFrame
- CLa première une Series, la seconde un DataFrame
- DLa première un DataFrame, la seconde une Series
Des nombres et du texte ont été mélangés dans une même colonne. Que fait pandas ?
import pandas as pd
mixed = pd.Series([1, "two", 3.0])
print(mixed.dtype)- AIl ne lève aucune erreur — il attribue le type `object` à toute la colonne
- BUne `TypeError` — deux types ne peuvent pas coexister dans une même colonne
- CIl supprime le texte et conserve la colonne sous forme de `float64`
- DIl conserve un type distinct pour chaque valeur
Répondre nécessite un compte
Connectez-vous pour vérifier vos réponses
Les questions sont ci-dessus, et les résoudre de tête est ce qui compte vraiment. Connectez-vous pour voir les réponses, les explications et les indices à trois niveaux.
À vous de jouer
Créez un fichier nommé library.py contenant un DataFrame d'au moins cinq livres — avec les titres comme index, et des colonnes pour le nombre d'exemplaires (nombres entiers), le prix (nombres décimaux) et le nom de l'auteur (texte).
Affichez ensuite :
- La table entière, son
shapeet sesdtypes - La colonne des prix seule — et montrez avec
type()qu'il s'agit d'uneSeries - La colonne des prix avec des doubles crochets — et montrez que celle-ci est un
DataFrame - La valeur totale de chaque livre (exemplaires × prix), ainsi que le total pour l'ensemble des livres
Faites ensuite une expérience : remplacez l'un des nombres d'exemplaires par "unknown" et réexécutez le fichier. Qu'indique dtypes à présent, et qu'est-il arrivé au total ?
Ce sont les cinq minutes les plus précieuses de ce chapitre. Un seul mot dans une seule cellule change le type d'une colonne entière et fausse les calculs arithmétiques sans déclencher la moindre erreur — et avec des données réelles, cela arrive constamment.
Étape 6 sur 6
Défi — le quiz du chapitre
Dix questions, de la plus facile à la plus difficile. Les dernières sont difficiles exprès.
Connectez-vous pour faire le quiz