filter(): subconjuntos por condición
Por qué filter() es el verbo más usado
Si te pones a contar verbos en cualquier análisis real de R, filter() gana por goleada. La razón es simple: los datos crudos siempre contienen más de lo que necesitas para una pregunta concreta. Casi todo análisis empieza con “quédate solo con las filas donde…”.
En R base esto se hacía con [:
ventas[ventas$año == 2024, ]Funcional pero ilegible cuando crecen las condiciones. filter() resuelve dos problemas a la vez: legibilidad y composición con el pipe.
La forma básica: una condición
library(dplyr)
ventas |>
filter(año == 2024)Lo que está dentro del paréntesis es una condición que se evalúa fila a fila. Cuando devuelve TRUE, la fila pasa. Cuando devuelve FALSE o NA, no pasa.
Operadores típicos:
| Operador | Significado |
|---|---|
== |
Igual a |
!= |
Distinto de |
> < |
Mayor / menor que |
>= <= |
Mayor o igual / menor o igual |
%in% |
Pertenece al conjunto |
!x |
Negación de x |
Recordatorio: = es asignación, == es comparación. Confundirlos es uno de los errores tipográficos que más sufren los analistas que vienen de Excel.
Múltiples condiciones: las tres formas
Si quieres filtrar por más de una condición, tienes tres formas equivalentes:
# Forma 1: separadas por coma (AND implícito)
ventas |> filter(año == 2024, region == "Norte")
# Forma 2: con & explícito
ventas |> filter(año == 2024 & region == "Norte")
# Forma 3: en líneas separadas (cuando el filtro es largo)
ventas |> filter(
año == 2024,
region == "Norte",
unidades > 100
)Las tres dan el mismo resultado. La convención del tidyverse es separar con comas: es ligeramente más legible y deja claro que cada condición es independiente.
Para OR, en cambio, hay que usar | explícito:
ventas |> filter(region == "Norte" | region == "Sur")Que normalmente se simplifica con %in%:
ventas |> filter(region %in% c("Norte", "Sur"))NA: el gotcha número uno
Esta es la trampa que sorprende a casi todo el mundo:
df |> filter(precio == NA) # NO funciona como esperasNo devuelve las filas donde precio es NA. Devuelve cero filas, sin error. Porque en R, cualquier_cosa == NA evalúa a NA, no a TRUE o FALSE. Y filter() solo deja pasar TRUE.
La forma correcta:
df |> filter(is.na(precio)) # filas con NA
df |> filter(!is.na(precio)) # filas sin NAMás sutil aún: cuando filtras por una condición sobre una columna que contiene NAs, esas filas con NA tampoco pasan:
df |> filter(precio > 100) # NO incluye filas donde precio es NAEsto es consistente con la regla anterior (cualquier comparación con NA da NA, no TRUE), pero te quema cuando esperabas que esas filas siguieran ahí. Si necesitas conservarlas, sé explícito:
df |> filter(precio > 100 | is.na(precio))Operadores útiles para condiciones complejas
between(): rangos numéricos
ventas |> filter(between(precio, 100, 500))
# equivalente a: filter(precio >= 100, precio <= 500)%in%: pertenencia a un conjunto
ventas |> filter(region %in% c("Norte", "Sur", "Este"))str_detect(): patrones de texto
library(stringr)
ventas |> filter(str_detect(producto, "café|té"))near(): comparación de floats con tolerancia
# Esto puede fallar por precisión de coma flotante:
df |> filter(precio == 0.1 + 0.2) # ¡no devuelve filas donde precio == 0.3!
# Esto sí funciona:
df |> filter(near(precio, 0.3))Si alguna vez te has preguntado por qué un filtro sobre números decimales no encuentra lo que crees que debería, esta es la respuesta.
if_any() y if_all(): filtrar a través de columnas
A veces quieres filtrar filas donde alguna de varias columnas cumple una condición, o donde todas la cumplen. Sin estos helpers, tendrías que escribir cada columna a mano.
# Filas donde CUALQUIER columna numérica tiene NA
df |> filter(if_any(where(is.numeric), is.na))
# Filas donde TODAS las columnas de respuesta están completas
df |> filter(if_all(starts_with("resp_"), ~ !is.na(.x)))Este patrón es especialmente útil en preprocesado: detectar filas con datos perdidos sin escribir 14 is.na(columna_i) a mano.
Trampas habituales
filter(x == NA)devuelve cero filas en silencio. Usais.na(x)siempre que quieras encontrarNAs.- Las filas con NA se eliminan silenciosamente al filtrar por la columna. Si filtras por
precio > 100y hay preciosNA, las filas conNAtambién se van. Tenlo en cuenta si auditas pérdidas de filas en el pipeline. - Filtrar después de
mutate()que crea NAs. Si calculas una nueva columna que produce algunosNAy luego filtras por ella sin pensar, pierdes filas que quizá no querías perder. filter()no essubset(). Aunque hagan lo mismo en casos simples,subset()(R base) tiene comportamientos diferentes con scoping y se desaconseja en código de producción.
En la siguiente entrega
Has aprendido a quedarte con las filas que te interesan. El siguiente verbo, select(), se ocupa de las columnas. Es más simple que filter() pero tiene helpers que casi nadie conoce y que ahorran mucho código.