Aprendiendo R por su cuenta, primera parte.

Usted está aquí

Esta es una lista de supermercado de cosas que vas a ir aprendiendo y que le servirá para ver donde está. Están aproximadamente en orden. Como ya ha asistido a varias sesiones de laboratorio computacional, doy por hecho que está OK al menos hasta el item 7. Del 8 al 11 o bien lo sabe o le va a parecer conocido cuando lo vuelva a ver. Asumiré que no sabe nada desde el 11 hacia adelante. Al llegar al final de la lista tendrá todas las habilidades básicas para escribir sus propios programas y seguir los ejemplos, tutoriales y explicaciones encontrará en internet.

  1. Sabes que es R y RStudio?

  2. Puedes comenzar una sesión en RStudio?

  3. Sabes qué es un script de R?

  4. Si tienes un script en tus achivos, lo puedes abrir?

  5. Si tienes un script abierto en RStudio, puedes ejecutarlo? (línea por línea o todo a la vez)

  6. Puedes crear un nuevo script en RStudio?

  7. Sabes cómo asignarle un valor a un objeto? (e.g. “a <- 3+7”)

  8. Sabes como crear un vector?

  9. Sabes como usar plot e hist para crear diagramas de dispersión e histogramas básicos?

  10. Sabes lo que son las funciones en R y cómo se usan? (por ejemplo, la función mean)

  11. Sabes como instalar y cargar un paquete de R?

  12. Sabes como crear un data.frame y un tibble?

  13. Sabes como crear una lista?

  14. Sabes como seleccionar partes de un vector? de un data.frame? de una lista?

  15. Sabes como manipular un data.frame o un tibble?

  16. Puedes crear un diagrama de dispersión, un diagrama de caja usando ggplot2?

  17. Sabes cómo importar una hoja de Excel en R?

  18. Has escrito un for loop?

  19. Has escrito una función?

Vectores y primeras funciones

Vectores

Uno de los objetos más usados y más sencillos es el vector. Puede ser creado en forma manual (usando la función c, combinar) o como el resultado final que nos devuelve alguna otra función de R (como rnorm):

# crear manualmente un vector de números y asignar el vector a un objeto que llamaremos x
x <- c(2, 65, 8, 33)

# crear manualmente un vector de strings (texto) y asignar el vector a un objeto que llamaremos y
y <- c("mejor curso", "o mejor curso")

# crear manualmente un vector lógico  asignar el vector a un objeto que llamaremos z
z <- c(TRUE, FALSE, TRUE)

# para imprimir los valores en la consola debemos escribir el nombre del objeto y ejecutar o bien 
# usar la función print

x
## [1]  2 65  8 33
y
## [1] "mejor curso"   "o mejor curso"
print(z)
## [1]  TRUE FALSE  TRUE

Primeras funciones

Ya hemos usado dos funciones en el ejemplo anterior: c y print. Lo primero que debemos notar es que siempre que se usan debe agregarse un paréntesis después del nombre de la función: c() y print(). Lo segundo es que dentro del paréntsis van objectos que dentro de la función reciben el nombre de argumentos. Algunos argumentos tienen un valor por defecto, que es usado por la función y otros que, al no tener asignado un valor por defecto, deben pedirle al usuario que especifique un valor al momento de usar la función.

Que son los argumentos y aquellos que tienen valor por defecto quedan más claro si estudiamos la función mean:

# un vector igual a x, pero agregando un missing value al final 
# missing values en R se representan con NA e indican que no existe 
# un valor para ese elemento

xna <- c(2, 65, 8, 33, NA)

# usemos la función mean
mean(x = xna)
## [1] NA
# oops, que pasó? usemos la función otra vez
# pero especificando el valor de otro de sus argumentos
mean(x = xna, na.rm = TRUE)
## [1] 27
# ahora si calculó el promedio, excluyendo el valor NA

