7.5 Condicionales

Las condicionales nos permiten obtener subconjuntos que para los que una o más condiciones son verdaderas (TRUE).

Para este procedimiento usamos operadores lógicos y condicionales, como revisamos en el capítulo 5 y lo podemos aplicar a data frames.

Con este procedimiento, podemos extraer todos los datos de una encuesta que corresponden a mujeres, o a personas que viven en una entidad específica, o que tienen un ingreso superior a la media, o cualquier otra condición que se verificable con álgebra Booleana.

Realizamos la extracción de subconjuntos mediante operaciones relacionales y lógicas dentro de corchetes.

Esta operación tiene la siguiente estructura.

objeto[condicion, columnas_devueltas]

En donde:

  • objeto: es un data frame.
  • condición: un subconjunto de objeto, que devuelva un columna como vector, al que se le aplica una o más operaciones lógicas o relacionales.
  • columnas_devueltas: los índices o nombres de las columnas que deseamos sean devueltas como resultado.

Dentro del corchete escribimos, antes de una coma, el código para obtener un subconjunto que extraiga una columna, del data frame al que queremos extraer un subconjunto usando un subconjunto.

Este primer subconjunto debe ser extraído con alguno de los dos procedimientos que da como resultado un vector, ya sea el signo $ o corchetes dobles [[]].

Necesitamos extraer un vector, porque aplicaremos a todos los elementos de esa columna una operación relacional, usando vectorización, como lo vimos en el capítulo 6.

Todos los elementos para los que el resultado de esta operación sea TRUE, formarán parte de nuestro subconjunto usando condicionales. Como cada elemento de una columna de un data frame está ubicado en su propio renglón, podemos decir que nos serán devueltos los renglones para los que la condición sea verdadera.

Si dejamos el espacio para columnas_devueltas vacío, nuestro resultado será un data frame con las mismas columnas que el data frame original. Si damos un índice o un nombre, entonces obtendremos un data frame sólo con las columnas solicitadas. De este modo, podemos extraer renglones y columnas específicas.

Veamos esto en práctica, extrayendo subconjuntos de los datos iris.

7.5.1 Usando condicionales

Intentaremos extraer todos los datos en iris en los que el largo del sépalo, columna Petal.Width, sea mayor que 7.5.

Primero, obtenemos el subconjunto de esta columna de iris. Usaremos el signo $

iris$Sepal.Length
##   [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4
##  [18] 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5
##  [35] 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0
##  [52] 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8
##  [69] 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4
##  [86] 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8
## [103] 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7
## [120] 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7
## [137] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9

Si aplicamos la operación relacional > 7 a este subconjunto, obtenemos un vector, con TRUE o FALSE para cada elemento de iris$Petal.Width.

iris$Sepal.Length > 7.5
##   [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [12] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [45] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##  [89] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [100] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
## [111] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE
## [122] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
## [133] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [144] FALSE FALSE FALSE FALSE FALSE FALSE FALSE

Escribimos esta operación dentro de los corchetes, antes de una coma. No escribimos nada después de la coma, para obtener un subconjunto con todas las columnas de iris.

iris[iris$Sepal.Length > 7.5, ]
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 106          7.6         3.0          6.6         2.1 virginica
## 118          7.7         3.8          6.7         2.2 virginica
## 119          7.7         2.6          6.9         2.3 virginica
## 123          7.7         2.8          6.7         2.0 virginica
## 132          7.9         3.8          6.4         2.0 virginica
## 136          7.7         3.0          6.1         2.3 virginica

Podemos pedir que se nos devuelvan sólo los datos de la columna Species, escribiendo el índice o nombre de esta columna después de la coma.

# Usando índice
iris[iris$Sepal.Length > 7.5, 5]
## [1] virginica virginica virginica virginica virginica virginica
## Levels: setosa versicolor virginica
#USando nombres
iris[iris$Sepal.Length > 7.5, "Species"]
## [1] virginica virginica virginica virginica virginica virginica
## Levels: setosa versicolor virginica

Nota que si pedimos una sola columna en nuestros resultado, el resultado será un vector en lugar de un data frame.

class(iris[iris$Sepal.Length > 7.5, "Species"])
## [1] "factor"

Podemos realizar más de una operación relacional antes de la coma, usando operadores lógicos.

Por ejemplo, extraemos todos los datos para los que el ancho del pétalo (Sepal.Width) sea menor que 3 y la especie (Species) sea “setosa”.

iris[iris$Sepal.Width < 3 & iris$Species == "setosa", ]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 9           4.4         2.9          1.4         0.2  setosa
## 42          4.5         2.3          1.3         0.3  setosa

Recuerda que puedes usar el operados ! para negaciones. De este modo puedes extraer todos los datos que no son de la especie virginica o su largo sea menor a 4.7.

iris[!(iris$Petal.Length < 4.7 | iris$Species == "virginica"), ]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
## 51          7.0         3.2          4.7         1.4 versicolor
## 53          6.9         3.1          4.9         1.5 versicolor
## 57          6.3         3.3          4.7         1.6 versicolor
## 64          6.1         2.9          4.7         1.4 versicolor
## 71          5.9         3.2          4.8         1.8 versicolor
## 73          6.3         2.5          4.9         1.5 versicolor
## 74          6.1         2.8          4.7         1.2 versicolor
## 77          6.8         2.8          4.8         1.4 versicolor
## 78          6.7         3.0          5.0         1.7 versicolor
## 84          6.0         2.7          5.1         1.6 versicolor
## 87          6.7         3.1          4.7         1.5 versicolor

