Esta es una demostración anotada de subsetting en R utilizando el set de datos iris incluido en R.
data(iris)
#para aquellos no familiarizados con los datos,
#corramos str() y summary()
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
summary(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.30 Min. :2.00 Min. :1.00 Min. :0.1
## 1st Qu.:5.10 1st Qu.:2.80 1st Qu.:1.60 1st Qu.:0.3
## Median :5.80 Median :3.00 Median :4.35 Median :1.3
## Mean :5.84 Mean :3.06 Mean :3.76 Mean :1.2
## 3rd Qu.:6.40 3rd Qu.:3.30 3rd Qu.:5.10 3rd Qu.:1.8
## Max. :7.90 Max. :4.40 Max. :6.90 Max. :2.5
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
Hagamos un gráfico de todos los datos (de una forma ligeramente indirecta que será más adecuada para demostrar el subsetting)
plot(iris$Petal.Length, iris$Petal.Width, type="n",xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length, iris$Petal.Width, pch=19, col=iris$Species)
Ahora podemos hacer un subset utilizando el operador []
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[iris$Species=="setosa"], iris$Petal.Width[iris$Species=="setosa"], pch=19, col=iris$Species[iris$Species=="setosa"])
Lo que nos dice básicamente la gráfica es la longitud del pétalo (Petal.Length) (de los casos donde la especie (Species) es setosa) versus el ancho del pétalo (Petal.Width) (de los casos donde la especie es setosa).
Hay un error muy común que muchos cometen en este punto. Si ingresan el siguiente código:
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[iris$Species=="setosa"], iris$Petal.Width, pch=19, col=iris$Species[iris$Species=="setosa"])
Obtendrán el siguiente mensaje de error:
Error in xy.coords(x, y, xlabel, ylabel, log) :
‘x’ and ‘y’ lengths differ
Esto sucede porque estás intentando comparar algo que posee un subset (Petal.Length) contra algo que no lo tiene (Petal.Width) y R no tiene idea como hacer coincidir el número diferente de entradas.
Las cosas en el interior de [] son el criterio para seleccionar las entradas, para que podamos ver lo que está pasando en una forma un poco menos congestionada podemos establecer los criterios como una variable.
criterios <- iris$Species=="setosa"
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
El doble signo igual es utilizado para hacer una comparación directa, y dado que estamos trabajando con texto usamos “”. Otros operadores comunes son <,<=,>,>=,!=, y ! (que quiere decir “no es el caso”)
criterios <- iris$Sepal.Width < 3.1
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
Si queremos un resultado mas estrecho podemos utilizar el símbolo AND (&) para combinar diferentes criterios.
criterios <- iris$Species=="setosa" & iris$Sepal.Width < 3.1
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
En este caso combinamos en “criterios” la especie setosa y un ancho de sépalo menor a 3.1.
Si queremos resultados más amplios, se puede utilizar el símbolo OR | para proveer una lista de criterios, pero en este caso es también buena idea usar paréntesis () para dejar claro cuáles son los grupos (como cuando aprendes matemáticas, las cosas dentro del paréntesis se ordenan primero.
criterios <- iris$Species=="setosa" & (iris$Sepal.Width < 3.1 | iris$Sepal.Width > 3.5)
plot(iris$Petal.Length, iris$Petal.Width, type="n",xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
Utilizando la misma lectura que en matemáticas, el primer criterio que procesamos es donde indicamos que queremos incluir las observaciones que tengan un ancho de sépalo menor a 3.1 ó mayor a 3.5, obtenido eso, agregamos aquellas donde la especie es setosa. Fijense que la cantidad de datos que cumple con esos criterios es mayor que en nuestro gráfico anterior.
Si no utilizamos paréntesis obtendríamos un resultado bastante diferente:
criterios <- iris$Species=="setosa" & iris$Sepal.Width < 3.1 | iris$Sepal.Width > 3.5
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
En este caso la lectura es distinta, lo que hace R es seguir el orden y primero nos entrega aquellos datos de especie setosa Y ancho de sépalo mayor a 3.1, además aquellas observaciones con ancho de sépalo mayor a 3.5 que no están asociados solo a setosa.
El operador %in% puede ser útil para proveer una lista para mirar
criterios <- iris$Species %in% c("setosa","versicolor","dingo")
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
R buscará los match de la lista entregada, en este caso si existe dentro de especie la palabra “setosa”, “versicolor” y “dingo”, arrojará las observaciones correspondientes. Dado que “dingo” no es una de las especies incluídas en nuestro frame de datos, solo veremos todas las observaciones correspondientes a “setosa” y “versicolor”.
También puedes realizar un subset numérico, eligiendo un rango de números de fila
criterios <- 5:10
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
O puedes incluir una lista de números de fila con distintos rangos.
criterios <- c(2, 5:10, 12, 18:32)
plot(iris$Petal.Length, iris$Petal.Width, type="n", xlab="Largo Pétalo", ylab="Ancho Pétalo")
points(iris$Petal.Length[criterios], iris$Petal.Width[criterios], pch=19, col=iris$Species[criterios])
Hasta ahora hemos hecho subsetting a una variable en particular, pero también podemos hacer subset a un frame de datos completo utilizando la forma dataframe[criteriofila, criteriocolumna]
criteriofila <- iris$Species=="setosa"
criteriocolumna <- 1:2
summary(iris[criteriofila,criteriocolumna])
## Sepal.Length Sepal.Width
## Min. :4.30 Min. :2.30
## 1st Qu.:4.80 1st Qu.:3.20
## Median :5.00 Median :3.40
## Mean :5.01 Mean :3.43
## 3rd Qu.:5.20 3rd Qu.:3.67
## Max. :5.80 Max. :4.40
La coma es el marcador de el criterio de fila y el criterio de columna. En el caso que queramos todas las filas o todas las columnas dejaremos vacio el espacio después o antes de la coma correspondientemente.
criteriofila <- iris$Species=="setosa"
criteriocolumna <- 1:2
summary(iris[,criteriocolumna])
## Sepal.Length Sepal.Width
## Min. :4.30 Min. :2.00
## 1st Qu.:5.10 1st Qu.:2.80
## Median :5.80 Median :3.00
## Mean :5.84 Mean :3.06
## 3rd Qu.:6.40 3rd Qu.:3.30
## Max. :7.90 Max. :4.40
o
criteriofila <- iris$Species=="setosa"
criteriocolumna <- 1:2
summary(iris[criteriofila,])
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.30 Min. :2.30 Min. :1.00 Min. :0.100
## 1st Qu.:4.80 1st Qu.:3.20 1st Qu.:1.40 1st Qu.:0.200
## Median :5.00 Median :3.40 Median :1.50 Median :0.200
## Mean :5.01 Mean :3.43 Mean :1.46 Mean :0.246
## 3rd Qu.:5.20 3rd Qu.:3.67 3rd Qu.:1.57 3rd Qu.:0.300
## Max. :5.80 Max. :4.40 Max. :1.90 Max. :0.600
## Species
## setosa :50
## versicolor: 0
## virginica : 0
##
##
##
Hay otro error común que podemos encontrar en este punto. Si estas seleccionando filas, es fácil que se olvide incluir la coma al final.
criteriofila <- iris$Species=="setosa"
criteriocolumna <- 1:2
summary(iris[criteriofila])
Obtendrán el siguiente error: Error in ‘[.data.frame’(iris, rowcriteria) : undefined columns selected
Debido a que estamos lidiando con un bloque de datos, sin la coma R no sabe como aplicar las reglas incluídas.
También se puede utilizar una lista numerada de las columnas que se desean
criteriofila <- iris$Species=="setosa"
criteriocolumna <- c(1:2,4)
summary(iris[criteriofila,criteriocolumna])
## Sepal.Length Sepal.Width Petal.Width
## Min. :4.30 Min. :2.30 Min. :0.100
## 1st Qu.:4.80 1st Qu.:3.20 1st Qu.:0.200
## Median :5.00 Median :3.40 Median :0.200
## Mean :5.01 Mean :3.43 Mean :0.246
## 3rd Qu.:5.20 3rd Qu.:3.67 3rd Qu.:0.300
## Max. :5.80 Max. :4.40 Max. :0.600
Y una lista de nombres
criteriofila <- iris$Species=="setosa"
criteriocolumna <- c("Petal.Length","Petal.Width")
summary(iris[criteriofila,criteriocolumna])
## Petal.Length Petal.Width
## Min. :1.00 Min. :0.100
## 1st Qu.:1.40 1st Qu.:0.200
## Median :1.50 Median :0.200
## Mean :1.46 Mean :0.246
## 3rd Qu.:1.57 3rd Qu.:0.300
## Max. :1.90 Max. :0.600
Si por ejemplo tienen datos para usar de una forma en particular, por ejemplo en un test estadístico, probablemente quieran almacenarlos en un objeto
criteriofila <- iris$Species=="setosa"
criteriocolumna <- c("Petal.Length","Petal.Width")
misubset <- iris[criteriofila,criteriocolumna]
summary(misubset)
## Petal.Length Petal.Width
## Min. :1.00 Min. :0.100
## 1st Qu.:1.40 1st Qu.:0.200
## Median :1.50 Median :0.200
## Mean :1.46 Mean :0.246
## 3rd Qu.:1.57 3rd Qu.:0.300
## Max. :1.90 Max. :0.600
También puedes hacer un subset removiendo columnas (en vez de especificar las columnas que quieres)
criteriofila <- iris$Species=="setosa"
criteriocolumna <- names(iris) %in% c("Petal.Length","Petal.Width")
misubset <- iris[criteriofila,!(criteriocolumna)]
summary(misubset)
## Sepal.Length Sepal.Width Species
## Min. :4.30 Min. :2.30 setosa :50
## 1st Qu.:4.80 1st Qu.:3.20 versicolor: 0
## Median :5.00 Median :3.40 virginica : 0
## Mean :5.01 Mean :3.43
## 3rd Qu.:5.20 3rd Qu.:3.67
## Max. :5.80 Max. :4.40
Esto dice básicamente “Encuentra las columnas con ese nombre, las ves, pues esas son las que no quiero”
También pueden realizar subsetting removiendo datos al utilizar números negativos
criteriofila <- iris$Species=="setosa"
criteriocolumna <- c(-1,-3:-5)
misubset <- iris[criteriofila,criteriocolumna]
summary(misubset)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.30 3.20 3.40 3.43 3.68 4.40
Pueden remover filas de una forma similar, utilizando números negativos
criteriofila <- c(-1:-25)
criteriocolumna <- c(-1,-3:-5)
mysubset <- iris[criteriofila,criteriocolumna]
summary(mysubset)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.00 2.70 3.00 2.97 3.20 4.20
Subsetting no es lo mismo que borrar una variable. Subsetting es esconder aquellas que no queremos, pero siguen estando disponibles para posteridad, borrar es destruirlas. Aunque a veces se puede utilizar de la misma manera haciendo primero una copia de los datos
misubset <- iris
misubset$Species <- NULL
summary(misubset)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.30 Min. :2.00 Min. :1.00 Min. :0.1
## 1st Qu.:5.10 1st Qu.:2.80 1st Qu.:1.60 1st Qu.:0.3
## Median :5.80 Median :3.00 Median :4.35 Median :1.3
## Mean :5.84 Mean :3.06 Mean :3.76 Mean :1.2
## 3rd Qu.:6.40 3rd Qu.:3.30 3rd Qu.:5.10 3rd Qu.:1.8
## Max. :7.90 Max. :4.40 Max. :6.90 Max. :2.5