Por qué cambió el resultado al incluir explicitamente na.rm = TRUE dentro de la función? La explicación la encontramos al ver la página de ayuda de mean, que contiene, entre otras cosas, las siguientes explicaciones:

  • Default S3 method: mean(x, trim = 0, na.rm = FALSE, …). Qué aprendemos? Que la función mean tiene tres argumentos, x, trim y na.rm, y que dos de ellos tiene valores por defecto de modo que, si estamos conformes, podemos ahorrarnos trabajo y no especificarlos al usar la función. Esta es la razón de por qué es imprescindible que nosotros le asignemos un valor a x. El argumento trim tiene asignado el valor 0 y es lo que va a usar a menos que especifiquemos otro valor. Finalmente, vemos que el argumento na.rm tiene asignado por defecto el valor FALSE y es el que va a usar a menos que especifiquemos otro valor, tal como lo hicimos la segunda vez que intentamos calcular el promedio: mean(x = xna, na.rm = TRUE).

  • x: An R object. Currently there are methods for numeric/logical vectors and date, date-time and time interval objects. Complex vectors are allowed for trim = 0, only.. Nos dice que x debe ser un objeto válido en R y que actualmente la función acepta vectores lógicos, vectores numéricos y vectores de fechas.

  • na.rm: a logical value indicating whether NA values should be stripped before the computation proceeds.. OK, este es el argumento que nos importa! Dice que debe ser igual a un valor lógico (es decir TRUE o FALSE) indicando si debe remover los NA antes de calcular el promedio. Y como el valor por defecto que tiene ese argumento es FALSE, así es que no removerá ningún NA a menos que explicitamente escribamos rm.na = TRUE al usar la función.

En R y en muchos lenguajes de programación la suma de un número con un NA es igual a NA, la división de un número por un NA es igual a NA … en general todas las operaciones ariméticas que involucran al menos un NA resultan en NA.

Un último punto sobre argumentos: en la definición de la función los argumentos tienen un orden (x primero, trim segundo, etc.). Podemos obviar los nombres de los argumentos y simplemente escribir su valor, por ejemplo mean(xna, 0, FALSE) y R va a descansar en la posición de cada uno para saber a qué argumento se refiere, es decir asume que el primer valor es el de x, el segundo es el de trim, etc. La ventaja de usar los nombres es hacer más explícito al lector nuestras opciones y, adicionalmente aquellos argumentos en que usemos los nombres pueden aparecer en cualquier orden.

El tema de las funciones y los argumentos puede ser un poco pesado de digerir, por lo que aquí agregaremos unas líneas de código a modo de ilustración:

# recordemos nuestro objecto "xna":
xna
## [1]  2 65  8 33 NA
# Hacer esto:
mean(x = xna)
## [1] NA
# ... es equivalente a usar los valores por defecto:
mean(x = xna, trim = 0, na.rm = FALSE)
## [1] NA
# Esto cambia el valor de na.rm pero mantiene el de trim
mean(x = xna, na.rm = TRUE)
## [1] 27
# Esto cambia el valor de trim pero mantiene el de na.rm
mean(x = xna, trim =0.2)
## [1] NA
# Hacer esto:
mean(x = xna, trim = 0.2, na.rm = TRUE)
## [1] 27
# .. es equivalente a 
mean(xna, 0.2, TRUE) # (porque se guía por la posición de los arg)
## [1] 27
# Sin embargo lo siguiente es inválido:
# mean(xna, TRUE, 0.2) # (porque le asigna TRUE a trim y 0.2 a na.rm)

# Pero usar los nombres nos  permite cambiar el orden:
mean(x = xna, na.rm = TRUE, trim = 0.2)
## [1] 27
# Posición y nombre se pueden usar en la misma llamada a mean:
# x lo entiende por posición y los otros dos por nombre
mean(xna, na.rm = TRUE, trim = 0.2)
## [1] 27
# esa última formulación es muy común porque el primer argumente suele
# ser el más usado y el que todo el mundo tiene claro cuál es.

Ejercicio

  • Para testear si entendió, lea el help de la función rnorm (por ejemplo, escribiendo ?rnorm en la consola), identifique cuántos argumentos tiene, cuáles argumentos tienen valores por defecto y cuales son estos valores por defecto.

Recursos relacionados

