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.

Implementación de un data frame.
Implementación de un data frame.

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:

datos <- edit(data.frame()) # Abre un editor para introducir los datos

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:

View(iris)

Ejercicio. Crea un data frame que incluya las notas de 3 estudiantes (observaciones) en los exámenes de teoría y prácticas.

notas <- data.frame(Nombre = c("Laura", "Pedro", "Luis"),
                    Teoria = c(6, 7.3, 9.0),
                    Practicas = c(8.2, 7.0, 9.2)
)
notas
##   Nombre Teoria Practicas
## 1  Laura    6.0       8.2
## 2  Pedro    7.3       7.0
## 3   Luis    9.0       9.2

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:

d[, "Nombre"] # Selecciona 1 columna, devuelve un vector
## [1] "Laura" "Mario" "Luis"
d[, "Nombre", drop = FALSE] # Ahora devuelve un data frame
##   Nombre
## 1  Laura
## 2  Mario
## 3   Luis

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"`
mtcars[, -2] # todas las columnas salvo la última (indexación matriz)
mtcars[-2]   # todas las columnas salvo la última (indexación lista)
mtcars[, c("mpg", "cyl")] # columnas mpg y cyl (indexación matriz)
mtcars[c("mpg", "cyl")]   # columnas mpg y cyl (indexación lista)

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:

v <- c(23, 19, 5, 30, 18)
(o <- order(v))
## [1] 3 5 2 1 4
v[order(v)]
## [1]  5 18 19 23 30

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

  1. 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)
  2. Calcula cuántas flores de cada especie contiene el data frame iris (puedes usar la función table).

  3. Calcula la desviación estándar de la anchura de pétalo para aquellas flores de la especie setosa del conjunto de datos iris.

  4. Calcula la desviación estándar de la anchura de pétalo de las distintas especies del conjunto de datos iris.

  5. Calcula la desviación estándar de todas las columnas numéricas de iris para aquellas flores de la especie setosa.

  6. El data frame mtcars contiene información sobre varios coches (usa ?mtcars y head(mtcars) para familiarizarte con él). El campo mpg 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. Usa range para obtener el rango de valores de la columna mpg, selecciona también aquellas observaciones en las que mpg es igual o superior a 30.

  7. Ordena el data frame mtcar en orden descendente por el campo mgp.

  8. Para el conjunto de datos mtcar, ¿cuántos coches tiene 4 o 6 cilindros (columna cyl)?

  9. ¿Por qué mtcars[1:20] produce un error y mtcars[1:20, ] no?

  10. Genera un data frame a partir de iris, pero con sus columnas permutadas aleatoriamente.

  11. Genera un data frame a partir de iris, pero con sus columnas ordenadas alfabéticamente (según el nombre de las columnas).

  12. Arregla los siguientes errores comunes al indexar un data frame:

    mtcars[mtcars$cyl = 4, ]
    mtcars[mtcars$cyl <= 5]
    mtcars[mtcars$cyl == 4 | 6, ]
  13. 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)

  14. 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
# Permuta aleatoriamente las columnas de iris
iris[, sample(ncol(iris))]
# Ordenar las columnas de iris por nombre
iris[, sort(colnames(iris))]
# 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