Tema 8 Tipos de datos: data frames
En este tema seguimos analizando los principales tipos de datos de R, estudiando los data frames. Un data frame es una estructura o tipo de dato muy usado en estadística, similar a una hoja de cálculo. Un data frame es parecido a una matriz, en el sentido de que es una estructura bidimensional con sus filas y columnas, pero con la diferencia de que las distintas columnas de un data frame pueden ser de distintos tipos. Esto lo hace muy versátil. Normalmente un data frame contienen distintas variables en las columnas que indican los valores asociados a cada fila. Cada fila constituye una observación. Si realizas análisis de datos en R vas a trabajar con data frames.
Internamente un data frame se implementa como una lista, cada componente de la lista es una columna del data frame. Todas las columnas deben tener la misma longitud. Las distintas columnas de un data frame serán vectores, aunque técnicamente es posible que sean matrices o listas. Esto se ilustra en la Figura 8.1. El hecho de que un data frame sea semánticamente similar a una matriz, pero esté implementado como una lista, va a producir una gran riqueza sintáctica a la hora de acceder a sus elementos.

8.1 Creación de data frames
Normalmente los data frames se construirán a partir de datos que están en archivos tipo hoja de cálculo almacenados en un dispositivo de almacenamiento local (como un disco duro) o internet con funciones como read.table
. También existen muchos paquetes que incluyen data frames entre sus datos. Por ejemplo:
head(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
En este caso se ha mostrado el famoso conjunto de datos sobre lirios (iris
). Se puede obtener ayuda sobre los data frames incluidos en paquetes: prueba ?iris
para obtener ayuda sobre este conjunto de datos. La función head
hace que se visualicen las primeras filas u observaciones del conjunto de datos. head
es una función genérica y se puede aplicar a otros tipos de datos como vectores. Este data frame tiene 150 observaciones, con 5 variables por observación: longitud y anchura de sépalo, longitud y anchura de pétalo (todas en centímetros) y especie. La especie es un factor con 3 valores posibles: setosa, versicolor y virginica.
También se puede crear un data frame con la función data.frame
, aportando los datos oportunos. La sintaxis de creación de un data frame es muy parecida a la de creación de una lista:
d <- data.frame(Nombre = c("Laura", "Mario", "Luis"),
Edad = c(22, 43, 55),
Ciudad = factor(c("Jaén", "Granada", "Jaén")),
Casado = c(FALSE, TRUE, TRUE)
)
d
## Nombre Edad Ciudad Casado
## 1 Laura 22 Jaén FALSE
## 2 Mario 43 Granada TRUE
## 3 Luis 55 Jaén TRUE
Observa que cada columna del data frame se ha especificado como un vector o factor, precedida por su nombre y el carácter =
. Todas las columnas deben tener la misma longitud.
También es posible crear un data frame con la función edit
, que permite editar de una forma sencilla los contenidos:
Una función parecida a edit
es View
, que nos permite visualizar los elementos de un data frame o matriz de manera similar a una hoja de cálculos:
Ejercicio. Crea un data frame que incluya las notas de 3 estudiantes (observaciones) en los exámenes de teoría y prácticas.
8.2 Acceso a los elementos con formato matriz
Se puede acceder a los datos de un data frame como se hace con una matriz, es decir, especificando las filas y columnas a las que queremos acceder con la sintaxis: df[filas, columnas]
. En concreto, se puede especificar las distintas filas y columnas usando vectores de:
- índices positivos
- índices negativos
- cadenas de caracteres
- valores lógicos
Vamos a ver algunos ejemplos:
d[c(1, 3), ] # Índices positivos: filas 1 y 3
## Nombre Edad Ciudad Casado
## 1 Laura 22 Jaén FALSE
## 3 Luis 55 Jaén TRUE
d[, -2] # Índices negativos: excluir columna 2
## Nombre Ciudad Casado
## 1 Laura Jaén FALSE
## 2 Mario Granada TRUE
## 3 Luis Jaén TRUE
d[, c("Nombre", "Edad")] # cadenas de caracteres
## Nombre Edad
## 1 Laura 22
## 2 Mario 43
## 3 Luis 55
d[d[, "Ciudad"] == "Jaén", ] # valores lógicos
## Nombre Edad Ciudad Casado
## 1 Laura 22 Jaén FALSE
## 3 Luis 55 Jaén TRUE
Observa que si se deja en blanco la parte de filas o columnas se seleccionan todos los elementos de la dimensión. Por ejemplo, en d[c(1, 3), ]
se seleccionan las filas 1 y 3 y todas las columnas.
La última operación filtra las observaciones (filas) cuya ciudad es Jaén. Si sólo se selecciona una columna de un data frame, el comportamiento por defecto es devolver un vector con los datos de dicha columna, en lugar de un data frame con una única columna. Veamos cómo anular este comportamiento por defecto:
8.3 Acceso a los elementos con formato lista
Como hemos comentado, un data frame se implementa como una lista cuyos componentes son las columnas del data frame. Esto permite acceder con formato de lista para seleccionar las columnas de un data frame. Se recuerda que los operadores de acceso a lista son: []
, [[]]
y $
.
Con []
podemos seleccionar una o varias columnas y el resultado será un data frame con las columnas seleccionadas:
d[1:3] # primeras 3 columnas
## Nombre Edad Ciudad
## 1 Laura 22 Jaén
## 2 Mario 43 Granada
## 3 Luis 55 Jaén
d[c("Nombre", "Casado")] # columnas de nombres: Nombre y Casado
## Nombre Casado
## 1 Laura FALSE
## 2 Mario TRUE
## 3 Luis TRUE
d[ncol(d)] # última columna (produce un data frame)
## Casado
## 1 FALSE
## 2 TRUE
## 3 TRUE
d[-ncol(d)] # Todas las columnas, salvo la última
## Nombre Edad Ciudad
## 1 Laura 22 Jaén
## 2 Mario 43 Granada
## 3 Luis 55 Jaén
Observa que al acceder con []
siempre se obtiene un data frame, aunque sólo se seleccione una columna. Si se accede con [[]]
sólo se puede obtener una columna; además, se obtiene un vector con los contenidos de dicha columna, Por ejemplo, no tiene sentido mean(d["Edad"])
(pues no tiene sentido aplicar mean
a un data frame), pero sí mean(d[["Edad"]])
.
d[["Nombre"]] # Vector asociado a la columna Nombre
## [1] "Laura" "Mario" "Luis"
mean(d[[2]]) # Media de la columna 2
## [1] 40
mean(d[["Edad"]]) # Media de la columna Edad
## [1] 40
Por último, el operador $
se utiliza para acceder al contenido de una columna mediante su nombre. d$Edad
equivale a d[["Edad"]]
, pero resulta algo más corto de escribir.
d$Ciudad
## [1] Jaén Granada Jaén
## Levels: Granada Jaén
d[d$Casado, ] # Muestra sólo las personas casadas
## Nombre Edad Ciudad Casado
## 2 Mario 43 Granada TRUE
## 3 Luis 55 Jaén TRUE
Ejercicio. El data frame mtcars
contiene 32 observaciones de coches, con 10 datos (variables) sobre cada coche (puedes ejecutar ?mtcars
para obtener información sobre cada variable). Usa indexación tipo matriz y lista para obtener:
- Todas las columnas, salvo la segunda
- Las columnas de nombre `"mpg"` y `"cyl"`
8.4 Trabajando con data frames
Las posibilidades de procesamiento de un data frame son enormes. En general, podemos aplicar la mayor parte de las funciones asociadas a matrices y a listas. En este apartado vamos a ver algunas, pero ten presente que casi cualquier cálculo que quieras realizar sobre un data frame se puede realizar de manera elegante con unas pocas sentencias.
8.4.1 Consulta de información y nombres
Las funciones length
y ncol
devuelven el número de columnas de un data frame, mientras que nrow
devuelve el número de filas. Todos los data frames tienen nombres de columnas y filas (por defecto los nombres de las filas son el número de observación), éstos se pueden consultar y modificar con colnames
y rownames
:
length(d) # número de columnas
## [1] 4
ncol(d) # número de columnas
## [1] 4
nrow(d) # número de filas
## [1] 3
colnames(d) # nombres de las columnas
## [1] "Nombre" "Edad" "Ciudad" "Casado"
names(d) # también produce el nombre de las columnas
## [1] "Nombre" "Edad" "Ciudad" "Casado"
rownames(d) # nombres de las filas
## [1] "1" "2" "3"
rownames(d) <- c("Primero", "Segundo", "Tercero")
d
## Nombre Edad Ciudad Casado
## Primero Laura 22 Jaén FALSE
## Segundo Mario 43 Granada TRUE
## Tercero Luis 55 Jaén TRUE
Las funciones str
y summary
(funciones genéricas aplicables a distintos objetos, no solo a data frames) nos dan información sobre un data frame:
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.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
## 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
## Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
str
nos indica cuántas observaciones (filas) y variables (columnas) tiene el data frame y el nombre, tipo y varios valores de cada variable. summary
nos ofrece información descriptiva de las distintas columnas. Si la columna es numérica nos ofrece el mínimo, máximo, cuartiles 1, 2 y 3 y media. Si la columna es un factor nos cuenta el número de ocurrencias de cada nivel.
8.4.2 Modificación de un data frame
Se puede modificar datos de un data frame:
d
## Nombre Edad Ciudad Casado
## Primero Laura 22 Jaén FALSE
## Segundo Mario 43 Granada TRUE
## Tercero Luis 55 Jaén TRUE
d[3, 3] <- "Granada" # d[3, "Ciudad] <- "Granada"
d
## Nombre Edad Ciudad Casado
## Primero Laura 22 Jaén FALSE
## Segundo Mario 43 Granada TRUE
## Tercero Luis 55 Granada TRUE
Es posible también añadir o eliminar nuevas filas y columnas de diferentes formas. Veamos algunos ejemplos:
d$Peso <- c(55.2, 72.1, 85) # se añade una columna
d
## Nombre Edad Ciudad Casado Peso
## Primero Laura 22 Jaén FALSE 55.2
## Segundo Mario 43 Granada TRUE 72.1
## Tercero Luis 55 Granada TRUE 85.0
d$Casado <- NULL # se elimina una columna
d
## Nombre Edad Ciudad Peso
## Primero Laura 22 Jaén 55.2
## Segundo Mario 43 Granada 72.1
## Tercero Luis 55 Granada 85.0
t <- data.frame(Nombre = "Marta", Edad = 18, Ciudad = "Jaén", Peso = 67)
d <- rbind(d, t) # se añade una fila
d
## Nombre Edad Ciudad Peso
## Primero Laura 22 Jaén 55.2
## Segundo Mario 43 Granada 72.1
## Tercero Luis 55 Granada 85.0
## 1 Marta 18 Jaén 67.0
d2 <- d[3:nrow(d), ] # se eliminan las dos primeras filas
d2
## Nombre Edad Ciudad Peso
## Tercero Luis 55 Granada 85
## 1 Marta 18 Jaén 67
Las funciones cbind
y rbind
permiten concatenar data frames horizontal y verticalmente de manera respectiva.
8.4.3 Funciones apply
Dada la dicotomía lista-matriz de los data frames es posible aplicarles distintas variedades de la familia de funciones apply. Veamos algunos ejemplos:
apply(iris[, -5], 2, mean) # media por columnas de las 4 primeras columnas
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.843333 3.057333 3.758000 1.199333
lapply(iris[-5], mean) # lapply aplica la función a las columnas
## $Sepal.Length
## [1] 5.843333
##
## $Sepal.Width
## [1] 3.057333
##
## $Petal.Length
## [1] 3.758
##
## $Petal.Width
## [1] 1.199333
sapply(iris[, -5], mean)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.843333 3.057333 3.758000 1.199333
colMeans(iris[-5]) # también se puede usar colMeans
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.843333 3.057333 3.758000 1.199333
Observa que las funciones se aplican sobre iris[, -5]
. Es decir, se elimina la última columna porque esta es un factor y no tiene sentido calcular su media. En este ejemplo, todos los cálculos producen el mismo resultado, aunque lapply
genera como salida una lista en lugar de un vector.
También tiene sentido aplicar tapply
usando las columnas de un data frame. Se recuerda que tapply
aplica una función a los subgrupos o categorías de un factor. Por ejemplo, podemos calcular la media de las edades por ciudades:
d
## Nombre Edad Ciudad Peso
## Primero Laura 22 Jaén 55.2
## Segundo Mario 43 Granada 72.1
## Tercero Luis 55 Granada 85.0
## 1 Marta 18 Jaén 67.0
tapply(d$Edad, d$Ciudad, mean) # media de edades por ciudad
## Granada Jaén
## 49 20
tapply(d$Edad, d$Ciudad, max) # máxima edad por ciudad
## Granada Jaén
## 55 22
8.4.4 Casos completos
Un data frame puede contener valores perdidos para una o varias variables. La función complete.cases
genera un vector lógico que indica si las distintas filas de un data frame no contienen valores perdidos, es decir, son observaciones completas.
datos <- data.frame(altura = rnorm(5, 1.7, 0.1),
peso = rnorm(5, 75, 10))
datos
## altura peso
## 1 1.654182 70.18074
## 2 1.637160 95.09443
## 3 1.727791 77.00376
## 4 1.863559 72.78744
## 5 1.543505 64.45271
datos[sample(nrow(datos), size = 1), "altura"] <- NA
datos[sample(nrow(datos), size = 1), "peso"] <- NA
datos
## altura peso
## 1 1.654182 70.18074
## 2 1.637160 95.09443
## 3 1.727791 77.00376
## 4 1.863559 NA
## 5 NA 64.45271
complete.cases(datos)
## [1] TRUE TRUE TRUE FALSE FALSE
datos[complete.cases(datos), ]
## altura peso
## 1 1.654182 70.18074
## 2 1.637160 95.09443
## 3 1.727791 77.00376
8.4.5 Muestra aleatoria de un data frame
Se puede obtener una muestra aleatoria de las observaciones de un data frame usando sample
. Veamos algunos ejemplos (ejecuta antes ?letters
para ver qué contiene este vector):
(df <- data.frame(a = 1:7, b = letters[1:7]))
## a b
## 1 1 a
## 2 2 b
## 3 3 c
## 4 4 d
## 5 5 e
## 6 6 f
## 7 7 g
df[sample(nrow(df)), ] # Permutación de las filas de df
## a b
## 5 5 e
## 2 2 b
## 7 7 g
## 4 4 d
## 1 1 a
## 3 3 c
## 6 6 f
df[sample(nrow(df), 3), ] # Muestra de 3 filas
## a b
## 7 7 g
## 6 6 f
## 5 5 e
# Muestra de 5 filas con reemplazo
df[sample(nrow(df), 5, replace = TRUE), ]
## a b
## 2 2 b
## 3 3 c
## 1 1 a
## 1.1 1 a
## 1.2 1 a
8.4.6 Ordenación
Cuando estudiamos los vectores vimos la función sort
, que permite ordenar un vector. Sin embargo, sort
no nos sirve para ordenar las filas de un data frame:
set.seed(15)
df <- data.frame(a = 1:5, b = letters[5:1])
(df <- df[sample(nrow(df)), ])
## a b
## 5 5 a
## 3 3 c
## 2 2 d
## 4 4 b
## 1 1 e
(df2 <- data.frame(a = sort(df$a), b = df$b))
## a b
## 1 1 a
## 2 2 c
## 3 3 d
## 4 4 b
## 5 5 e
El problema es que se ordena la primera columna, pero la segunda queda como estaba y, por lo tanto, los datos de las distintas columnas están mezclados. Para ordenar por el contenido de una columna hay que usar la función order
. Vamos a ver su funcionamiento con un ejemplo:
La invocación o <-order(v)
produce un vector (o
) de índices de v
, de forma que o[1]
es el índice del menor elemento de v
, o[2]
es el índice del segundo elemento menor de v
y así sucesivamente. Por lo tanto v[o]
equivale a sort[v]
. La ventaja es que order
sirve para ordenar las filas de un data frame por el valor de cualquiera de sus columnas:
df[order(df$a), ] # ordenamos por la columna a
## a b
## 1 1 e
## 2 2 d
## 3 3 c
## 4 4 b
## 5 5 a
df[order(df$b), ] # ordenamos por la columna b
## a b
## 5 5 a
## 4 4 b
## 3 3 c
## 2 2 d
## 1 1 e
Además, order
permite ordenar ascendente o descendentemente, así como por varios campos (por ejemplo, por apellido y nombre).
8.5 Ejercicios
El data frame
mtcars
contiene 32 observaciones de coches, con 10 datos (variables) sobre cada coche (puedes ejecutar?mtcars
). Selecciona:- Las 5 primeras observaciones del data frame
- Todas las columnas, salvo la segunda
- Las columnas de nombre
"mpg"
y"cyl"
- Los coches con cambio manual (el valor de la columna
am
vale 1) - Los coches con 4 u 8 cilindros (la información de los cilindros está en la columna
cyl
)
Calcula cuántas flores de cada especie contiene el data frame iris (puedes usar la función
table
).Calcula la desviación estándar de la anchura de pétalo para aquellas flores de la especie setosa del conjunto de datos
iris
.Calcula la desviación estándar de la anchura de pétalo de las distintas especies del conjunto de datos
iris
.Calcula la desviación estándar de todas las columnas numéricas de
iris
para aquellas flores de la especie setosa.El data frame
mtcars
contiene información sobre varios coches (usa?mtcars
yhead(mtcars)
para familiarizarte con él). El campompg
indica cuántas millas puede recorrer un coche con un galón de combustible. Por lo tanto, cuanto mayor sea este campo más económico es el coche. Usarange
para obtener el rango de valores de la columnampg
, selecciona también aquellas observaciones en las quempg
es igual o superior a 30.Ordena el data frame
mtcar
en orden descendente por el campomgp
.Para el conjunto de datos
mtcar
, ¿cuántos coches tiene 4 o 6 cilindros (columnacyl
)?¿Por qué
mtcars[1:20]
produce un error ymtcars[1:20, ]
no?Genera un data frame a partir de
iris
, pero con sus columnas permutadas aleatoriamente.Genera un data frame a partir de
iris
, pero con sus columnas ordenadas alfabéticamente (según el nombre de las columnas).Arregla los siguientes errores comunes al indexar un data frame:
Dado el siguiente data frame:
notas <- data.frame(nombre = c("Marta", "Pilar", "Luis"), teo = c(5, 6, 3), pra = c(2, 4, 2.5) ) notas ## nombre teo pra ## 1 Marta 5 2.0 ## 2 Pilar 6 4.0 ## 3 Luis 3 2.5
Añade una variable al data frame con la nota final (suma de nota teoría más prácticas)
El siguiente código genera un data frame con las notas en 3 exámenes de 10 alumnos:
set.seed(5) notas <- data.frame(nota1 = runif(10, max = 10), nota2 = runif(10, max = 10), nota3 = runif(10, max = 10), grupo = sample(c("G1", "G2"), size = 10, replace = TRUE) )
- Añade una columna con la nota final de cada alumno. Se calcula como la media de las tres notas.
- Calcula la nota final media por grupo.
- Obtén un data frame con los alumnos del grupo 1 que han aprobado.
- Obtén un data frame con los alumnos que han aprobado el primer examen, pero no han aprobado ni el segundo ni el tercero.
8.6 Soluciones a los ejercicios
# Consultas sobre el data frame mtcars
mtcars[1:5, ] # Las 5 primeras observaciones del data frame
mtcars[, -2] # Todas las columnas, salvo la segunda
mtcars[, c("mpg", "cyl")] # Las columnas de nombre mpg y cyl
mtcars[mtcars[, "am"] == 1, ] # Coches con cambio manual
mtcars[mtcars[, "cyl"] %in% c(4, 8), ] # Coches con 4 u 8 cilindros
mtcars[mtcars[, "cyl"] == 4 | mtcars[, "cyl"] == 8, ]
# Contar flores de cada especie en iris
table(iris$Species)
##
## setosa versicolor virginica
## 50 50 50
# Desviación estándar de la anchura de pétalo de las setosa de iris
sd(iris[iris$Species == "setosa", "Petal.Width"])
## [1] 0.1053856
# Desviación estándar de la anchura de pétalo de las
# distintas especies de iris
tapply(iris$Petal.Width, iris$Species, sd)
## setosa versicolor virginica
## 0.1053856 0.1977527 0.2746501
# Desviación estándar de las columnas numéricas de las setosa
apply(iris[iris$Species == "setosa", -5], 2, sd)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 0.3524897 0.3790644 0.1736640 0.1053856
# Trabajar con campo mpg de mtcars
range(mtcars$mpg) # rango de valores del campo
## [1] 10.4 33.9
mtcars[mtcars$mpg >= 30, ] # coches con mpg >= 30
## mpg cyl disp hp drat wt qsec vs am gear carb
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
# Ordenar mtcar por campo mpg en orden decreciente
mtcars[order(mtcars$mpg, decreasing = TRUE), ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
## Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
## Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
## Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
## Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
## Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
## Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
## AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
## Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
## Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
## Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
## Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
## Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
# Número de coches con 4 o 6 cilindros en mtcars
sum(mtcars$cyl == 4 | mtcars$cyl == 6)
## [1] 18
sum(mtcars$cyl %in% c(4, 6)) # alternativamente
## [1] 18
# Error al indexar mtcars
# mtcars[1:20] intenta seleccionar las primeras 20 columnas, pero mtcars tiene 11 columnas
# mtcars[1:20, ] selecciona las primeras 20 filas
# Errores comunes al indexar un data frame
mtcars[mtcars$cyl == 4, ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
mtcars[mtcars$cyl <= 5, ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
mtcars[mtcars$cyl == 4 | mtcars$cyl == 6, ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
## Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
# Añade una columna al data frame de notas con la nota final
notas <- data.frame(nombre = c("Marta", "Pilar", "Luis"),
teo = c(5, 6, 3),
pra = c(2, 4, 2.5)
)
notas$final <- notas$teo + notas$pra
notas
## nombre teo pra final
## 1 Marta 5 2.0 7.0
## 2 Pilar 6 4.0 10.0
## 3 Luis 3 2.5 5.5
# Ejercicio de 3 notas de 10 alumnos
set.seed(5)
notas <- data.frame(nota1 = runif(10, max = 10),
nota2 = runif(10, max = 10),
nota3 = runif(10, max = 10),
grupo = sample(c("G1", "G2"), size = 10,
replace = TRUE)
)
notas
## nota1 nota2 nota3 grupo
## 1 2.002145 2.732849 8.902071 G2
## 2 6.852186 4.905132 7.207010 G1
## 3 9.168758 3.184040 2.113403 G1
## 4 2.843995 5.591728 2.257173 G2
## 5 1.046501 2.625931 1.399837 G2
## 6 7.010575 2.018752 4.799139 G2
## 7 5.279600 3.875257 4.374119 G2
## 8 8.079352 8.878698 9.659641 G1
## 9 9.565001 5.549226 1.419139 G1
## 10 1.104530 8.421794 9.549789 G1
notas$nf <- (notas$nota1 + notas$nota2 + notas$nota3) / 3
# alternativamente: notas$nf <- rowMeans(notas[1:3])
head(notas, 2)
## nota1 nota2 nota3 grupo nf
## 1 2.002145 2.732849 8.902071 G2 4.545688
## 2 6.852186 4.905132 7.207010 G1 6.321443
tapply(notas$nf, notas$grupo, mean) # nota media por grupo
## G1 G2
## 6.377180 3.783978
notas[notas$nf >= 5 & notas$grupo == "G1", ] # alumnos del grupo 1 aprobados
## nota1 nota2 nota3 grupo nf
## 2 6.852186 4.905132 7.207010 G1 6.321443
## 8 8.079352 8.878698 9.659641 G1 8.872564
## 9 9.565001 5.549226 1.419139 G1 5.511122
## 10 1.104530 8.421794 9.549789 G1 6.358704
# alumnos que aprueban el examen 1, pero no el 2 ni el 3
notas[notas$nota1 >=5 & notas$nota2 < 5 & notas$nota3 < 5, ]
## nota1 nota2 nota3 grupo nf
## 3 9.168758 3.184040 2.113403 G1 4.822067
## 6 7.010575 2.018752 4.799139 G2 4.609488
## 7 5.279600 3.875257 4.374119 G2 4.509659