A esta altura, pueden consultar las siguientes secciones de libros o tutoriales que están en línea para repasar y complementar lo aprendido hasta aquí:

Otras funciones comunes en estadística

A modo de práctica, le invitamos a probar estas otras funciones muy comunes en estadística, que incluyen gráficos, estadísticas descriptiva y generación de numeros aleatorios. Pruebe usted también otros valores para los argumentos para explorar su comportamiento.

Comencemos generando números aleatorios desde una distribución normal y desde una distribución chi-cuadrado

# chi-squared
rchisq(n = 6, df = 4)
## [1] 3.8525159 3.4623350 5.9294927 2.1083952 0.7920738 1.0243940
rchisq(n = 10, df = 4)
##  [1] 4.9737885 0.5679142 7.7560177 1.3119055 2.8621852 3.4673769 1.1259361
##  [8] 8.2362929 2.4470159 3.0303367
a <- rchisq(n = 1000, df = 4)


# normal
rnorm(n = 6)
## [1] -0.50693959 -0.08515794  0.56346433 -1.45268894 -0.38601363 -2.62511271
rnorm(n = 10)
##  [1]  0.7638260  2.4493917  1.5589733  1.5476578  0.9856659  0.4459846
##  [7]  0.4743983  1.2015013 -0.7956240  1.3044471
rnorm(n = 10, mean = 2)
##  [1] 1.67715232 3.19892033 0.05934536 3.54041959 2.28720608 1.78160182
##  [7] 2.16203717 1.26474768 3.01864375 3.18265250
b <- rnorm(1000, mean = 2)

# histograms
hist(a, breaks = 40)

hist(b)

# desviación estandard
sd(a)
## [1] 2.981452
# quantiles de los datos, ignorando la distribución
quantile(a)
##         0%        25%        50%        75%       100% 
##  0.1438892  1.9854047  3.3538224  5.5232897 19.4308765
quantile(a, 0.3)
##      30% 
## 2.232755
quantile(a, c(0.3, 0.6))
##      30%      60% 
## 2.232755 4.048580
# quantiles de la distribución, teóricos 
qchisq(df = 4, p = c(0.3, 0.6))
## [1] 2.194698 4.044626
# boxplot
boxplot(a)

# similar al box plot, pero como texto
summary(a)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1439  1.9854  3.3538  4.1285  5.5233 19.4309

Practique usando funciones

Ejercicio

  • Prueba las siguientes funciones: consulta su página de ayuda, mira los ejemplos en la página de ayuda, pruébalos en la consola, prueba distintos valores en algunos argumentos y vea que sucede

    • rbinom
    • qbinom
    • pnorm
    • qexp
    • head
    • tail
  • Use la función rbinom para generar una muestra de 22 números aleatorios desde una distribución binomial con parámetros size = 5 y prob = 0.6.

  • Use la función pnorm para obtener el valor de \(y\) tal que \(Prob(x \leq y) = 0.92\), donde \(x\) proviene de una distribución normal con media igual a 0 y varianza igual a 1. ¿Es necesario cambiar alguno de los valores por defecto que usa la función pnorm?

Paquetes

Un paquete (package) en R es un archivo que contiene nuevas funciones. Cuando \(R\) se instala por primera vez, se instalan algunos paquetes, como los paquetes base, utils y stats que contienen las funciones más elementales de R. No sólo vienen instalados cuando se instala R, si no que que cuando se abre un sesión (por ejemplo en RStudio) Ud. automáticamente tiene acceso a dichas funciones: por ejemplo podemos usar las funciones c y mean del paquete base, así como la función pnorm del paquete stats.

A menudo, sin embargo, queremos usar funciones que se encuentran en otros paquetes. Por ejemplo, si queremos estimar la probabilidad acumulada en una Normal Estándar Truncada por la derecha en el valor 1 (es decir, una distribución normal estándar a la que se amputó la sección a la derecha del número 1), o generar números aleatorios prevenientes de dicha distribución, nada en los paquetes base o stats nos sirve. Una breve búsqueda en internet nos revela que existe un paquete de R llamado extraDistr que contiene funciones apropiadas para estos fines, en concreto las funciones ptnorm y rtnorm.

