HOWTO · Pandas
Filtrer les lignes d’un DataFrame pandas selon les valeurs d’une colonne
Filtrez les lignes d’un DataFrame pandas avec des masques booléens, loc, isin, between, des tests de texte ou query.
Sur cette page
Filtrer revient à produire un booléen par ligne et à conserver celles dont la valeur est True. La forme générale df.loc[condition] est claire ; son second argument sélectionne également les colonnes de sortie.
Créer le DataFrame d’exemple
La table contient du texte, des nombres, des dates et une valeur manquante. Les exemples suivants réutilisent orders. L’index d’origine est conservé ; ne le réinitialisez que si le résultat l’exige.
import pandas as pd
orders = pd.DataFrame(
{
"order_id": [101, 102, 103, 104, 105, 106],
"region": ["East", "West", "East", "North", "West", "East"],
"product": ["Desk", "Chair", "Desk lamp", "Desk", None, "Chair"],
"sales": [450, 275, 125, 700, 320, 410],
"status": ["paid", "pending", "paid", "paid", "cancelled", "pending"],
"ordered_at": pd.to_datetime(["2026-08-01", "2026-08-03", "2026-08-08", "2026-08-11", "2026-08-14", "2026-08-18"]),
}
)
Filtrer les lignes selon une valeur de colonne
La comparaison d’une Series crée le masque. Les crochets simples donnent les mêmes lignes, mais .loc sélectionne aussi les colonnes. Utilisez != pour exclure une valeur et isna() ou notna() pour les valeurs manquantes.
east_orders = orders.loc[orders["region"] == "East"]
print(east_orders[["order_id", "region", "sales"]].to_string(index=False))
Sortie :
order_id region sales
101 East 450
103 East 125
106 East 410
large_orders = orders.loc[orders["sales"] >= 400, ["order_id", "sales"]]
print(large_orders.to_string(index=False))
Sortie :
order_id sales
101 450
104 700
106 410
Combiner plusieurs conditions
Combinez les conditions élément par élément avec &, | et ~, chaque comparaison étant entre parenthèses. Les opérateurs Python and et or attendent un seul booléen et déclenchent l’erreur de vérité ambiguë avec une Series.
mask = (orders["region"] == "East") & (orders["sales"] >= 400)
result = orders.loc[mask, ["order_id", "region", "sales"]]
print(result.to_string(index=False))
Sortie :
order_id region sales
101 East 450
106 East 410
mask = (orders["status"] == "paid") | (orders["sales"] > 600)
result = orders.loc[mask, ["order_id", "status", "sales"]]
# Raises ValueError: The truth value of a Series is ambiguous.
orders.loc[(orders["region"] == "East") and (orders["sales"] >= 400)]
Filtrer selon une liste de valeurs
Series.isin() remplace une longue suite de comparaisons. Passez une liste ou une collection similaire, jamais une chaîne seule. Pour exclure des valeurs, niez le résultat complet avec ~.
allowed = ["East", "West"]
result = orders.loc[orders["region"].isin(allowed), ["order_id", "region"]]
print(result.to_string(index=False))
Sortie :
order_id region
101 East
102 West
103 East
105 West
106 East
excluded = ["cancelled", "pending"]
paid_only = orders.loc[~orders["status"].isin(excluded)]
Filtrer des plages et des dates
between() inclut les deux bornes par défaut et accepte aussi les dates après conversion par pd.to_datetime(). Pour des données avec fuseau, les bornes doivent être compatibles.
mid_value = orders.loc[orders["sales"].between(300, 500), ["order_id", "sales"]]
print(mid_value.to_string(index=False))
Sortie :
order_id sales
101 450
105 320
106 410
date_mask = orders["ordered_at"].between("2026-08-03", "2026-08-14")
result = orders.loc[date_mask, ["order_id", "ordered_at"]]
Filtrer les valeurs manquantes ou présentes
isna() repère None, NaN et NaT, tandis que notna() exige une valeur. Une chaîne vide n’est pas automatiquement manquante et doit être traitée séparément.
missing_product = orders.loc[orders["product"].isna(), ["order_id", "product"]]
present_product = orders.loc[orders["product"].notna()]
print(missing_product.to_string(index=False))
Sortie :
order_id product
105 None
Filtrer les valeurs textuelles
str.contains() crée un masque vectorisé. regex=False recherche un texte littéral, case=False ignore la casse et na=False classe explicitement les textes manquants comme non correspondants.
has_desk = orders["product"].str.contains("desk", case=False, regex=False, na=False)
result = orders.loc[has_desk, ["order_id", "product"]]
print(result.to_string(index=False))
Sortie :
order_id product
101 Desk
103 Desk lamp
104 Desk
Utiliser DataFrame.query() pour des expressions lisibles
Dans query(), @ référence une variable locale et les colonnes contenant des espaces utilisent des accents graves. Ne concaténez jamais une entrée non fiable dans l’expression, car elle est évaluée et peut exécuter du code.
minimum_sales = 400
result = orders.query("region == 'East' and sales >= @minimum_sales")
print(result[["order_id", "region", "sales"]].to_string(index=False))
Sortie :
order_id region sales
101 East 450
106 East 410
Choisir entre .loc, les crochets et query()
Préférez .loc en général, les crochets pour une simple sélection de lignes et query() pour une expression contrôlée réellement plus lisible. Utilisez .copy() avant de modifier un résultat indépendant.
east_orders = orders.loc[orders["region"] == "East"].copy()
east_orders["sales_with_tax"] = east_orders["sales"] * 1.2
Éviter les surprises d’alignement et de type
Un masque est aligné sur les étiquettes d’index. Créez-le depuis le même DataFrame. Convertissez les textes numériques et les dates avant comparaison ; normalisez casse et espaces uniquement si la règle le demande.
Réutiliser et déboguer les masques booléens
Un masque nommé peut être inspecté, compté et testé. Aucun résultat produit un DataFrame vide valide. DataFrame.filter() sélectionne des étiquettes d’axe, et non des lignes selon les cellules.
Filtrer sans modifier les données sources
Pour modifier la source, affectez via .loc. Pour un nouveau jeu de travail, créez une copie. Ce choix explicite évite de dépendre de l’indexation chaînée ou d’un partage de mémoire incertain.
DataFrame.loc, Series.isin, Series.between, Series.str.contains, DataFrame.query