HOWTO · Pandas
Filtrar filas de un DataFrame de pandas por valores de columna
Filtra filas de un DataFrame de pandas con máscaras booleanas, loc, isin, between, pruebas de texto y nulos o query.
En esta página
Filtrar consiste en crear un valor booleano por fila y conservar las filas cuyo valor es True. La forma general más clara es df.loc[condición]; el segundo argumento permite seleccionar también las columnas de salida.
Crear el DataFrame de ejemplo
La tabla incluye texto, números, fechas y un producto ausente. Los ejemplos posteriores reutilizan orders. El índice original se conserva; usa .reset_index(drop=True) solo si necesitas uno nuevo.
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"]),
}
)
Filtrar filas por un valor de columna
Comparar una Series genera la máscara. Los corchetes simples seleccionan las mismas filas, pero .loc expresa mejor la selección simultánea de filas y columnas. Usa != para excluir una coincidencia e isna() o notna() para nulos.
east_orders = orders.loc[orders["region"] == "East"]
print(east_orders[["order_id", "region", "sales"]].to_string(index=False))
Salida:
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))
Salida:
order_id sales
101 450
104 700
106 410
Combinar varias condiciones
Combina condiciones elemento a elemento con &, | y ~, encerrando cada comparación entre paréntesis. and y or de Python esperan un único booleano y provocan el error de verdad ambigua con una Series.
mask = (orders["region"] == "East") & (orders["sales"] >= 400)
result = orders.loc[mask, ["order_id", "region", "sales"]]
print(result.to_string(index=False))
Salida:
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)]
Filtrar por una lista de valores
Series.isin() evita cadenas de comparaciones. Recibe una lista u otra colección similar, no una cadena suelta. Para excluir valores, aplica ~ al resultado completo de isin().
allowed = ["East", "West"]
result = orders.loc[orders["region"].isin(allowed), ["order_id", "region"]]
print(result.to_string(index=False))
Salida:
order_id region
101 East
102 West
103 East
105 West
106 East
excluded = ["cancelled", "pending"]
paid_only = orders.loc[~orders["status"].isin(excluded)]
Filtrar intervalos y fechas
between() incluye ambos extremos por defecto y también funciona con fechas convertidas mediante pd.to_datetime(). En datos con zona horaria, las fechas límite deben ser compatibles.
mid_value = orders.loc[orders["sales"].between(300, 500), ["order_id", "sales"]]
print(mid_value.to_string(index=False))
Salida:
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"]]
Filtrar valores ausentes o presentes
isna() selecciona None, NaN y NaT; notna() exige un valor presente. Una cadena vacía no es nula automáticamente y debe normalizarse o comprobarse por separado.
missing_product = orders.loc[orders["product"].isna(), ["order_id", "product"]]
present_product = orders.loc[orders["product"].notna()]
print(missing_product.to_string(index=False))
Salida:
order_id product
105 None
Filtrar valores de texto
str.contains() produce una máscara vectorizada. regex=False busca texto literal, case=False ignora mayúsculas y na=False decide que los textos ausentes no coinciden.
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))
Salida:
order_id product
101 Desk
103 Desk lamp
104 Desk
Usar DataFrame.query() para expresiones legibles
En query(), @ referencia una variable local y los nombres de columna con espacios requieren comillas invertidas. No concatenes entradas no confiables: la expresión se evalúa y puede ejecutar código.
minimum_sales = 400
result = orders.query("region == 'East' and sales >= @minimum_sales")
print(result[["order_id", "region", "sales"]].to_string(index=False))
Salida:
order_id region sales
101 East 450
106 East 410
Elegir entre .loc, corchetes y query()
Usa .loc como opción general, corchetes para una selección breve de filas y query() cuando una expresión controlada sea más legible. Añade .copy() antes de modificar un resultado independiente.
east_orders = orders.loc[orders["region"] == "East"].copy()
east_orders["sales_with_tax"] = east_orders["sales"] * 1.2
Evitar sorpresas de alineación y tipos
Las máscaras se alinean por índice. Créala desde el mismo DataFrame o realinéala deliberadamente. Convierte texto numérico o fechas antes de comparar y normaliza texto solo si la regla lo requiere.
Reutilizar y depurar máscaras booleanas
Una máscara con nombre puede contarse, inspeccionarse y probarse. Cero coincidencias devuelve un DataFrame vacío válido. DataFrame.filter() selecciona etiquetas del eje, no filas por valores de celdas.
Filtrar sin modificar los datos de origen
Para cambiar el original, asigna mediante .loc; para trabajar aparte, crea una copia. Esta decisión explícita evita depender de la indexación encadenada o de si un objeto comparte memoria.
DataFrame.loc, Series.isin, Series.between, Series.str.contains, DataFrame.query