Una consulta al panel Packages en RStudio nos revela que no tenemos dicho paquete en nuestro computador: deberemos instalarlo. La funciónque instala paquete se llama install.packages y fácilmente utilizable vía el panel Packages en RStudio, basta con hacer click en Install y seguir las instrucciones (básicamente escribir el nombre del paquete que deseamos instalar). En la consola se va a ejecutar el comando install.packages(“extraDistr”) y apareceran algunos mensajes relativos al proceso de instalar

OK, ahora usemos las funciones que queríamos, por ejemplo ptnorm:

ptnorm(0.50, b = 1)
## Error in ptnorm(0.5, b = 1): could not find function "ptnorm"

¿No la encuentra? ¿Pero cómo? Si acabamos de instalar el paquete extraDistr!! … Bueno porque una cosa es instalar un paquete y otra es cargarlo. Vea lo que ocurre si usamos la función library antes de usar ptnorm:

library(extraDistr)
ptnorm(0.50, b = 1)
## [1] 0.8218539

Ahora sí! Lo que ocurre es que library(extraDistr) trae a la sesión de R en curso todos los nombres de las fuciones contenidas en ese paquete. Una vez que library(extraDistr) se ha ejecutado, podemos usar hasta el final de la sesión todas las funciones que ahí existen. Cuando reiniciamos R debemos ejecutar library(extraDistr) otra vez para poder usar las funciones. Los paquetes base y stats no son distintos, lo que ocurre es que los comandos library(base) y library(stats) son ejecutados tras bambalinas cada vez que se inicia un sesión de R, sin que usted se entere.

Se recomienda poner en las primeras líneas de su script todos los comandos del tipo library(xyz) que se requieran para correr el script completo, de este modo cualquier usuario sabe de antemano si debe instalar algún paquete que le falte.

Ejercicio

  • Instale el paquete tibble
  • Instale el paquete readxl
  • Instale el paquete datos

Data frames y tibbles

Data frames

El tipo de objeto más usado para representar un conjunto de datos es un data frame. Aquí hay un ejemplo:

print(mtcars)
##                      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
## 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
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## 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
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## 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
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    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
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    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
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
class(mtcars)
## [1] "data.frame"

Lo que vemos son las seis primeras filas del data frame, que contiene once columnas, que representan once variables. Además vemos que las filas tiene nombres, en este caso el modelo de automóvil. Una pregunta válida es si no será mejor tener los modelos de autos como una variable igual a las otras once.

Por ahora nos preocuparemos de dos formas de crear data frames: a mano y como resultado de importar un hoja de excel

# la funcion read.csv2 retorna un data frame 
notas_curso_df <- read.csv2("notas_curso.csv")

notas_curso_df
##   nombre solemne controles examen final
## 1  pedro     3.5       5.0    3.0   3.8
## 2   juan     4.0       3.0    4.0   3.7
## 3  diego     5.0       6.0    4.8   5.2
## 4  frida     6.0       6.5    5.5   6.0
## 5 simone     5.5       7.0    6.0   6.2
## 6 judith     7.0       6.0    6.0   6.3
class(notas_curso_df)
## [1] "data.frame"
# también se podría haber creado manualmente
# una COLUMNA a la vez
notas_curso_df_manual <- data.frame(
  nombre = c("pedro", "juan", "diego", "frida", "simone", "judith"),
  solemne = c(3.5, 4, 5, 6, 5.5, 7),
  controles = c(5, 3, 6, 6.5, 7, 6),
  examen = c(3, 4, 4.8, 5.5, 6, 6),
  final = c(3.8, 3.7, 5.2, 6, 6.2, 6.3))

Es perfectamente posible crear previamente el contenido de alguna columna como un vector y luego simplemente escribir el nombre de la columna, el signo igual, y nuestro vector.

Tibbles