En realidad podemos usar un vector lógico para extraer subconjuntos, sin necesidad de realizar una operación relacional.

Por ejemplo, para obtener los dato en el primer y cuarto renglón de mi_df.

mi_df[c(TRUE, FALSE, FALSE, TRUE), ]
##    nombre edad sexo grupo
## 1 Armando   20    H     0
## 4    Olga   30    H     0

Si damos un vector lógico de largo menor que el número de renglones en un data frame, el vector es reciclado.

Al utilizar vector c(FALSE, TRUE), nos serán devueltos el segundo y cuarto renglón de mi_df.

mi_df[c(FALSE, TRUE), ]
##   nombre edad sexo grupo
## 2   Elsa   24    M     1
## 4   Olga   30    H     0

Si proporcionamos una condición que no se cumple en ningún caso, es decir, devuelve un vector que consiste sólo de FALSE, el subconjunto que obtenemos es una data frame sin renglones.

iris[iris$Species == "oceanica", ]
## [1] Sepal.Length Sepal.Width  Petal.Length Petal.Width  Species     
## <0 rows> (or 0-length row.names)

Finalmente, si no escribimos una coma dentro del corchete después de la condicional, obtendremos un data frame sin columnas, que para fines prácticos es un objeto sin utilidad.

iris[iris$Petal.Width >= 5]
## data frame with 0 columns and 150 rows

7.5.2 La función subset()

Una alternativa para usar condicionales, sin necesidad de corchetes, es la función subset().

Esta función tiene los siguientes argumentos:

  • x: Un objeto, generalmente un data frame.
  • subset: Una condición, expresada como operaciones relacionales o condicionales, que se aplicarán a una columna de x.
  • select: Un vector con los nombres de las columnas a conservar en el resultado. Si no asignamos un valor a este argumento, se nos devuelven todas las columnas de x.

Puedes leer la documentación completa llamando ?subset.

Como podrás ver, subset() necesita como argumentos los mismos elementos que usamos para extraer subconjuntos con corchetes. La principal diferencia se encuentra en el argumento subset.

Al usar corchetes, necesitamos aplicar una operación relacional a un vector, extraído de nuestro objeto original, por ejemplo iris[iris$Species == "setosa", ]. Con subset(), basta proporcionar el nombre del elemento al que aplicaremos las operaciones relacionales y condicionales. Para este caso, escribimos Species == "setosa". Nota que el nombre de la columna está escrito sin comillas.

Por ejemplo, para extraer de iris todos los datos en los que el largo del sépalo es mayor que 7.5, damos a iris como argumento x y Sepal.Lenght como argumento subset. Dejamos sin definir el argumento select, para obtener todas las columnas.

subset(x = iris, subset = Sepal.Length > 7.5)
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 106          7.6         3.0          6.6         2.1 virginica
## 118          7.7         3.8          6.7         2.2 virginica
## 119          7.7         2.6          6.9         2.3 virginica
## 123          7.7         2.8          6.7         2.0 virginica
## 132          7.9         3.8          6.4         2.0 virginica
## 136          7.7         3.0          6.1         2.3 virginica

Obtenemos el mismo resultado que al llamar iris[iris.Sepal.Length > 7.5, ].

Damos además c("Sepal.Length", "Species") como argumento a select y con ello sólo nos serán devueltas las columnas con estos nombres.

subset(x = iris, subset = Sepal.Length > 7.5, select = c("Sepal.Length", "Species"))
##     Sepal.Length   Species
## 106          7.6 virginica
## 118          7.7 virginica
## 119          7.7 virginica
## 123          7.7 virginica
## 132          7.9 virginica
## 136          7.7 virginica

Desde luego, esto es equivalente a llamar iris[iris.Sepal.Length > 7.5, c("Sepal.Length", "Species")].

También podemos usar operaciones lógicas para el argumento subset.

Por ejemplo, los datos para los que el largo del sépalo sea mayor que 7.5 y el ancho del pétalo sea igual a 3.

subset(x = iris, subset = Sepal.Length > 7.5 & Sepal.Width == 3)
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 106          7.6           3          6.6         2.1 virginica
## 136          7.7           3          6.1         2.3 virginica

Si damos como argumento a subset una comparación con un nombre de columna no válido, obtenemos un error.

subset(x = iris, subset = Sepal.Weight > 7.5 )
## Error in eval(e, x, parent.frame()): object 'Sepal.Weight' not found

Al igual que si usamos corchetes, si pedimos una condición que para ningún caso es verdadera, nuestro resultado es un data frame sin renglones.

subset(x = iris, subset = Sepal.Length > 9 )
## [1] Sepal.Length Sepal.Width  Petal.Length Petal.Width  Species     
## <0 rows> (or 0-length row.names)

Si damos como argumento select el nombre de una columna inválida, también se nos devuelve un error.

subset(x = iris, subset = Sepal.Length > 7, select = "Sepal.Weight")
## Error in `[.data.frame`(x, r, vars, drop = drop): undefined columns selected

Finalmente, también podemos dar un vector lógico como argumento subset para obtener subconjuntos. Por ejemplo, el segundo y cuarto renglón de mi_df.

subset(x = mi_df, subset = c(TRUE, FALSE, FALSE, TRUE))
##    nombre edad sexo grupo
## 1 Armando   20    H     0
## 4    Olga   30    H     0

La función subset() casi siempre resulta en código más breve y es más fácil de interpretar por el usuario que los subconjuntos condicionales con corchetes. Sin embargo, la mayor parte de las veces, usar uno u otro procedimiento depende de tu preferencia personal y de las necesidades de los proyectos en los que colaboras.