Tibbles son tipo especial data frame, creadas en el contexto de los paquetes que forman el tidyverse. Las formas de manipularlos son generalmente las mismas. En una primera etapa la principal diferencia que van a encontrar es que la forma en que aparecen en la consola es distinta: las data frames tratan de mostrar todas las filas y columnas en la consola mientras que las tibble muestran solo las primeras filas y la cantidad de columnas que caben en el ancho de la consola. Adicionalmente entrega información sobre el número de filas y columnas y sobre el tipo (clase) de datos en cada columna (p.e. dbl para clase double, chr para datos clase character, etc.)

Veamos a modo de ejemplo una tibble de nombre diamantes:

library(datos) # instaló el package "datos", cierto? 
diamantes
## # A tibble: 53,940 x 10
##    precio quilate corte     color claridad profundidad tabla     x     y     z
##     <int>   <dbl> <ord>     <ord> <ord>          <dbl> <dbl> <dbl> <dbl> <dbl>
##  1    326   0.23  Ideal     E     SI2             61.5    55  3.95  3.98  2.43
##  2    326   0.21  Premium   E     SI1             59.8    61  3.89  3.84  2.31
##  3    327   0.23  Bueno     E     VS1             56.9    65  4.05  4.07  2.31
##  4    334   0.290 Premium   I     VS2             62.4    58  4.2   4.23  2.63
##  5    335   0.31  Bueno     J     SI2             63.3    58  4.34  4.35  2.75
##  6    336   0.24  Muy bueno J     VVS2            62.8    57  3.94  3.96  2.48
##  7    336   0.24  Muy bueno I     VVS1            62.3    57  3.95  3.98  2.47
##  8    337   0.26  Muy bueno H     SI1             61.9    55  4.07  4.11  2.53
##  9    337   0.22  Regular   E     VS2             65.1    61  3.87  3.78  2.49
## 10    338   0.23  Muy bueno H     VS1             59.4    61  4     4.05  2.39
## # … with 53,930 more rows
class(diamantes)
## [1] "tbl_df"     "tbl"        "data.frame"

En la última linea podemos ver que pertenecen a la clase de los data frames, pero son un tipo especial llamado tbl_df.

La forma en que se crean son muy similares a los data frames. En particular algunas funciones retornan los resutados en forma de tibble o pueden ser creadas directamente por el usuario. Tal como en el caso anterior, veamos una función que importa datos y retorna un tibble y una manera manual de crear el mismo objeto:

# read_excel, como en general las funciones del tidyverse
# retorna un tibble en vez de un data frame
library(readxl) # instaló el package "readxl", cierto?
notas_curso_tbl <- read_excel("notas_curso.xlsx")

# la creacion manual es muy similar a un data frame   

library(tibble) # instalarlo era parte de un ejercicio más arriba
notas_curso_tbl_manual <- tibble(
  nombre = c("pedro", "juan", "diego", "frida", "simone", "judith"),
  solemne = c(3.5, 4, 5, 6, 5.5, 7),
  controles = c(5, 3, 6, 6.5, 7, 6),
  examen = c(3, 4, 4.8, 5.5, 6, 6),
  final = c(3.8, 3.7, 5.2, 6, 6.2, 6.3))

Ejercicios

En el siguiente ejercicio van a crear unos vectores y luego usaran esos vectores como columnas para crear un data frame

  • Cree un data frame con dos columnas: valor_x y valor_y. En la primera columna debe usar el vector mis_x y en la segunda, mis_y. Guarde el data frame en un objeto de nombre df_xy
# primero creamos dos vectores de numeros
mis_x <- seq(1, 20)
mis_y <- 3 + 0.5*mis_x + rnorm(20)

# breve vistazo
print(mis_x)
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
print(mis_y)
##  [1]  1.336657  6.137163  4.162461  5.606822  5.346281  5.638097  6.194906
##  [8]  8.135004  7.571069  7.389824  8.710711  7.954121  9.671387  8.375363
## [15] 10.938638 12.259975 11.865509 11.407012 11.184863 11.436653
# Con esos vectores, cree el data frame pedido.

# considere lo siguiente como ejemplo:

# df_xy <- data.frame(valor_x = un vector, valor_y = el otro vector)

# ahora usted: