Lenguaje

Apuntes sobre el lenguaje de programación y su uso.


Introducción

R es tanto un lenguaje de programación de propósito específico (enfocado en la estadística) como un entorno para su uso. Es un proyecto GNU, similar al lenguaje y al entorno S, desarrollados en Bell Laboratories (anteriormente AT&T, ahora Lucent Technologies) por John Chambers y colegas, aunque puede considerarse una implementación diferente de S. Hay algunas diferencias importantes, pero gran parte del código escrito para S se ejecuta sin cambios en R.

R proporciona una amplia variedad de técnicas estadísticas (modelado lineal y no lineal, pruebas estadísticas clásicas, análisis de series temporales, clasificación, agrupamiento), cuenta con capacidades gráficas y es altamente extensible. El lenguaje S suele ser el vehículo elegido para la investigación en metodología estadística, mientras que R ofrece una alternativa de código abierto para participar en esa actividad.

Uno de los puntos fuertes de R es la facilidad con la que se pueden producir elementos visuales de buen diseño con calidad de publicación, que incluyen símbolos matemáticos y fórmulas donde sea necesario. Se ha tenido mucho cuidado con los valores predeterminados de las opciones de diseño menores en los gráficos, pero el usuario mantiene el control total.

R está disponible como software libre bajo los términos de la Licencia Pública General GNU de la Free Software Foundation en forma de código fuente. Se compila y se ejecuta en una amplia variedad de plataformas UNIX y sistemas similares (incluidos FreeBSD y Linux), así como en Windows y macOS.


Acciones básicas

Siendo un lenguaje interpretado, tanto las facilidades de programación como las de ejecución en la consola deben tomarse en cuenta. Por ejemplo, puede ser empleado como calculador:

> 5+7
[1] 12

Para asignar el resultado de 5 + 7 a una nueva variable llamada x, se escribe x <- 5 + 7. Esto puede leerse como “x recibe 5 más 7”.

> x<-5+7

Para ver el contenido de una variable en la consola, basta con introducir su nombre y presionar Enter.

> x
[1] 12

Cualquier objeto que contiene datos es una «estructura de datos». Los vectores numéricos son el tipo más simple de estructura de datos en R. De hecho, incluso un solo número se considera un vector de longitud 1. La forma más sencilla de crear un vector es mediante la función c(), que significa “concatenar” o “combinar”. Para crear un vector que contenga los números 1.1, 9 y 3.14, escribe c(1.1, 9, 3.14).

> z<-c(1.1,9,3.14)

Escribimos z para ver su contenido; y observaremos que no hay comas separando los valores en la salida.

> z
[1] 1.10 9.00 3.14

En caso de dudas sobre una función en particular, se puede acceder a los archivos de ayuda incorporados de R mediante el comando ?. Por ejemplo:

> ?c

Los vectores numéricos pueden usarse en expresiones aritméticas.

> z*2+100
[1] 102.20 118.00 106.28

R multiplicará cada uno de los tres elementos de z por 2. Luego sumará 100 a cada elemento para obtener el resultado que se muestra arriba. Otros operadores aritméticos comunes son +, -, / y ^, donde x^2 significa “x al cuadrado”. Para obtener la raíz cuadrada, se usa la función sqrt(); y para obtener el valor absoluto, usa la función abs().

Calculamos la raíz cuadrada de z - 1 y la asignamos a una nueva variable llamada my_sqrt.

> my_sqrt<-sqrt(z-1)
> my_sqrt
[1] 0.3162278 2.8284271 1.4628739
> my_div<-z/my_sqrt
> my_div
[1] 3.478505 3.181981 2.146460

Cuando hicimos z * 2 + 100 en el ejemplo anterior, z era un vector de longitud 3, pero técnicamente 2 y 100 son, cada uno, vectores de longitud 1. Detrás de escena, R está “reciclando” el 2 para formar un vector de «doses» y el 100 para formar un vector de «cientos». En otras palabras, se le pide a R que calcule z * 2 + 100, y lo que realmente calcula es esto: z * c(2, 2, 2) + c(100, 100, 100).

Para ver otro ejemplo de cómo funciona este “reciclaje” de vectores, intentemos sumar c(1, 2, 3, 4) y c(0, 10).

> c(1,2,3,4)+c(0,100)
[1]   1 102   3 104

> c(1,2,3,4)+c(0,10)
[1]  1 12  3 14

Si la longitud del vector más corto no divide de manera exacta la longitud del vector más largo, R seguirá aplicando el método de “reciclaje”, pero mostrará una advertencia para indicarte que algo sospechoso podría estar ocurriendo.

Otro ejemplo:

> c(1, 2, 3, 4) + c(0, 10, 100)
[1]   1  12 103   4
Warning message:
In c(1, 2, 3, 4) + c(0, 10, 100) :
longer object length is not a multiple of shorter object length

Espacio de trabajo e interacción con archivos

Mediante getwd() se determina qué directorio se está usando en la sesión de R como directorio de trabajo actual

> getwd()
[1] "/Users/lalo/Documents/Personal/Education/Coursera/DSS/C2-R"

Algunos comandos de R son similares a sus homólogos en Linux o en macOS, pero pueden variar en cuanto a su propósito. Por ejemplo, listar todos los objetos del espacio de trabajo local usando ls().

> ls()
[1] "my_div"  "my_sqrt" "x"       "y"       "z"

Muestra variables en el entorno, no archivos. Mostramos los archivos del directorio de trabajo con list.files() o dir().

> dir()
[1] "Week1" "Week2" "Week3" "Week4"

Usar la función args() sobre el nombre de una función también es una forma práctica de ver qué argumentos puede recibir dicha función. Por ejemplo, para determinar los argumentos de list.files():

> args(list.files)
function (path = ".", pattern = NULL, all.files = FALSE, full.names = FALSE,
recursive = FALSE, ignore.case = FALSE, include.dirs = FALSE,
no.. = FALSE)
NULL

Asignamos el valor del directorio de trabajo actual a la variable old.dir

> old.dir<-getwd()

y usamos dir.create() para crear un directorio llamado testdir dentro del directorio de trabajo actual.

> dir.create()
Error in dir.create() : argument "path" is missing, with no default
> dir.create("testdir")

Establece tu directorio de trabajo en testdir con el comando setwd().

> setwd("testdir")

Crea un archivo llamado mytest.R en tu directorio de trabajo usando la función file.create().

> file.create("mytest.R")
[1] TRUE

list.files() muestra que el directorio solo contiene mytest.R.

> list.files()
[1] "myTest.R"

Verifica si mytest.R existe en el directorio de trabajo usando la función file.exists().

> file.exists("mytest.R")
[1] TRUE

Accede a la información del archivo mytest.R usando file.info().

> file.info("mytest.R")
size isdir mode               mtime               ctime               atime uid gid
mytest.R    0 FALSE  644 2015-03-28 11:59:16 2015-03-28 11:59:16 2015-03-28 11:58:56 503  20
uname grname
mytest.R  lalo  staff

Puedes usar el operador $, por ejemplo file.info(«mytest.R»)$mode, para obtener elementos específicos.

Cambia el nombre del archivo mytest.R a mytest2.R usando file.rename().

> file.rename("mytest.R", "mytest2.R")
[1] TRUE

Haz una copia de mytest2.R llamada mytest3.R usando file.copy().

> file.copy("mytest2.R", "mytest3.R")
[1] TRUE

Proporciona la ruta relativa al archivo mytest3.R usando file.path().

> file.path("mytest3.R")
[1] "mytest3.R"

Puedes usar file.path para construir rutas de archivos y directorios que sean independientes del sistema operativo en el que se esté ejecutando tu código de R. Pasa ‘folder1’ y ‘folder2’ como argumentos a file.path para crear una ruta independiente de la plataforma.

> file.path("folder1", "folder2")
[1] "folder1/folder2"

Consulta la documentación de dir.create escribiendo ?dir.create. Observa el argumento recursive. Para crear directorios anidados, recursive debe establecerse en TRUE.

> ?dir.create()

Una vez más. ¡Puedes hacerlo! O escribe info() para ver más opciones.

?dir.create mostrará la documentación.

> ?dir.create

¡Eso es correcto!

Crea un directorio llamado testdir2 en el directorio de trabajo actual y, dentro de él, un subdirectorio llamado testdir3, todo en un solo comando usando dir.create() y file.path().

> dir.create(filepath("testdir2", "testdir3"), recursive=true)
Error in dir.create(filepath("testdir2", "testdir3"), recursive = true) :
could not find function "filepath"
> dir.create(file.path("testdir2", "testdir3"), recursive=true)
Error in dir.create(file.path("testdir2", "testdir3"), recursive = true) :
object 'true' not found
> dir.create(file.path("testdir2", "testdir3"), recursive=TRUE)

Para eliminar un directorio necesitas usar el argumento recursive = TRUE con la función unlink(). Si no usas recursive = TRUE, R asume que quizá no estás consciente de que estás eliminando un directorio y todo su contenido. R razona que, si no especificas que recursive es igual a TRUE, quizá no sabes que hay algo dentro del directorio que intentas eliminar. R intenta evitar que cometas un error.

> unlink("testdir2", recursive=TRUE)

Regresa a tu directorio de trabajo original usando setwd(). Recuerda que creamos la variable old.dir con la ruta completa del directorio de trabajo original al inicio de estas preguntas.

> setwd(old.dir)

Elimina el directorio testdir que acabas de dejar, junto con todo su contenido.

> dir.remove("testdir", recursive=TRUE)
Error: could not find function "dir.remove"
> dir.delete("testdir", recursive=TRUE)
Error: could not find function "dir.delete"
> unlink("testdir", recursive=TRUE)

Secuencias de números

La forma más sencilla de crear una secuencia de números en R es usando el operador :. Escribe 1:20 para ver cómo funciona.

> 1:20
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

Eso nos dio todos los enteros entre 1 y 20, incluyendo ambos extremos. También podríamos usarlo para crear una secuencia de números reales. Por ejemplo, prueba pi:10.

> pi:10
[1] 3.141593 4.141593 5.141593 6.141593 7.141593 8.141593 9.141593

El resultado es un vector de números reales que inicia con pi, es decir, 3.142…, y aumenta en incrementos de 1. El límite superior de 10 nunca se alcanza, ya que el siguiente número en la secuencia sería mayor que 10.

...
|===============                                                                 |  18%

¿Qué sucede si hacemos 15:1? Inténtalo para averiguarlo.

> 15:1
[1] 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1

Recuerda que, si tienes preguntas sobre una función particular de R, puedes acceder a su documentación con un signo de interrogación seguido del nombre de la función: ?nombre_de_la_funcion. Sin embargo, en el caso de un operador como los dos puntos usados arriba, debes encerrar el símbolo entre acentos graves, así: ?`:`. Nota: la tecla de acento grave (`) generalmente se encuentra en la esquina superior izquierda del teclado, encima de la tecla Tab. Si no tienes una tecla de acento grave, puedes usar comillas normales.

...
|==========================                                                      |  32%

Abre ahora la documentación de :.

> ?:
Error: unexpected ':' in "?:"
> ?`:`

Secuencias de números

La forma más sencilla de crear una secuencia de números en R es usando el operador :. Escribe 1:20 para ver cómo funciona.

> 1:20
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

Eso nos dio todos los enteros entre 1 y 20, incluyendo ambos extremos. También podríamos usarlo para crear una secuencia de números reales. Por ejemplo, prueba pi:10.

> pi:10
[1] 3.141593 4.141593 5.141593 6.141593 7.141593 8.141593 9.141593

El resultado es un vector de números reales que comienza con pi, es decir, 3.142…, y aumenta en incrementos de 1. El límite superior de 10 nunca se alcanza, ya que el siguiente número en nuestra secuencia sería mayor que 10.

...
|===============                                                                 |  18%

¿Qué sucede si hacemos 15:1? Inténtalo para averiguarlo.

> 15:1
[1] 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1

Recuerda que, si tienes preguntas sobre una función particular de R, puedes acceder a su documentación con un signo de interrogación seguido del nombre de la función: ?nombre_de_la_funcion. Sin embargo, en el caso de un operador como los dos puntos usados arriba, debes encerrar el símbolo entre acentos graves, así: ?`:`. Nota: la tecla de acento grave (`) generalmente se encuentra en la esquina superior izquierda del teclado, encima de la tecla Tab. Si no tienes una tecla de acento grave, puedes usar comillas normales.

...
|==========================                                                      |  32%

Abre ahora la documentación de :.

> ?:
Error: unexpected ':' in "?:"
> ?`:`

A menudo, necesitaremos más control sobre una secuencia que estamos creando que el que nos da el operador :. La función seq() sirve para este propósito.

...
|==================================                                                |  41%

El uso más básico de seq() hace exactamente lo mismo que el operador :. Prueba seq(1, 20) para verlo.

> seq(1,20)
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

Esto nos da la misma salida que 1:20. Sin embargo, supongamos que en cambio queremos un vector de números que vaya de 0 a 10, incrementándose en 0.5. seq(0, 10, by=0.5) hace precisamente eso. Pruébalo.

> seq(0,10, by=0.5)
[1]  0.0  0.5  1.0  1.5  2.0  2.5  3.0  3.5  4.0  4.5  5.0  5.5  6.0  6.5  7.0  7.5  8.0
[18]  8.5  9.0  9.5 10.0

O quizá no nos importa cuál sea el incremento y solo queremos una secuencia de 30 números entre 5 y 10. seq(5, 10, length=30) lo consigue. Inténtalo ahora y guarda el resultado en una nueva variable llamada my_seq.

Estás usando la misma función aquí, pero cambiando sus argumentos para obtener resultados diferentes. Asegúrate de guardar el resultado en una nueva variable llamada my_seq, así: my_seq <- seq(5, 10, length=30).

> my_seq<-seq(5, 10, length=30)

Para confirmar que my_seq tiene longitud 30, podemos usar la función length(). Inténtalo ahora.

> length(my_seq)
[1] 30

Hay varias formas en que podríamos hacer esto. Una posibilidad es combinar el operador : y la función length() de esta manera: 1:length(my_seq). Inténtalo.

> 1:length(my_seq)
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[30] 30

A menudo, necesitaremos más control sobre una secuencia que estamos creando que el que nos da el operador :. La función seq() sirve para este propósito.

...
|==================================                                                |  41%

El uso más básico de seq() hace exactamente lo mismo que el operador :. Prueba seq(1, 20) para verlo.

> seq(1,20)
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

Esto nos da la misma salida que 1:20. Sin embargo, supongamos que en cambio queremos un vector de números que vaya de 0 a 10, incrementándose en 0.5. seq(0, 10, by=0.5) hace precisamente eso. Pruébalo.

> seq(0,10, by=0.5)
[1]  0.0  0.5  1.0  1.5  2.0  2.5  3.0  3.5  4.0  4.5  5.0  5.5  6.0  6.5  7.0  7.5  8.0
[18]  8.5  9.0  9.5 10.0

O quizá no nos importa cuál sea el incremento y solo queremos una secuencia de 30 números entre 5 y 10. seq(5, 10, length=30) lo consigue. Inténtalo ahora y guarda el resultado en una nueva variable llamada my_seq.

Estás usando la misma función aquí, pero cambiando sus argumentos para obtener resultados diferentes. Asegúrate de guardar el resultado en una nueva variable llamada my_seq, así: my_seq <- seq(5, 10, length=30).

> my_seq<-seq(5, 10, length=30)

Para confirmar que my_seq tiene longitud 30, podemos usar la función length(). Inténtalo ahora.

> length(my_seq)
[1] 30

Hay varias formas en que podríamos hacer esto. Una posibilidad es combinar el operador : y la función length() de esta manera: 1:length(my_seq). Inténtalo.

> 1:length(my_seq)
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[30] 30

Otra opción es usar seq(along.with = my_seq). Inténtalo.

> seq(along.with = my_seq)
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[30] 30

Otra función relacionada con la creación de secuencias de números es rep(), que significa “replicar”. Veamos algunos usos.

Si nos interesa crear un vector que contenga 40 ceros, podemos usar rep(0, times = 40). Pruébalo.

> rep(0, times=40)
[1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Si en cambio queremos que nuestro vector contenga 10 repeticiones del vector (0, 1, 2), podemos hacer rep(c(0, 1, 2), times = 10). Adelante.

>
> rep(c(0, 1, 2), times = 10)
[1] 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2

Finalmente, supongamos que, en lugar de repetir el vector (0, 1, 2) una y otra vez, queremos que nuestro vector contenga 10 ceros, luego 10 unos y después 10 doses. Podemos hacer esto con el argumento each. Prueba rep(c(0, 1, 2), each = 10).

> rep(c(0, 1, 2), each = 10)
[1] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2

Vectores

La estructura de datos más simple y común en R es el vector. Los vectores vienen en dos variantes distintas: vectores atómicos y listas. Un vector atómico contiene exactamente un tipo de dato, mientras que una lista puede contener múltiples tipos de datos. Exploraremos más los vectores atómicos antes de llegar a las listas.

Anteriormente trabajamos por completo con vectores numéricos, que son un tipo de vector atómico. Otros tipos de vectores atómicos incluyen los lógicos, de caracteres, enteros y complejos. En esta lección, examinaremos con más detalle los vectores lógicos y de caracteres.

Los vectores lógicos pueden contener los valores TRUE, FALSE y NA, este último por “not available” o “no disponible”. Estos valores se generan como resultado de condiciones lógicas. Experimentemos con algunas condiciones simples.

Crea un vector numérico llamado num_vect que contenga los valores 0.5, 55, -10 y 6.

> num_vect<-c(0.5, 55, -10, 6)

Crea una variable llamada tf que reciba el resultado de num_vect < 1, lo cual se lee como “num_vect es menor que 1”.

> tf<-num_vect < 1

¿Cómo crees que se verá tf?

1: un solo valor lógico
2: un vector de 4 valores lógicos

Selection: 2

El argumento collapse de la función paste() le indica a R que, cuando unamos los elementos del vector de caracteres my_char, queremos separarlos con espacios simples.

Para agregar, o “concatenar”, tu nombre al final de my_char, usa la función c() de esta manera: c(my_char, «your_name_here»). Coloca tu nombre entre comillas dobles donde he puesto «your_name_here». Inténtalo ahora, guardando el resultado en una nueva variable llamada my_name.

> my_name<-c(my_char, "Eduardo")
> my_name
[1] "My"      "name"    "is"      "Eduardo"

Ahora, usa la función paste() una vez más para unir las palabras de my_name en una sola cadena de caracteres. No olvides indicar collapse = » «.

> paste(my_name, collapse=" ")
[1] "My name is Eduardo"

En el caso más simple, podemos unir dos vectores de caracteres que tienen longitud 1 cada uno, es decir, unir dos palabras. Prueba paste(«Hello», «world!», sep = » «), donde el argumento sep le indica a R que queremos separar los elementos unidos con un solo espacio.

> paste("Hello", "world!", sep = " ")
[1] "Hello world!"

Para un ejemplo un poco más complicado, podemos unir dos vectores, cada uno de longitud 3. Usa paste() para unir el vector entero 1:3 con el vector de caracteres c(«X», «Y», «Z»). Esta vez, usa sep = «» para no dejar ningún espacio entre los elementos unidos.

> paste(1:3, c("X", "Y", "Z"), sep="")
[1] "1X" "2Y" "3Z"

El argumento collapse de la función paste() le indica a R que, cuando unamos los elementos del vector de caracteres my_char, queremos separarlos con espacios simples.

Para agregar, o “concatenar”, tu nombre al final de my_char, usa la función c() de esta manera: c(my_char, «your_name_here»). Coloca tu nombre entre comillas dobles donde he puesto «your_name_here». Inténtalo ahora, guardando el resultado en una nueva variable llamada my_name.

> my_name<-c(my_char, "Eduardo")
> my_name
[1] "My"      "name"    "is"      "Eduardo"

Ahora, usa la función paste() una vez más para unir las palabras de my_name en una sola cadena de caracteres. No olvides indicar collapse = » «.

> paste(my_name, collapse=" ")
[1] "My name is Eduardo"

En el caso más simple, podemos unir dos vectores de caracteres que tienen longitud 1 cada uno, es decir, unir dos palabras. Prueba paste(«Hello», «world!», sep = » «), donde el argumento sep le indica a R que queremos separar los elementos unidos con un solo espacio.

> paste("Hello", "world!", sep = " ")
[1] "Hello world!"

Para un ejemplo un poco más complicado, podemos unir dos vectores, cada uno de longitud 3. Usa paste() para unir el vector entero 1:3 con el vector de caracteres c(«X», «Y», «Z»). Esta vez, usa sep = «» para no dejar ningún espacio entre los elementos unidos.

> paste(1:3, c("X", "Y", "Z"), sep="")
[1] "1X" "2Y" "3Z"

Prueba paste(LETTERS, 1:4, sep = «-«), donde LETTERS es una variable predefinida en R que contiene un vector de caracteres con las 26 letras del alfabeto inglés.

> paste(LETTERS, 1:4, sep = "-")
[1] "A-1" "B-2" "C-3" "D-4" "E-1" "F-2" "G-3" "H-4" "I-1" "J-2" "K-3" "L-4" "M-1" "N-2"
[15] "O-3" "P-4" "Q-1" "R-2" "S-3" "T-4" "U-1" "V-2" "W-3" "X-4" "Y-1" "Z-2"

Como el vector de caracteres LETTERS es más largo que el vector numérico 1:4, R simplemente recicla, o repite, 1:4 hasta que coincide con la longitud de LETTERS.

También vale la pena notar que el vector numérico 1:4 es “coaccionado” o convertido en un vector de caracteres por la función paste().

Valores faltantes

Los valores faltantes desempeñan un papel importante en la estadística y el análisis de datos. A menudo, los valores faltantes no deben ignorarse; más bien, deben estudiarse cuidadosamente para ver si existe algún patrón o causa subyacente que explique su ausencia.

En R, NA se usa para representar cualquier valor que “no está disponible” o que está “faltante”, en el sentido estadístico. En esta lección exploraremos los valores faltantes con más detalle.

Cualquier operación que involucre NA generalmente produce NA como resultado. Para ilustrarlo, creemos un vector c(44, NA, 5, NA) y asignémoslo a una variable x.

> x<-c(44, NA, 5, NA)

Ahora multipliquemos x por 3.

> x*3
[1] 132  NA  15  NA

Para hacer las cosas un poco más interesantes, creemos un vector que contenga 1000 extracciones de una distribución normal estándar con y <- rnorm(1000).

> y <- rnorm(1000)

A continuación, creemos un vector que contenga 1000 valores NA con z <- rep(NA, 1000).

> z <- rep(NA, 1000)

Finalmente, seleccionemos 100 elementos al azar de estos 2000 valores, combinando y y z, de tal forma que no sepamos cuántos NA obtendremos ni qué posiciones ocuparán en nuestro vector final: my_data <- sample(c(y, z), 100).

>
> my_data <- sample(c(y, z), 100)

Primero hagamos la pregunta de dónde se encuentran nuestros NA en los datos. La función is.na() nos dice si cada elemento de un vector es NA. Aplica is.na() a my_data y asigna el resultado a my_na.

> my_na<-is.na(my_data)

Imprime my_na para ver qué obtuviste.

> my_na
[1]  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE
[15]  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE
[29]  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
[43] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE
[57] FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE
[71]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
[85] FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE
[99]  TRUE  TRUE

En cada lugar donde veas TRUE, sabes que el elemento correspondiente de my_data es NA. Del mismo modo, en cada lugar donde veas FALSE, sabes que el elemento correspondiente de my_data es una de nuestras extracciones aleatorias de la distribución normal estándar.

En nuestra discusión anterior sobre operadores lógicos, presentamos el operador == como un método para probar la igualdad entre dos objetos. Por eso, podrías pensar que la expresión my_data == NA produce los mismos resultados que is.na(). Inténtalo.

> my_data == NA
[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[30] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[59] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[88] NA NA NA NA NA NA NA NA NA NA NA NA NA

La razón por la que obtuviste un vector compuesto completamente por NA es que NA no es realmente un valor, sino solo un marcador de posición para una cantidad que no está disponible. Por lo tanto, la expresión lógica está incompleta y R no tiene otra opción más que devolver un vector de la misma longitud que my_data, compuesto completamente por NA.

No te preocupes si esto resulta un poco confuso. La idea clave es tener cuidado al usar expresiones lógicas siempre que puedan aparecer valores NA, ya que un solo valor NA puede alterar por completo el resultado.

Así que volvamos a la tarea que tenemos entre manos. Ahora que tenemos un vector, my_na, que tiene TRUE para cada NA y FALSE para cada valor numérico, podemos calcular el número total de NA en nuestros datos.

El truco está en reconocer que, por debajo de la superficie, R representa TRUE como el número 1 y FALSE como el número 0. Por lo tanto, si sumamos un conjunto de TRUE y FALSE, obtenemos el número total de TRUE.

Probémoslo aquí. Llama a la función sum() sobre my_na para contar el número total de TRUE en my_na y, por lo tanto, el número total de NA en my_data. No asignes el resultado a una nueva variable.

> sum(my_na)
[1] 51

Ahora que ya dominamos los NA, veamos un segundo tipo de valor faltante: NaN, que significa “not a number”, es decir, “no es un número”. Para generar NaN, intenta dividir 0 entre 0 usando una diagonal.

> 0/0
[1] NaN

Hagamos uno más, solo por diversión. En R, Inf representa el infinito. ¿Qué sucede si restas Inf de Inf?

> Inf-Inf
[1] NaN

Subconjuntos de vectores

En esta lección veremos cómo extraer elementos de un vector con base en algunas condiciones que especifiquemos.

Por ejemplo, quizá solo nos interesen los primeros 20 elementos de un vector, o solo los elementos que no son NA, o solo aquellos que son positivos o que corresponden a una variable específica de interés. Al final de esta lección, sabrás cómo manejar cada uno de estos escenarios.

He creado para ti un vector llamado x que contiene un ordenamiento aleatorio de 20 números, tomados de una distribución normal estándar, y 20 valores NA. Escribe x ahora para ver cómo se ve.

> x
[1]           NA           NA           NA           NA           NA  0.121071045
[7]           NA           NA           NA -2.147869240  1.750775902 -0.087078058
[13] -0.864871259  1.271334617           NA           NA -0.113763702           NA
[19]           NA           NA  1.529772485 -0.297384825 -1.626953012           NA
[25]  0.397615042           NA -0.135198351           NA           NA -1.188919666
[31] -0.145686488           NA -0.009722626  0.945610258 -1.914849913 -0.543808781
[37]           NA           NA  0.141761620  0.981105190

La forma de indicarle a R que quieres seleccionar algunos elementos particulares, es decir, un subconjunto, de un vector es colocando un “vector de índices” entre corchetes inmediatamente después del nombre del vector.

Como ejemplo simple, prueba x[1:10] para ver los primeros diez elementos de x.

> x[1:10]
[1]        NA        NA        NA        NA        NA  0.121071        NA        NA        NA
[10] -2.147869

Los vectores de índices vienen en cuatro variantes distintas: vectores lógicos, vectores de enteros positivos, vectores de enteros negativos y vectores de cadenas de caracteres.

Comencemos indexando con vectores lógicos. Un escenario común al trabajar con datos del mundo real es que queremos extraer todos los elementos de un vector que no son NA, es decir, datos faltantes. Recuerda que is.na(x) produce un vector de valores lógicos de la misma longitud que x, con TRUE correspondientes a los valores NA en x y FALSE correspondientes a los valores que no son NA en x.

¿Qué crees que te dará x[is.na(x)]?

Un vector compuesto completamente por NA.

> x[is.na(x)]
[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA

Recuerda que ! nos da la negación de una expresión lógica, así que !is.na(x) puede leerse como “no es NA”. Por lo tanto, si queremos crear un vector llamado y que contenga todos los valores no NA de x, podemos usar y <- x[!is.na(x)]. Inténtalo.

> y <- x[!is.na(x)]
> y
[1]  0.121071045 -2.147869240  1.750775902 -0.087078058 -0.864871259  1.271334617
[7] -0.113763702  1.529772485 -0.297384825 -1.626953012  0.397615042 -0.135198351
[13] -1.188919666 -0.145686488 -0.009722626  0.945610258 -1.914849913 -0.543808781
[19]  0.141761620  0.981105190

Recuerda que la expresión y > 0 nos dará un vector de valores lógicos de la misma longitud que y, con TRUE correspondientes a los valores de y que son mayores que cero y FALSE correspondientes a los valores de y que son menores o iguales que cero. ¿Qué crees que te dará y[y > 0]?

La expresión lógica y > 0 nos dará TRUE para cada elemento de y que sea positivo. Con base en eso, ¿qué crees que devolverá y[y > 0]?

Un vector con todos los elementos positivos de y.

> y[y > 0]
[1] 0.1210710 1.7507759 1.2713346 1.5297725 0.3976150 0.9456103 0.1417616 0.9811052

Quizá te preguntes por qué no empezamos simplemente con x[x > 0] para aislar los elementos positivos de x. Inténtalo ahora para ver por qué.

> x[x > 0]
[1]        NA        NA        NA        NA        NA 0.1210710        NA        NA        NA
[10] 1.7507759 1.2713346        NA        NA        NA        NA        NA 1.5297725        NA
[19] 0.3976150        NA        NA        NA        NA 0.9456103        NA        NA 0.1417616
[28] 0.9811052

Como NA no es un valor, sino un marcador de posición para una cantidad desconocida, la expresión NA > 0 se evalúa como NA. Por eso obtenemos varios NA mezclados con nuestros números positivos cuando hacemos esto.

Combinando nuestro conocimiento de operadores lógicos con nuestro nuevo conocimiento sobre la extracción de subconjuntos, podríamos hacer esto: x[!is.na(x) & x > 0]. Pruébalo.

> x[!is.na(x) & x > 0]
[1] 0.1210710 1.7507759 1.2713346 1.5297725 0.3976150 0.9456103 0.1417616 0.9811052

En este caso, solicitamos solamente los valores de x que son, al mismo tiempo, no faltantes y mayores que cero.

Ya te mostré cómo extraer solo los primeros diez valores de x usando x[1:10]. En este caso, estamos proporcionando un vector de enteros positivos dentro de los corchetes, lo cual le indica a R que devuelva solamente los elementos de x numerados del 1 al 10.

...
|==========================================                                        |  50%

Muchos lenguajes de programación usan lo que se llama “indexación basada en cero”, lo que significa que el primer elemento de un vector se considera el elemento 0. R usa “indexación basada en uno”, lo cual, como seguramente adivinaste, significa que el primer elemento de un vector se considera el elemento 1.

...
|============================================                                      |  53%

¿Puedes averiguar cómo extraeríamos los elementos 3, 5 y 7 de x? Pista: usa la función c() para especificar los números de los elementos como un vector numérico.

> x[c(3,5,7)]
[1] NA NA NA

¿Qué sucede si pedimos el elemento cero de x, es decir, x[0]? Inténtalo.

> x[0]
numeric(0)

¿Y qué sucede si pedimos el elemento número 3000 de x? Pruébalo.

> x[3000]
[1] NA

¿Qué pasa si nos interesan todos los elementos de x EXCEPTO el segundo y el décimo? Sería bastante tedioso construir un vector que contenga todos los números del 1 al 40 EXCEPTO el 2 y el 10.

> x[c(-2, -10)]
[1]           NA           NA           NA           NA  0.121071045           NA
[7]           NA           NA  1.750775902 -0.087078058 -0.864871259  1.271334617
[13]           NA           NA -0.113763702           NA           NA           NA
[19]  1.529772485 -0.297384825 -1.626953012           NA  0.397615042           NA
[25] -0.135198351           NA           NA -1.188919666 -0.145686488           NA
[31] -0.009722626  0.945610258 -1.914849913 -0.543808781           NA           NA
[37]  0.141761620  0.981105190

Una forma abreviada de especificar varios números negativos consiste en colocar el signo negativo delante del vector de números positivos. Escribe x[-c(2, 10)] para obtener exactamente el mismo resultado.

> x[-c(2, 10)]
[1]           NA           NA           NA           NA  0.121071045           NA
[7]           NA           NA  1.750775902 -0.087078058 -0.864871259  1.271334617
[13]           NA           NA -0.113763702           NA           NA           NA
[19]  1.529772485 -0.297384825 -1.626953012           NA  0.397615042           NA
[25] -0.135198351           NA           NA -1.188919666 -0.145686488           NA
[31] -0.009722626  0.945610258 -1.914849913 -0.543808781           NA           NA
[37]  0.141761620  0.981105190

Hasta ahora hemos cubierto tres tipos de vectores de índices: lógicos, enteros positivos y enteros negativos. El único tipo restante requiere que introduzcamos el concepto de elementos “nombrados”.

Crea un vector numérico con tres elementos nombrados usando vect <- c(foo = 11, bar = 2, norf = NA).

> vect <- c(foo = 11, bar = 2, norf =NA)

Cuando imprimamos vect en la consola, verás que cada elemento tiene un nombre. Pruébalo.

>
> vect
foo  bar norf
 11    2   NA

También podemos obtener los nombres de vect pasando vect como argumento a la función names(). Inténtalo.

> names(vect)
[1] "foo"  "bar"  "norf"

De forma alternativa, podemos crear un vector sin nombres llamado vect2 con c(11, 2, NA). Hazlo ahora.

> vect2<-c(11, 2, NA)

Después, podemos agregar el atributo names a vect2 con names(vect2) <- c(«foo», «bar», «norf»). Adelante.

> names(vect2) <-c("foo", "bar", "norf")

Ahora revisemos que vect y vect2 sean iguales pasándolos como argumentos a la función identical().

> identical(vect2)
Error in identical(vect2) : argument "y" is missing, with no default
> identical(vect2,vect)
[1] TRUE

En efecto, vect y vect2 son vectores nombrados idénticos.

> x[c(3,5,7)]
[1] NA NA NA

¿Qué sucede si pedimos el elemento cero de x, es decir, x[0]? Inténtalo.

> x[0]
numeric(0)

¿Y si pedimos el elemento número 3000 de x? Pruébalo.

> x[3000]
[1] NA

¿Qué pasa si nos interesan todos los elementos de x EXCEPTO el segundo y el décimo? Sería bastante tedioso construir un vector que contenga todos los números del 1 al 40 EXCEPTO el 2 y el 10.

> x[c(-2, -10)]
[1]           NA           NA           NA           NA  0.121071045           NA
[7]           NA           NA  1.750775902 -0.087078058 -0.864871259  1.271334617
[13]           NA           NA -0.113763702           NA           NA           NA
[19]  1.529772485 -0.297384825 -1.626953012           NA  0.397615042           NA
[25] -0.135198351           NA           NA -1.188919666 -0.145686488           NA
[31] -0.009722626  0.945610258 -1.914849913 -0.543808781           NA           NA
[37]  0.141761620  0.981105190

Una forma abreviada de especificar varios números negativos consiste en colocar el signo negativo delante del vector de números positivos. Escribe x[-c(2, 10)] para obtener exactamente el mismo resultado.

> x[-c(2, 10)]
[1]           NA           NA           NA           NA  0.121071045           NA
[7]           NA           NA  1.750775902 -0.087078058 -0.864871259  1.271334617
[13]           NA           NA -0.113763702           NA           NA           NA
[19]  1.529772485 -0.297384825 -1.626953012           NA  0.397615042           NA
[25] -0.135198351           NA           NA -1.188919666 -0.145686488           NA
[31] -0.009722626  0.945610258 -1.914849913 -0.543808781           NA           NA
[37]  0.141761620  0.981105190

Hasta ahora hemos cubierto tres tipos de vectores de índices: lógicos, enteros positivos y enteros negativos. El único tipo restante requiere que introduzcamos el concepto de elementos “nombrados”.

Crea un vector numérico con tres elementos nombrados usando vect <- c(foo = 11, bar = 2, norf = NA).

> vect <- c(foo = 11, bar = 2, norf =NA)

Cuando imprimamos vect en la consola, verás que cada elemento tiene un nombre. Pruébalo.

>
> vect
foo  bar norf
 11    2   NA

También podemos obtener los nombres de vect pasando vect como argumento a la función names(). Inténtalo.

> names(vect)
[1] "foo"  "bar"  "norf"

De forma alternativa, podemos crear un vector sin nombres llamado vect2 con c(11, 2, NA). Hazlo ahora.

> vect2<-c(11, 2, NA)

Después, podemos agregar el atributo names a vect2 con names(vect2) <- c(«foo», «bar», «norf»). Adelante.

> names(vect2) <-c("foo", "bar", "norf")

Ahora revisemos que vect y vect2 sean iguales pasándolos como argumentos a la función identical().

> identical(vect2)
Error in identical(vect2) : argument "y" is missing, with no default
> identical(vect2,vect)
[1] TRUE

En efecto, vect y vect2 son vectores nombrados idénticos.

> length(my_vector)
[1] 20

¡Ah! Eso era lo que queríamos. Pero, ¿qué sucede si le damos a my_vector un atributo dim? Intentémoslo. Escribe dim(my_vector) <- c(4, 5).

> dim(my_vector) <- c(4, 5)

Hay dos valores lógicos en R, también llamados valores booleanos. Son TRUE y FALSE. En R puedes construir expresiones lógicas que se evaluarán como TRUE o FALSE.

No hay problema si el último comando te pareció un poco extraño. ¡Debería parecerlo! La función dim() te permite obtener O establecer el atributo dim de un objeto de R. En este caso, asignamos el valor c(4, 5) al atributo dim de my_vector.

> dim(my_vector)
[1] 4 5

Otra forma de ver esto es llamando a la función attributes() sobre my_vector. Inténtalo ahora.

> attributes(my_vector)
$dim
[1] 4 5

Igual que en la clase de matemáticas, cuando trabajamos con un objeto bidimensional, piensa en una tabla rectangular, el primer número es el número de filas y el segundo es el número de columnas. Por lo tanto, acabamos de darle a my_vector 4 filas y 5 columnas.

> my_vector
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    9   13   17
[2,]    2    6   10   14   18
[3,]    3    7   11   15   19
[4,]    4    8   12   16   20
> class(my_vector)
[1] "matrix"

El ejemplo que hemos usado hasta ahora tenía como propósito ilustrar que una matriz es simplemente un vector atómico con un atributo de dimensión. Un método más directo para crear la misma matriz usa la función matrix().

Ahora consulta la documentación de la función matrix y ve si puedes averiguar cómo crear una matriz que contenga los mismos números, del 1 al 20, y las mismas dimensiones, 4 filas y 5 columnas, llamando a la función matrix(). Guarda el resultado en una variable llamada my_matrix2.

> ?matrix
> matrix(1:20, nrow=4, ncol=5)
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    9   13   17
[2,]    2    6   10   14   18
[3,]    3    7   11   15   19
[4,]    4    8   12   16   20

Llama a la función matrix() con tres argumentos: 1:20, el número de filas y el número de columnas. Asegúrate de especificar los argumentos con sus nombres adecuados y guarda el resultado en my_matrix2, no en my_matrix.

> my_matrix2<-matrix(1:20, nrow=4, ncol=5)
> identical(my_matrix,my_matrix2)
[1] TRUE

Ahora imagina que los números de nuestra tabla representan algunas mediciones de un experimento clínico, donde cada fila representa a un paciente y cada columna representa una variable para la cual se tomaron mediciones.

...
|==============================================                                      |  54%

Podríamos querer etiquetar las filas para saber qué números pertenecen a cada paciente del experimento. Una forma de hacer esto es agregar una columna a la matriz que contenga los nombres de las cuatro personas.

...
|================================================                                    |  57%

Comencemos creando un vector de caracteres que contenga los nombres de nuestros pacientes: Bill, Gina, Kelly y Sean. Recuerda que las comillas dobles le indican a R que algo es una cadena de caracteres. Guarda el resultado en una variable llamada patients.

> patients<-c("Bill", "Gina", "Kelly", "Sean")

Ahora usaremos la función cbind() para “combinar columnas”. No te preocupes por guardar el resultado en una nueva variable. Simplemente llama a cbind() con dos argumentos: el vector patients y my_matrix.

> cbind(patients, my_matrix)
     patients
[1,] "Bill"   "1" "5" "9"  "13" "17"
[2,] "Gina"   "2" "6" "10" "14" "18"
[3,] "Kelly"  "3" "7" "11" "15" "19"
[4,] "Sean"   "4" "8" "12" "16" "20"

¡Buen trabajo!

|=====================================================                           |  63%

¡Hay algo sospechoso en nuestro resultado! Parece que combinar el vector de caracteres con nuestra matriz de números provocó que todo quedara encerrado entre comillas dobles. Esto significa que terminamos con una matriz de cadenas de caracteres, lo cual no es bueno.

...
|=======================================================                         |  66%

Si recuerdas el inicio de esta lección, te dije que las matrices solo pueden contener UNA clase de datos. Por lo tanto, cuando intentamos combinar un vector de caracteres con una matriz numérica, R se vio obligado a “coaccionar” o convertir los números a caracteres; de ahí las comillas dobles.

...
|==========================================================                      |  69%

Esto se llama “coerción implícita”, porque no la pedimos. Simplemente ocurrió. Pero ¿por qué R no convirtió los nombres de nuestros pacientes a números? Te dejaré reflexionar sobre esa pregunta por tu cuenta.

...
|============================================================                    |  71%

Así que todavía nos queda la pregunta de cómo incluir los nombres de nuestros pacientes en la tabla sin destruir la integridad de nuestros datos numéricos. Prueba lo siguiente: my_data <- data.frame(patients, my_matrix).

> my_data <-data.frame(patients, my_matrix)

¡Lo lograste! ¡Buen trabajo!

|==============================================================                  |  74%

Ahora visualiza el contenido de my_data para ver qué hemos obtenido.

> my_data
  patients X1 X2 X3 X4 X5
1     Bill  1  5  9 13 17
2     Gina  2  6 10 14 18
3    Kelly  3  7 11 15 19
4     Sean  4  8 12 16 20

Parece que la función data.frame() nos permitió almacenar nuestro vector de caracteres con nombres justo al lado de nuestra matriz de números. ¡Eso es exactamente lo que esperábamos!

...
|===================================================================             |  80%

Detrás de escena, la función data.frame() toma cualquier número de argumentos y devuelve un solo objeto de clase data.frame que está compuesto por los objetos originales.

...
|======================================================================          |  83%

Confirmemos esto llamando a la función class() sobre nuestro data frame recién creado.

> class(my_data)
[1] "data.frame"

También es posible asignar nombres a las filas y columnas individuales de un data frame, lo que presenta otra forma posible de determinar qué fila de valores de nuestra tabla pertenece a cada paciente.

...
|==========================================================================      |  89%

Sin embargo, como ya resolvimos ese problema, resolvamos uno diferente asignando nombres a las columnas de nuestro data frame para saber qué tipo de medición representa cada columna.

...
|=============================================================================   |  91%

Como tenemos seis columnas, incluyendo los nombres de los pacientes, primero necesitaremos crear un vector que contenga un elemento por cada columna. Crea un vector de caracteres llamado cnames que contenga los siguientes valores, en orden: «patient», «age», «weight», «bp», «rating», «test».

> cnames<-c("patient", "age", "weight", "bp", "rating","test")

Ahora usa la función colnames() para establecer el atributo colnames de nuestro data frame. Esto es similar a la forma en que usamos la función dim() anteriormente en esta lección.

> colnames(my_data)<-cnames
> my_data
  patient age weight bp rating test
1    Bill   1      5  9     13   17
2    Gina   2      6 10     14   18
3   Kelly   3      7 11     15   19
4    Sean   4      8 12     16   20

Lógica

Hay dos valores lógicos en R, también llamados valores booleanos. Son TRUE y FALSE. En R puedes construir expresiones lógicas que se evaluarán como TRUE o FALSE.

Crear expresiones lógicas requiere operadores lógicos. Probablemente estás familiarizado con operadores aritméticos como +, -, * y /. El primer operador lógico que discutiremos es el operador de igualdad, representado por dos signos de igual: ==. Usa el operador de igualdad abajo para averiguar si TRUE es igual a TRUE.

> TRUE==TRUE
[1] TRUE
> (FALSE == TRUE) == FALSE
[1] TRUE
> 6==7
[1] FALSE

El siguiente operador que discutiremos es el operador “no igual”, representado por !=. Este operador prueba si dos valores son distintos, por lo que TRUE != FALSE se evalúa como TRUE. Al igual que el operador de igualdad, != también puede usarse con números. Intenta escribir una expresión para ver si 5 no es igual a 7.

> 5!=7
[1] TRUE

Para negar expresiones booleanas puedes usar el operador NOT. Un signo de exclamación ! hará que !TRUE, es decir, “no verdadero”, se evalúe como FALSE, y que !FALSE, es decir, “no falso”, se evalúe como TRUE. Intenta usar el operador NOT y el operador de igualdad para encontrar lo opuesto de si 5 es igual a 7.

> !5==7
[1] TRUE

Tomemos un momento para repasar. El operador de igualdad == prueba si dos valores booleanos o números son iguales; el operador de desigualdad != prueba si dos valores booleanos o números son distintos; y el operador NOT ! niega expresiones lógicas, de modo que las expresiones TRUE se vuelven FALSE y las expresiones FALSE se vuelven TRUE.

Veamos cómo funciona el operador AND. Hay dos operadores AND en R: & y &&. Ambos operadores funcionan de manera similar: si los operandos derecho e izquierdo de AND son ambos TRUE, toda la expresión es TRUE; de lo contrario, es FALSE. Por ejemplo, TRUE & TRUE se evalúa como TRUE. Intenta escribir FALSE & FALSE para ver cómo se evalúa.

Puedes usar el operador & para evaluar AND a lo largo de un vector. La versión && de AND solo evalúa el primer miembro de un vector. Probemos ambos como práctica. Escribe la expresión TRUE & c(TRUE, FALSE, FALSE).

> TRUE & c(TRUE, FALSE, FALSE)
[1]  TRUE FALSE FALSE

Lo que sucede en este caso es que el operando izquierdo TRUE se recicla a lo largo de cada elemento del vector del operando derecho. Esto equivale a la expresión c(TRUE, TRUE, TRUE) & c(TRUE, FALSE, FALSE).

...
|====================================                                            |  43%

Ahora escribiremos la misma expresión, excepto que usaremos el operador &&. Escribe la expresión TRUE && c(TRUE, FALSE, FALSE).

> TRUE && c(TRUE, FALSE, FALSE)
[1] TRUE

En este caso, el operando izquierdo solo se evalúa con el primer miembro del operando derecho, es decir, el vector. El resto de los elementos del vector no se evalúan en absoluto en esta expresión.

...
|========================================                                            |  47%

El operador OR sigue un conjunto de reglas similar. La versión | de OR evalúa OR a lo largo de todo un vector, mientras que la versión || de OR solo evalúa el primer elemento de un vector.

...
|=========================================                                           |  49%

Una expresión que usa el operador OR se evaluará como TRUE si el operando izquierdo o el operando derecho es TRUE. Si ambos son TRUE, la expresión se evaluará como TRUE; sin embargo, si ninguno es TRUE, entonces la expresión será FALSE.

...
|===========================================                                         |  51%

Probemos la versión vectorizada del operador OR. Escribe la expresión TRUE | c(TRUE, FALSE, FALSE).

> TRUE|c(TRUE, FALSE, FALSE)
[1] TRUE TRUE TRUE

Ahora probemos la versión no vectorizada del operador OR. Escribe la expresión TRUE || c(TRUE, FALSE, FALSE).

> TRUE|||| c(TRUE, FALSE, FALSE)
Error: unexpected '||' in "TRUE||||"
> TRUE|| c(TRUE, FALSE, FALSE)
[1] TRUE

Los operadores lógicos pueden encadenarse igual que los operadores aritméticos. Expresiones como 6 != 10 && FALSE && 1 >= 2 o TRUE || 5 < 9.3 || FALSE son completamente normales.

Como quizá recuerdes, la aritmética tiene un orden de operaciones, y las expresiones lógicas también. Todos los operadores AND se evalúan antes que los operadores OR. Veamos un ejemplo de un caso ambiguo. Escribe: 5 > 8 || 6 != 8 && 4 > 3.9.

> 5 > 8 || 6 != 8 && 4 > 3.9
[1] TRUE

Ahora que estás familiarizado con los operadores lógicos de R, puedes aprovechar algunas funciones que R proporciona para trabajar con expresiones lógicas.

...
|========================================================                            |  67%

La función isTRUE() toma un argumento. Si ese argumento se evalúa como TRUE, la función devolverá TRUE. De lo contrario, devolverá FALSE. Prueba esta función escribiendo: isTRUE(6 > 4).

>
> isTRUE(6 > 4)
[1] TRUE

La función identical() devolverá TRUE si los dos objetos de R que se le pasan como argumentos son idénticos. Prueba la función identical() escribiendo: identical(‘twins’, ‘twins’).

> identical('twins', 'twins')
[1] TRUE

También debes conocer la función xor(), que toma dos argumentos. La función xor() significa OR exclusivo. Si un argumento se evalúa como TRUE y el otro como FALSE, entonces esta función devolverá TRUE; de lo contrario, devolverá FALSE. Prueba la función xor() escribiendo: xor(5 == 6, !FALSE).

> xor(5 == 6, !FALSE)
[1] TRUE

5 == 6 se evalúa como FALSE, !FALSE se evalúa como TRUE, así que xor(FALSE, TRUE) se evalúa como TRUE. Por otro lado, si el primer argumento cambiara a 5 == 5 y el segundo argumento permaneciera igual, ambos argumentos serían TRUE, por lo que xor(TRUE, TRUE) se evaluaría como FALSE.

Para las siguientes preguntas, necesitaremos crear un vector de enteros llamado ints. Crea este vector escribiendo: ints <- sample(10).

> ints <- sample(10)
> ints
[1]  9  5  4  3  8 10  1  6  7  2

El vector ints es una muestra aleatoria de enteros del 1 al 10 sin reemplazo. Supongamos que queremos hacer algunas preguntas lógicas sobre el contenido de ints. Si escribimos ints > 5, obtendremos un vector lógico que corresponde a si cada elemento de ints es mayor que 5. Prueba escribiendo: ints > 5.

> ints > 5
[1]  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE

Podemos usar el vector lógico resultante para hacer otras preguntas sobre ints. La función which() toma un vector lógico como argumento y devuelve los índices del vector que son TRUE. Por ejemplo, which(c(TRUE, FALSE, TRUE)) devolvería el vector c(1, 3).

...
|==========================================================================          |  88%
|==========================================================================          |  88%

Usa la función which() para encontrar los índices de ints que son mayores que 7.

> ints(which(ints>7))
Error: could not find function "ints"
> ints[which(ints>7)]
[1]  9  8 10

¡Casi! Inténtalo de nuevo. O escribe info() para ver más opciones.

Usa la función which() sobre el vector lógico producido por: ints > 7.

> ints[which(ints>7)]
[1]  9  8 10

No exactamente, pero estás aprendiendo. Inténtalo de nuevo. O escribe info() para ver más opciones.

Usa la función which() sobre el vector lógico producido por: ints > 7.

> which(ints>7)
[1] 1 5 6

Al igual que la función which(), las funciones any() y all() toman vectores lógicos como argumento. La función any() devolverá TRUE si uno o más de los elementos del vector lógico son TRUE. La función all() devolverá TRUE si todos los elementos del vector lógico son TRUE.

> any(ints<0)
[1] FALSE
> all(ints>0)
[1] TRUE

Funciones

Las funciones son uno de los bloques fundamentales del lenguaje R. Son pequeñas piezas de código reutilizable que pueden tratarse como cualquier otro objeto de R. Las funciones suelen caracterizarse por el nombre de la función seguido de paréntesis.

> Sys.Date()
[1] "2015-03-28"

La mayoría de las funciones en R devuelven un valor. Funciones como Sys.Date() devuelven un valor basado en el entorno de tu computadora, mientras que otras funciones manipulan datos de entrada para calcular un valor de retorno.

La función mean() toma un vector de números como entrada y devuelve el promedio de todos los números del vector de entrada. Las entradas de una función suelen llamarse argumentos. Proporcionar argumentos a una función también se conoce a veces como pasar argumentos a esa función. Los argumentos que deseas pasar a una función van dentro de los paréntesis de la función. Intenta pasar el argumento c(2, 4, 5) a la función mean().

> mean(c(2, 4, 5))
[1] 3.666667

Las funciones generalmente toman argumentos, que son variables sobre las cuales opera la función. Por ejemplo, la función mean() toma un vector como argumento, como en el caso de mean(c(2,6,8)). Luego, la función mean() suma todos los números del vector y divide esa suma entre la longitud del vector.

Si quieres ver el código fuente de cualquier función, simplemente escribe el nombre de la función sin argumentos ni paréntesis.

Felicidades por escribir tu primera función. Al escribir funciones, puedes obtener una comprensión profunda de cómo funciona R. Como dijo alguna vez John Chambers, creador de R:

Para entender los cálculos en R, dos lemas son útiles: 1. Todo lo que existe es un objeto. 2. Todo lo que sucede es una llamada a una función.

Si quieres ver el código fuente de cualquier función, simplemente escribe el nombre de la función sin argumentos ni paréntesis. Probemos esto con la función que acabas de crear. Escribe boring_function para ver su código fuente.

> boring_function
function(x) {
x
}

A continuación, intentemos escribir una función con argumentos predeterminados. Puedes establecer valores predeterminados para los argumentos de una función, y esto puede ser útil si crees que alguien que use tu función configurará cierto argumento con el mismo valor la mayor parte del tiempo.

remainder <- function(num, divisor=2) {
num %% divisor
}

También puedes especificar argumentos explícitamente en una función. Cuando designas explícitamente los valores de los argumentos por nombre, el orden de los argumentos deja de ser importante. Puedes probar esto escribiendo: remainder(divisor = 11, num = 5).

> remainder(divisor = 11, num = 5)
[1] 5

R también puede hacer coincidencia parcial de argumentos. Intenta escribir remainder(4, div = 2) para ver esta característica en acción.

> remainder(4, div = 2)
[1] 0

Con toda esta discusión sobre argumentos, quizá te preguntes si hay una forma de ver los argumentos de una función, además de consultar la documentación. Afortunadamente, puedes usar la función args(). Escribe args(remainder) para examinar los argumentos de la función remainder.

> args(remainder)
function (num, divisor = 2)
NULL

¡Puedes pasar funciones como argumentos! Este es un concepto muy poderoso.

# Puedes pasar funciones como argumentos a otras funciones, del mismo modo que puedes pasar
# datos a funciones. Supongamos que defines las siguientes funciones:
#
# add_two_numbers <- function(num1, num2){
#    num1 + num2
# }
#
# multiply_two_numbers <- function(num1, num2){
#    num1 * num2
# }
#
# some_function <- function(func){
#    func(2, 4)
evaluate <- function(func, dat){
func(dat)
}
> evaluate(sd,c(1.4, 3.6, 7.9, 8.8))
[1] 3.514138

Quizá te sorprenda saber que puedes pasar una función como argumento sin definir primero la función que se va a pasar. Las funciones que no tienen nombre se conocen apropiadamente como funciones anónimas.

Usemos la función evaluate para explorar cómo funcionan las funciones anónimas. Como primer argumento de la función evaluate escribiremos una pequeña función que cabe en una sola línea. En el segundo argumento pasaremos algunos datos a la pequeña función anónima del primer argumento.

> evaluate(function(x){x+1},6)
[1] 7

Intenta usar evaluate() junto con una función anónima para devolver el primer elemento del vector c(8, 4, 0). Tu función anónima debe tomar solo un argumento, que debe ser una variable x.

> evaluate(function(x){x[1]},c(8, 4, 0))
[1] 8

Ahora intenta usar evaluate() junto con una función anónima para devolver el último elemento del vector c(8, 4, 0). Tu función anónima debe tomar solo un argumento, que debe ser una variable x.

>
> evaluate(function(x){x[length(x)]},c(8, 4, 0))
[1] 0

Durante el resto del curso usaremos frecuentemente la función paste(). Escribe ?paste para consultar la documentación de la función paste.

> ?paste

¡Excelente trabajo!

|===============================================================                     |  75%

Como puedes ver, el primer argumento de paste() es …, que se conoce como elipsis o simplemente punto-punto-punto. La elipsis permite pasar un número indefinido de argumentos a una función. En el caso de paste(), cualquier número de cadenas puede pasarse como argumento, y paste() devolverá todas las cadenas combinadas en una sola cadena.

...
|=================================================================                   |  77%

Solo para ver cómo funciona paste(), escribe paste(«Programming», «is», «fun!»).

> paste("Programming", "is", "fun!")
[1] "Programming is fun!"
# Observa que la elipsis es el primer argumento, y que todos los demás argumentos después
# de la elipsis tienen valores predeterminados. Esta es una regla estricta en la programación
# con R: todos los argumentos después de una elipsis deben tener valores predeterminados.

function(...){
# ¡Haz aquí el desempaquetado de tus argumentos!

args <- list(...)
place <- args[["place"]]
adjective <- args[["adjective"]]
noun <- args[["noun"]]

# No modifiques ningún código debajo de este comentario.
# Observa las variables que necesitarás crear para que el código de abajo
# sea funcional.
paste("News from", place, "today where", adjective, "students took to the streets in protest of the new", noun, "being installed on campus.")
}
# La sintaxis para crear nuevos operadores binarios en R no se parece a casi nada más en
# R, pero te permite definir una nueva sintaxis para tu función. Solo recomendaría crear
# tu propio operador binario si planeas usarlo con frecuencia.
#
# Los operadores binarios definidos por el usuario tienen la siguiente sintaxis:
#      %[lo_que_sea]%
# donde [lo_que_sea] representa cualquier nombre válido de variable.
#
# Supongamos que quisiera definir un operador binario que multiplicara dos números y
# luego sumara uno al producto. Una implementación de ese operador se muestra abajo:
#
# "%mult_add_one%" <- function(left, right){ # ¡Observa las comillas!
#   left * right + 1
# }
#
# Entonces podría usar este operador binario así: `4 %mult_add_one% 5`, lo cual se
# evaluaría como 21.
#
# Escribe abajo tu propio operador binario desde cero. Tu operador binario debe llamarse
# %p% para que la expresión:
#
#       "Good" %p% "job!"
#
# se evalúe como: "Good job!"

"%p%" <- function(op1,op2){ # ¡Recuerda agregar argumentos!
paste(op1, op2)
}
> "I"%p%"love"%p%"R!"
[1] "I love R!"

lapply y sapply

En esta lección aprenderás a usar lapply() y sapply(), los dos miembros más importantes de la familia de funciones *apply de R, también conocidas como funciones de bucle.

Estas poderosas funciones, junto con sus parientes cercanos, como vapply() y tapply(), entre otras, ofrecen una forma concisa y conveniente de implementar la estrategia Dividir-Aplicar-Combinar para el análisis de datos.

Cada una de las funciones *apply DIVIDE algunos datos en partes más pequeñas, APLICA una función a cada parte y luego COMBINA los resultados. Una discusión más detallada de esta estrategia se encuentra en el artículo de Hadley Wickham publicado en el Journal of Statistical Software, titulado “The Split-Apply-Combine Strategy for Data Analysis”.

A lo largo de esta lección usaremos el conjunto de datos Flags del UCI Machine Learning Repository. Este conjunto de datos contiene detalles de varias naciones y sus banderas. Puede encontrarse más información aquí: http://archive.ics.uci.edu/ml/datasets/Flags

La función lapply() toma una lista como entrada, aplica una función a cada elemento de la lista y luego devuelve una lista de la misma longitud que la original. Como un data frame en realidad es solo una lista de vectores, puedes ver esto con as.list(flags), podemos usar lapply() para aplicar la función class() a cada columna del conjunto de datos flags.

Escribe cls_list <- lapply(flags, class) para aplicar la función class() a cada columna del conjunto de datos flags y guardar el resultado en una variable llamada cls_list. Observa que solo proporcionas el nombre de la función que quieres aplicar, es decir, class, sin los paréntesis habituales después de ella.

> cls_list <- lapply(flags, class)

¡Excelente trabajo!

|=====================                                                           |  24%

Escribe cls_list para ver el resultado.

> cls_list
$name
[1] "factor"

$landmass
[1] "integer"

$zone
[1] "integer"

$area
[1] "integer"

$population
[1] "integer"

$language
[1] "integer"

$religion
[1] "integer"

$bars
[1] "integer"

$stripes
[1] "integer"

$colours
[1] "integer"

$red
[1] "integer"

$green
[1] "integer"

$blue
[1] "integer"

$gold
[1] "integer"

$white
[1] "integer"

$black
[1] "integer"

$orange
[1] "integer"

$mainhue
[1] "factor"

$circles
[1] "integer"

$crosses
[1] "integer"

$saltires
[1] "integer"

$quarters
[1] "integer"

$sunstars
[1] "integer"

$crescent
[1] "integer"

$triangle
[1] "integer"

$icon
[1] "integer"

$animate
[1] "integer"

$text
[1] "integer"

$topleft
[1] "factor"

$botright
[1] "factor"

La “l” en lapply significa “list”, es decir, “lista”. Escribe class(cls_list) para confirmar que lapply() devolvió una lista.

>
> class(cls_list)
[1] "list"

Como esperábamos, obtuvimos una lista de longitud 30: un elemento por cada variable o columna. La salida sería considerablemente más compacta si pudiéramos representarla como un vector en lugar de una lista.

...
|==========================                                                      |  31%

Quizá recuerdes de una lección anterior que las listas son más útiles para almacenar múltiples clases de datos. En este caso, como cada elemento de la lista devuelta por lapply() es un vector de caracteres de longitud uno, por ejemplo «integer» y «vector», cls_list puede simplificarse a un vector de caracteres. Para hacer esto manualmente, escribe as.character(cls_list).

> as.character(cls_list)
[1] "factor"  "integer" "integer" "integer" "integer" "integer" "integer" "integer" "integer"
[10] "integer" "integer" "integer" "integer" "integer" "integer" "integer" "integer" "factor"
[19] "integer" "integer" "integer" "integer" "integer" "integer" "integer" "integer" "integer"
[28] "integer" "factor"  "factor"

sapply() te permite automatizar este proceso llamando a lapply() detrás de escena, pero después intenta simplificar el resultado por ti; de ahí la “s” en sapply. Usa sapply() de la misma forma en que usaste lapply() para obtener la clase de cada columna del conjunto de datos flags y guarda el resultado en cls_vect. Si necesitas ayuda, escribe ?sapply para abrir la documentación.

> ?sapply
> ?sapply
> cls_vect<-sapply(flags, class)

Esa era la respuesta que estaba buscando.

|=============================                                                   |  35%

Usa class(cls_vect) para confirmar que sapply() simplificó el resultado a un vector de caracteres.

> class(cls_vect)
[1] "character"

¡Lo lograste!

|===============================                                                 |  37%

En general, si el resultado es una lista donde cada elemento tiene longitud uno, entonces sapply() devuelve un vector. Si el resultado es una lista donde cada elemento es un vector de la misma longitud, mayor que 1, sapply() devuelve una matriz. Si sapply() no puede simplificar el resultado, entonces simplemente devuelve una lista, sin diferencia respecto a lo que devolvería lapply().

Las columnas 11 a 17 de nuestro conjunto de datos son variables indicadoras, cada una representando un color diferente. El valor de la variable indicadora es 1 si el color está presente en la bandera de un país y 0 en caso contrario.

...
|====================================                                            |  43%

Por lo tanto, si queremos saber el número total de países, dentro de nuestro conjunto de datos, que tienen, por ejemplo, el color naranja en su bandera, simplemente podemos sumar todos los 1 y 0 de la columna orange. Prueba sum(flags$orange) para ver esto.

> sum(flags$orange)
[1] 26

Primero, usa flag_colors <- flags[, 11:17] para extraer las columnas que contienen los datos de color y guardarlas en un nuevo data frame llamado flag_colors. Observa la coma antes de 11:17. Este comando de subconjunto le indica a R que queremos todas las filas, pero solo las columnas 11 a 17.

> flag_colors <- flags[, 11:17]

Usa la función head() para ver las primeras 6 líneas de flag_colors.

> head(flag_colors, n=6)
  red green blue gold white black orange
1   1     1    0    1     1     1      0
2   1     0    0    1     0     1      0
3   1     1    0    0     1     0      0
4   1     0    1    1     1     0      1
5   1     0    1    1     0     0      0
6   1     0    0    1     0     1      0

Para obtener una lista que contenga la suma de cada columna de flag_colors, llama a la función lapply() con dos argumentos. El primer argumento es el objeto sobre el cual estamos iterando, es decir, flag_colors, y el segundo argumento es el nombre de la función que queremos aplicar a cada columna, es decir, sum. Recuerda que el segundo argumento es solo el nombre de la función, sin paréntesis ni nada parecido.

> lapply(flag_colors,sum)
$red
[1] 153

$green
[1] 91

$blue
[1] 99

$gold
[1] 91

$white
[1] 146

$black
[1] 52

$orange
[1] 26
> sapply(flag_colors,sum)
   red  green   blue   gold  white  black orange
   153     91     99     91    146     52     26
> sapply(flag_colors,mean)
      red     green      blue      gold     white     black    orange
0.7886598 0.4690722 0.5103093 0.4690722 0.7525773 0.2680412 0.1340206

En los ejemplos que hemos visto hasta ahora, sapply() ha podido simplificar el resultado a un vector. Esto se debe a que cada elemento de la lista devuelta por lapply() era un vector de longitud uno. Recuerda que sapply(), en cambio, devuelve una matriz cuando cada elemento de la lista devuelta por lapply() es un vector de la misma longitud, mayor que 1.

...
|=====================================================                           |  63%

Para ilustrar esto, extraigamos las columnas 19 a 23 del conjunto de datos flags y guardemos el resultado en un nuevo data frame llamado flag_shapes. flag_shapes <- flags[, 19:23] lo hará.

> flag_shapes <- flags[, 19:23]

La función range() devuelve el mínimo y el máximo de su primer argumento, el cual debe ser un vector numérico.

> lapply(flag_shapes, range)
$circles
[1] 0 4

$crosses
[1] 0 2

$saltires
[1] 0 1

$quarters
[1] 0 4

$sunstars
[1]  0 50
> shape_mat<-sapply(flag_shapes, range)
> shape_mat
     circles crosses saltires quarters sunstars
[1,]       0       0        0        0        0
[2,]       4       2        1        4       50

Cuando recibe un vector, la función unique() devuelve un vector con todos los elementos duplicados eliminados. En otras palabras, unique() devuelve un vector compuesto solo por los elementos “únicos”. Para ver cómo funciona, prueba unique(c(3, 4, 5, 5, 5, 6, 6)).

> unique(c(3, 4, 5, 5, 5, 6, 6))
[1] 3 4 5 6
> unique_vals <- lapply(flags, unique)

¡Sigue trabajando así y lo lograrás!

|======================================================================          |  84%

Imprime el valor de unique_vals en la consola.

> unique_vals
$name
[1] Afghanistan              Albania                  Algeria
[4] American-Samoa           Andorra                  Angola
[7] Anguilla                 Antigua-Barbuda          Argentina
[10] Argentine                Australia                Austria
[13] Bahamas                  Bahrain                  Bangladesh
[16] Barbados                 Belgium                  Belize
[19] Benin                    Bermuda                  Bhutan
[22] Bolivia                  Botswana                 Brazil
[25] British-Virgin-Isles     Brunei                   Bulgaria
[28] Burkina                  Burma                    Burundi
[31] Cameroon                 Canada                   Cape-Verde-Islands
[34] Cayman-Islands           Central-African-Republic Chad
[37] Chile                    China                    Colombia
[40] Comorro-Islands          Congo                    Cook-Islands
[43] Costa-Rica               Cuba                     Cyprus
[46] Czechoslovakia           Denmark                  Djibouti
[49] Dominica                 Dominican-Republic       Ecuador
[52] Egypt                    El-Salvador              Equatorial-Guinea
[55] Ethiopia                 Faeroes                  Falklands-Malvinas
[58] Fiji                     Finland                  France
[61] French-Guiana            French-Polynesia         Gabon
[64] Gambia                   Germany-DDR              Germany-FRG
[67] Ghana                    Gibraltar                Greece
[70] Greenland                Grenada                  Guam
[73] Guatemala                Guinea                   Guinea-Bissau
[76] Guyana                   Haiti                    Honduras
[79] Hong-Kong                Hungary                  Iceland
[82] India                    Indonesia                Iran
[85] Iraq                     Ireland                  Israel
[88] Italy                    Ivory-Coast              Jamaica
[91] Japan                    Jordan                   Kampuchea
[94] Kenya                    Kiribati                 Kuwait
[97] Laos                     Lebanon                  Lesotho
[100] Liberia                  Libya                    Liechtenstein
[103] Luxembourg               Malagasy                 Malawi
[106] Malaysia                 Maldive-Islands          Mali
[109] Malta                    Marianas                 Mauritania
[112] Mauritius                Mexico                   Micronesia
[115] Monaco                   Mongolia                 Montserrat
[118] Morocco                  Mozambique               Nauru
[121] Nepal                    Netherlands              Netherlands-Antilles
[124] New-Zealand              Nicaragua                Niger
[127] Nigeria                  Niue                     North-Korea
[130] North-Yemen              Norway                   Oman
[133] Pakistan                 Panama                   Papua-New-Guinea
[136] Parguay                  Peru                     Philippines
[139] Poland                   Portugal                 Puerto-Rico
[142] Qatar                    Romania                  Rwanda
[145] San-Marino               Sao-Tome                 Saudi-Arabia
[148] Senegal                  Seychelles               Sierra-Leone
[151] Singapore                Soloman-Islands          Somalia
[154] South-Africa             South-Korea              South-Yemen
[157] Spain                    Sri-Lanka                St-Helena
[160] St-Kitts-Nevis           St-Lucia                 St-Vincent
[163] Sudan                    Surinam                  Swaziland
[166] Sweden                   Switzerland              Syria
[169] Taiwan                   Tanzania                 Thailand
[172] Togo                     Tonga                    Trinidad-Tobago
[175] Tunisia                  Turkey                   Turks-Cocos-Islands
[178] Tuvalu                   UAE                      Uganda
[181] UK                       Uruguay                  US-Virgin-Isles
[184] USA                      USSR                     Vanuatu
[187] Vatican-City             Venezuela                Vietnam
[190] Western-Samoa            Yugoslavia               Zaire
[193] Zambia                   Zimbabwe
194 Levels: Afghanistan Albania Algeria American-Samoa Andorra Angola ... Zimbabwe

$landmass
[1] 5 3 4 6 1 2

$zone
[1] 1 3 2 4

$area
[1]   648    29  2388     0  1247  2777  7690    84    19     1   143    31    23   113
[15]    47  1099   600  8512     6   111   274   678    28   474  9976     4   623  1284
[29]   757  9561  1139     2   342    51   115     9   128    43    22    49   284  1001
[43]    21  1222    12    18   337   547    91   268    10   108   249   239   132  2176
[57]   109   246    36   215   112    93   103  3268  1904  1648   435    70   301   323
[71]    11   372    98   181   583   236    30  1760     3   587   118   333  1240  1031
[85]  1973  1566   447   783   140    41  1267   925   121   195   324   212   804    76
[99]   463   407  1285   300   313    92   237    26  2150   196    72   637  1221    99
[113]   288   505    66  2506    63    17   450   185   945   514    57     5   164   781
[127]   245   178  9363 22402    15   912   256   905   753   391

$population
[1]   16    3   20    0    7   28   15    8   90   10    1    6  119    9   35    4   24    2
[19]   11 1008    5   47   31   54   17   61   14  684  157   39   57  118   13   77   12   56
[37]   18   84   48   36   22   29   38   49   45  231  274   60

$language
[1] 10  6  8  1  2  4  3  5  7  9

$religion
[1] 2 6 1 0 5 3 4 7

$bars
[1] 0 2 3 1 5

$stripes
[1]  3  0  2  1  5  9 11 14  4  6 13  7

$colours
[1] 5 3 2 8 6 4 7 1

$red
[1] 1 0

$green
[1] 1 0

$blue
[1] 0 1

$gold
[1] 1 0

$white
[1] 1 0

$black
[1] 1 0

$orange
[1] 0 1

$mainhue
[1] green  red    blue   gold   white  orange black  brown
Levels: black blue brown gold green orange red white

$circles
[1] 0 1 4 2

$crosses
[1] 0 1 2

$saltires
[1] 0 1

$quarters
[1] 0 1 4

$sunstars
[1]  1  0  6 22 14  3  4  5 15 10  7  2  9 50

$crescent
[1] 0 1

$triangle
[1] 0 1

$icon
[1] 1 0

$animate
[1] 0 1

$text
[1] 0 1

$topleft
[1] black  red    green  blue   white  orange gold
Levels: black blue gold green orange red white

$botright
[1] green  red    white  black  blue   gold   orange brown
Levels: black blue brown gold green orange red white
> sapply(unique_vals, length)
      name   landmass       zone       area population   language   religion       bars
       194          6          4        136         48         10          8          5
   stripes    colours        red      green       blue       gold      white      black
        12          8          2          2          2          2          2          2
    orange    mainhue    circles    crosses   saltires   quarters   sunstars   crescent
         2          8          4          3          2          3         14          2
  triangle       icon    animate       text    topleft   botright
         2          2          2          2          7          8

Ocasionalmente, quizá necesites aplicar una función que aún no está definida, lo que requiere que escribas tu propia función. Escribir funciones en R está fuera del alcance de esta lección, pero veamos un ejemplo rápido de cómo podrías hacerlo en el contexto de las funciones de bucle.

...
|===============================================================================     |  94%

Supón que solo te interesa el segundo elemento de cada elemento de la lista unique_vals que acabas de crear. Como cada elemento de la lista unique_vals es un vector y no conocemos ninguna función incorporada en R que devuelva el segundo elemento de un vector, construiremos nuestra propia función.

...
|=================================================================================   |  96%

lapply(unique_vals, function(elem) elem[2]) devolverá una lista que contiene el segundo elemento de cada elemento de la lista unique_vals. Observa que nuestra función toma un argumento, elem, que es simplemente una “variable ficticia” que toma, por turno, el valor de cada elemento de unique_vals.

> lapply(unique_vals, function(elem) elem[2])
$name
[1] Albania
194 Levels: Afghanistan Albania Algeria American-Samoa Andorra Angola ... Zimbabwe

$landmass
[1] 3

$zone
[1] 3

$area
[1] 29

$population
[1] 3

$language
[1] 6

$religion
[1] 6

$bars
[1] 2

$stripes
[1] 0

$colours
[1] 3

$red
[1] 0

$green
[1] 0

$blue
[1] 1

$gold
[1] 0

$white
[1] 0

$black
[1] 0

$orange
[1] 1

$mainhue
[1] red
Levels: black blue brown gold green orange red white

$circles
[1] 1

$crosses
[1] 1

$saltires
[1] 1

$quarters
[1] 1

$sunstars
[1] 0

$crescent
[1] 1

$triangle
[1] 1

$icon
[1] 0

$animate
[1] 1

$text
[1] 1

$topleft
[1] red
Levels: black blue gold green orange red white

$botright
[1] red
Levels: black blue brown gold green orange red white

La única diferencia entre los ejemplos anteriores y este es que estamos definiendo y usando nuestra propia función directamente en la llamada a lapply(). Nuestra función no tiene nombre y desaparece tan pronto como lapply() termina de usarla. Las llamadas “funciones anónimas” pueden ser muy útiles cuando una de las funciones incorporadas de R no es una opción.

vapply y tapply

El conjunto de datos Flags del UCI Machine Learning Repository contiene detalles de varias naciones y sus banderas. Puede encontrarse más información aquí: http://archive.ics.uci.edu/ml/datasets/Flags

Mientras que sapply() intenta “adivinar” el formato correcto del resultado, vapply() te permite especificarlo explícitamente. Si el resultado no coincide con el formato que especificas, vapply() lanzará un error, haciendo que la operación se detenga. Esto puede prevenir problemas importantes en tu código que podrían ser causados por valores de retorno inesperados de sapply().

Prueba vapply(flags, unique, numeric(1)), lo cual indica que esperas que cada elemento del resultado sea un vector numérico de longitud 1. Como este NO es realmente el caso, OBTENDRÁS UN ERROR. Una vez que obtengas el error, escribe ok() para continuar con la siguiente pregunta.

> vapply(flags, unique, numeric(1))
Error in vapply(flags, unique, numeric(1)) : values must be length 1,
but FUN(X[[1]]) result is length 194
> ok()

Recuerda de la lección anterior que sapply(flags, class) devolverá un vector de caracteres que contiene la clase de cada columna del conjunto de datos. Inténtalo de nuevo ahora para ver el resultado.

> sapply(flags, class)
      name   landmass       zone       area population   language   religion       bars
  "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
   stripes    colours        red      green       blue       gold      white      black
 "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
    orange    mainhue    circles    crosses   saltires   quarters   sunstars   crescent
 "integer"   "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
  triangle       icon    animate       text    topleft   botright
 "integer"  "integer"  "integer"  "integer"   "factor"   "factor"

Si queremos ser explícitos sobre el formato del resultado que esperamos, podemos usar vapply(flags, class, character(1)). El argumento character(1) le dice a R que esperamos que la función class devuelva un vector de caracteres de longitud 1 cuando se aplique a CADA columna del conjunto de datos flags. Pruébalo ahora.

> vapply(flags, class, character(1))
      name   landmass       zone       area population   language   religion       bars
  "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
   stripes    colours        red      green       blue       gold      white      black
 "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
    orange    mainhue    circles    crosses   saltires   quarters   sunstars   crescent
 "integer"   "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
  triangle       icon    animate       text    topleft   botright
 "integer"  "integer"  "integer"  "integer"   "factor"   "factor"

Observa que, como nuestra expectativa fue correcta, es decir, character(1), el resultado de vapply() es idéntico al resultado de sapply(): un vector de caracteres con las clases de las columnas.

...
|==========================================                                      |  50%

Puedes pensar en vapply() como una alternativa “más segura” que sapply(), ya que requiere que especifiques de antemano el formato de la salida, en lugar de permitir que R simplemente “adivine” lo que querías. Además, vapply() puede ser más rápida que sapply() para conjuntos de datos grandes. Sin embargo, cuando haces análisis de datos de forma interactiva, en la consola, sapply() te ahorra algo de escritura y con frecuencia será suficiente.

...
|==============================================                                  |  54%

Como analista de datos, a menudo querrás dividir tus datos en grupos según el valor de alguna variable y luego aplicar una función a los miembros de cada grupo. La siguiente función que veremos, tapply(), hace exactamente eso.

La variable landmass de nuestro conjunto de datos toma valores enteros entre 1 y 6, cada uno de los cuales representa una parte diferente del mundo. Usa table(flags$landmass) para ver cuántas banderas o países caen en cada grupo.

> table(flags$landmass)

 1  2  3  4  5  6
31 17 35 52 39 20

La variable animate de nuestro conjunto de datos toma el valor 1 si la bandera de un país contiene una imagen animada, por ejemplo un águila, un árbol o una mano humana, y 0 en caso contrario. Usa table(flags$animate) para ver cuántas banderas contienen una imagen animada.

> table(flags$animate)

  0   1
155  39

Si calculas la media aritmética de un conjunto de ceros y unos, obtienes la proporción de unos. Usa tapply(flags$animate, flags$landmass, mean) para aplicar la función mean a la variable animate por separado para cada uno de los seis grupos de landmass, dándonos así la proporción de banderas que contienen una imagen animada DENTRO de cada grupo de landmass.

> tapply(flags$animate, flags$landmass, mean)
        1         2         3         4         5         6
0.4193548 0.1764706 0.1142857 0.1346154 0.1538462 0.3000000

De manera similar, podemos ver un resumen de los valores de población, en millones redondeados, para países con y sin el color rojo en su bandera con tapply(flags$population, flags$red, summary).

> tapply(flags$population, flags$red,summary)
$`0`
 Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
 0.00    0.00    3.00   27.63    9.00  684.00

$`1`
 Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
 0.0     0.0     4.0    22.1    15.0  1008.0

Observación de datos

Siempre que trabajes con un nuevo conjunto de datos, lo primero que debes hacer es mirarlo. ¿Cuál es el formato de los datos? ¿Cuáles son sus dimensiones? ¿Cuáles son los nombres de las variables? ¿Cómo están almacenadas las variables? ¿Hay datos faltantes? ¿Hay fallas en los datos?

Esta lección te enseñará cómo responder estas preguntas y más usando las funciones incorporadas de R. Usaremos un conjunto de datos construido a partir de la base de datos PLANTS del Departamento de Agricultura de Estados Unidos: http://plants.usda.gov/adv_search.html

He almacenado los datos para ti en una variable llamada plants. Escribe ls() para listar las variables en tu espacio de trabajo, entre las cuales debería estar plants.

> ls()
[1] "%p%"             "boring_function" "cls_list"        "cls_vect"        "cnames"
[6] "evaluate"        "flag_colors"     "flag_shapes"     "flags"           "ints"
[11] "mad_libs"        "my_char"         "my_data"         "my_div"          "my_matrix"
[16] "my_matrix2"      "my_mean"         "my_na"           "my_name"         "my_seq"
[21] "my_sqe"          "my_sqrt"         "my_vector"       "num_vect"        "ok"
[26] "old.dir"         "patients"        "plants"          "remainder"       "shape_mat"
[31] "telegram"        "tf"              "unique_vals"     "vect"            "vect2"
[36] "viewinfo"        "x"               "y"               "z"

¡Buen trabajo!

|==========                                                                      |  12%

Comencemos revisando la clase de la variable plants con class(plants). Esto nos dará una pista sobre la estructura general de los datos.

> class(plants)
[1] "data.frame"

¡Eres el mejor!

|==============                                                                  |  17%

Es muy común que los datos se almacenen en un data frame. Es la clase predeterminada para los datos leídos en R usando funciones como read.csv() y read.table(), que aprenderás en otra lección.

...
|==================                                                              |  21%

Como el conjunto de datos está almacenado en un data frame, sabemos que es rectangular. En otras palabras, tiene dos dimensiones, filas y columnas, y encaja ordenadamente en una tabla u hoja de cálculo. Usa dim(plants) para ver exactamente con cuántas filas y columnas estamos tratando.

> dim(plants)
[1] 5166   10

¡Lo lograste! ¡Buen trabajo!

|=====================                                                           |  25%

El primer número que ves, 5166, es el número de filas u observaciones, y el segundo número, 10, es el número de columnas o variables.

...
|========================                                                        |  29%

También puedes usar nrow(plants) para ver únicamente el número de filas. Pruébalo.

> nrow(plants)
[1] 5166

¡Vas muy bien!

|============================                                                    |  33%

… Y ncol(plants) para ver únicamente el número de columnas.

> ncol(plants)
[1] 10

¡Lo estás haciendo muy bien!

|================================                                                |  38%

Si tienes curiosidad por saber cuánto espacio ocupa el conjunto de datos en memoria, puedes usar object.size(plants).

> object.size(plants)
644232 bytes

Ahora que tenemos una idea de la forma y el tamaño del conjunto de datos, veamos qué contiene. names(plants) devolverá un vector de caracteres con los nombres de las columnas, es decir, de las variables. Inténtalo.

> names(plants)
[1] "Scientific_Name"      "Duration"             "Active_Growth_Period"
[4] "Foliage_Color"        "pH_Min"               "pH_Max"
[7] "Precip_Min"           "Precip_Max"           "Shade_Tolerance"
[10] "Temp_Min_F"

La función head() te permite previsualizar la parte superior del conjunto de datos. Pruébala con un solo argumento.

> head(plants)
              Scientific_Name          Duration Active_Growth_Period Foliage_Color pH_Min
1                  Abelmoschus              <NA>                 <NA>          <NA>     NA
2       Abelmoschus esculentus Annual, Perennial                 <NA>          <NA>     NA
3                        Abies              <NA>                 <NA>          <NA>     NA
4               Abies balsamea         Perennial    Spring and Summer         Green      4
5 Abies balsamea var. balsamea         Perennial                 <NA>          <NA>     NA
6                     Abutilon              <NA>                 <NA>          <NA>     NA
  pH_Max Precip_Min Precip_Max Shade_Tolerance Temp_Min_F
1     NA         NA         NA            <NA>         NA
2     NA         NA         NA            <NA>         NA
3     NA         NA         NA            <NA>         NA
4      6         13         60        Tolerant        -43
5     NA         NA         NA            <NA>         NA
6     NA         NA         NA            <NA>         NA

De forma predeterminada, head() muestra las primeras seis filas de los datos. Puedes modificar este comportamiento pasando como segundo argumento el número de filas que quieres ver. Usa head() para previsualizar las primeras 10 filas de plants.

> head(plants,n=10)

tail(plants, 15)
> summary(plants)

summary() proporciona una salida diferente para cada variable, dependiendo de su clase. Para datos numéricos como Precip_Min, summary() muestra el mínimo, el primer cuartil, la mediana, la media, el tercer cuartil y el máximo. Estos valores nos ayudan a entender cómo están distribuidos los datos.

Para variables categóricas, llamadas variables factor en R, summary() muestra el número de veces que cada valor o “nivel” aparece en los datos. Por ejemplo, cada valor de Scientific_Name aparece solo una vez, ya que es único para una planta específica. En contraste, el resumen de Duration, también una variable factor, nos dice que nuestro conjunto de datos contiene 3031 plantas Perennial, 682 plantas Annual, etc.

Puedes ver que R truncó el resumen de Active_Growth_Period incluyendo una categoría general llamada Other. Como es una variable categórica o factor, podemos ver cuántas veces aparece realmente cada valor en los datos con table(plants$Active_Growth_Period).

> table(plants$Active_Growth_Period)

Fall, Winter and Spring                  Spring         Spring and Fall
                      15                     144                      10
       Spring and Summer    Spring, Summer, Fall                  Summer
                     447                      95                      92
         Summer and Fall              Year Round
                      24                       5

Quizá la función más útil y concisa para entender la estructura de tus datos sea str(). Pruébala ahora.

> str(plants)

La belleza de str() es que combina muchas de las características de las otras funciones que ya has visto, todo en un formato conciso y legible. En la parte superior nos dice que la clase de plants es data.frame y que tiene 5166 observaciones y 10 variables. Después nos da el nombre y la clase de cada variable, así como una vista previa de su contenido.

...
|================================================================================    |  96%

str() es en realidad una función muy general que puedes usar sobre la mayoría de los objetos en R. Cada vez que quieras entender la estructura de algo, ya sea un conjunto de datos, una función, etc., str() es un buen punto de partida.

Simulación

Una de las grandes ventajas de usar un lenguaje de programación estadística como R es su vasta colección de herramientas para simular números aleatorios.

Simulemos el lanzamiento de cuatro dados de seis caras: sample(1:6, 4, replace = TRUE).

> sample(1:6, 4, replace = TRUE)
[1] 5 3 4 6

Escribe sample(1:20, 10) para seleccionar una muestra de 10 números entre 1 y 20, sin reemplazo.

> sample(1:20:10)
[1]  2  6  5  8  9  4  3  7  1 10
Warning message:
In 1:20:10 : numerical expression has 20 elements: only the first used

¡Sigue intentando! O escribe info() para ver más opciones.

Escribe sample(1:20, 10) para seleccionar una muestra de 10 números entre 1 y 20, sin reemplazo.

> sample(1:20, 10)
[1]  9  3 12 19  8 17  2  6  4 16

Como el último comando hizo la muestra sin reemplazo, ningún número aparece más de una vez en la salida.

LETTERS es una variable predefinida en R que contiene un vector con las 26 letras del alfabeto inglés. Obsérvala ahora.

>
> LETTERS
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V"
[23] "W" "X" "Y" "Z"

La función sample() también puede usarse para permutar, o reordenar, los elementos de un vector. Por ejemplo, prueba sample(LETTERS) para permutar las 26 letras del alfabeto inglés.

> sample(LETTERS)
[1] "V" "W" "F" "Q" "K" "Z" "L" "M" "B" "T" "Y" "R" "A" "O" "U" "J" "I" "N" "P" "D" "H" "G"
[23] "C" "E" "S" "X"

Haz que el valor 0 represente cruz y el valor 1 represente cara. Usa sample() para extraer una muestra de tamaño 100 del vector c(0,1), con reemplazo. Como la moneda está sesgada, debemos asignar probabilidades específicas a los valores 0, cruz, y 1, cara, con un cuarto argumento: prob = c(0.3, 0.7). Asigna el resultado a una nueva variable llamada flips.

> flips<-sample(c(0,1),100, replacement=TRUE, prob=c(0.3, 0.7))
Error in sample(c(0, 1), 100, replacement = TRUE, prob = c(0.3, 0.7)) :
unused argument (replacement = TRUE)
> flips<-sample(c(0,1),100, replace=TRUE, prob=c(0.3, 0.7))
> flips
[1] 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 0 0 1 0 0 0 1
[45] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 0 1 1 1 0 1 0 1 1 1 0 1 1 0 1 1 0 0
[89] 1 0 1 1 1 1 1 0 1 0 1 0

Como establecimos la probabilidad de obtener cara en cualquier lanzamiento en 0.7, esperaríamos que aproximadamente 70 de nuestros lanzamientos de moneda tuvieran el valor 1. Cuenta el número real de unos contenidos en flips usando la función sum().

> sum(flips)
[1] 74

Cada distribución de probabilidad en R tiene una función r***, por “random”; una función d***, por “density”; una función p***, por “probability”; y una función q***, por “quantile”. En esta lección nos interesan principalmente las funciones r***, pero te animo a explorar las demás por tu cuenta.

Una variable aleatoria binomial representa el número de “éxitos”, caras, en un número dado de “ensayos” independientes, lanzamientos de moneda. Por lo tanto, podemos generar una sola variable aleatoria que represente el número de caras en 100 lanzamientos de nuestra moneda sesgada usando rbinom(1, size = 100, prob = 0.7). Observa que solo especificas la probabilidad de “éxito”, cara, y NO la probabilidad de “fracaso”, cruz. Inténtalo ahora.

> rbinom(1, size =100, prob = 0.7)
[1] 69

De manera equivalente, si queremos ver todos los ceros y unos, podemos solicitar 100 observaciones, cada una de tamaño 1, con probabilidad de éxito de 0.7. Inténtalo, asignando el resultado a una nueva variable llamada flips2.

> flips2<-rbinom(1, size =100, prob = 0.7)

No exactamente, pero sigue intentando. O escribe info() para ver más opciones.

Llama a rbinom() con n = 100, size = 1 y prob = 0.7, y asigna el resultado a flips2.

> flips2<-rbinom(n=100, size =1, prob = 0.7)
> flips2
[1] 1 1 0 0 1 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0 0 1 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 0 0
[45] 1 1 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0
[89] 1 0 0 1 0 1 1 0 1 0 0 1

Ahora usa sum() para contar el número de unos, es decir, caras, en flips2. ¡Debería estar cerca de 70!

> sum(flips2)
[1] 70

La distribución normal estándar tiene media 0 y desviación estándar 1. Como puedes ver en la sección Usage de la documentación, los valores predeterminados para los argumentos mean y sd de rnorm() son 0 y 1, respectivamente. Por lo tanto, rnorm(10) generará 10 números aleatorios de una distribución normal estándar. Inténtalo.

> rnomr(10)
Error: could not find function "rnomr"
> rnorm(10)
[1]  1.17680121  0.34531770  1.08200926 -1.06659155  0.51239600  1.10383585  0.84827705
[8]  0.06453649  0.99852214  1.06458334

Usa rnorm(10, mean = 100, sd = 25) para generar 10 números aleatorios de una distribución normal con media 100 y desviación estándar 25.

> rnorm(10, mean = 100, sd = 25)
[1]  83.13575 132.85562  91.76528  75.15640  68.22486  71.95940 107.73553  70.11017 136.26369
[10] 109.66943

Finalmente, ¿qué pasa si queremos simular 100 grupos de números aleatorios, cada uno con 5 valores generados a partir de una distribución de Poisson con media 10? Empecemos con un grupo de 5 números, luego te mostraré cómo repetir la operación 100 veces de una forma conveniente y compacta.

Genera 5 valores aleatorios de una distribución de Poisson con media 10. Consulta la documentación de rpois() si necesitas ayuda.

> ?rpois
> rpois(5,10)
[1]  8 13  9 10 10

Ahora usa replicate(100, rpois(5, 10)) para realizar esta operación 100 veces. Guarda el resultado en una nueva variable llamada my_pois.

>
> my_pois<-replicate(100, rpois(5, 10))
> my_pois
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
[1,]    8    8   15    8    9   11   12   12   11     9     6     9    12     8     8    12
[2,]    8    9   13   11   11   13   11    9    4     7     8    10     4    12    11    12
[3,]   12   12    6    7   10    9   12    5   10    12    10     8     9     5    10     9
[4,]   15    9   10    9   16    5    7   10    6    11     7    15     9     4    17    10
[5,]   12    8   14    6    9    9   10    9   12     9    11    17     7    11     8    15

...

      [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98] [,99] [,100]
[1,]     8    14    10     8    11    10    11     8     6     7    11     6     9      8
[2,]     7    13    14    10    13     7     2     7    10     9    14     6    13      8
[3,]     4    12    13     8     7     8     4     6    10    11    18     6    11      5
[4,]     9     8     7    14    13     9     8    11    10    14     9    10    12      5
[5,]    10    11    12     6     9     7     6    14     9     7     9    13     6      7

replicate() creó una matriz, cada una de cuyas columnas contiene 5 números aleatorios generados a partir de una distribución de Poisson con media 10. Ahora podemos encontrar la media de cada columna en my_pois usando la función colMeans(). Guarda el resultado en una variable llamada cm.

> colMeans(my_pois)
[1] 11.0  9.2 11.6  8.2 11.0  9.4 10.4  9.0  8.6  9.6  8.4 11.8  8.2  8.0 10.8 11.6  8.8
[18] 10.0 10.6  9.0 11.0 10.2 12.2  9.6 11.2  7.8 10.2  9.6 10.8 11.0 10.6  7.4  7.6 10.6
[35]  7.8  9.6 12.0  7.8  8.0 13.4 11.6 10.0  8.8 10.2  8.6  9.2 12.8 11.2 10.2 11.6  9.2
[52]  8.8  9.0  8.8 12.0 11.8  9.4 11.0  9.0 11.0 10.6 11.0 10.6  9.0 11.6 11.6 11.0  9.2
[69]  8.6  9.2 14.0 10.8  9.0 10.0 11.2  9.2  8.8 10.8  8.4  9.8  9.4 13.8 11.4  9.8 10.0
[86] 10.6  7.6 11.6 11.2  9.2 10.6  8.2  6.2  9.2  9.0  9.6 12.2  8.2 10.2  6.6

> cm <- colMeans(my_pois)

> hist(cm)

¡Eres muy bueno, amigo!

|===============================================================================     |  94%

Parece que nuestras medias de columnas están casi normalmente distribuidas, ¿cierto? Ese es el Teorema Central del Límite en acción, pero esa es una lección para otro día.

...
|=================================================================================   |  97%

Todas las distribuciones de probabilidad estándar están incorporadas en R, incluyendo la exponencial rexp(), chi-cuadrada rchisq(), gamma rgamma(), etc. Bueno, ya ves el patrón.

...
|====================================================================================| 100%

La simulación es prácticamente un campo por sí misma y apenas hemos rozado la superficie de lo que es posible. Te animo a explorar estas y otras funciones por tu cuenta.

Fechas y horas

R tiene una forma especial de representar fechas y horas, lo cual puede ser útil si estás trabajando con datos que muestran cómo algo cambia con el tiempo, es decir, datos de series de tiempo, o si tus datos contienen alguna otra información temporal, como fechas de nacimiento.

...
|==                                                                              |   3%

Las fechas se representan mediante la clase Date y las horas se representan mediante las clases POSIXct y POSIXlt. Internamente, las fechas se almacenan como el número de días desde 1970-01-01, y las horas se almacenan como el número de segundos desde 1970-01-01, para POSIXct, o como una lista de segundos, minutos, horas, etc., para POSIXlt.

...
|=====                                                                           |   6%

Comencemos usando d1 <- Sys.Date() para obtener la fecha actual y almacenarla en la variable d1. Esa es la letra d y el número 1.

>
> d1 <- Sys.Date()

> class(d1)
[1] "Date"

Podemos usar la función unclass() para ver cómo luce d1 internamente. Inténtalo.

> unclass(d1)
[1] 16522

¡Ese es el número exacto de días desde 1970-01-01!

Sin embargo, si imprimes d1 en la consola, obtendrás la fecha de hoy en el formato AÑO-MES-DÍA. Inténtalo.

> d1
[1] "2015-03-28"

¿Qué pasa si necesitamos hacer referencia a una fecha anterior a 1970-01-01? Crea una variable d2 que contenga as.Date(«1969-01-01»).

> as.Date("1969-01-01")
[1] "1969-01-01"

¡Casi! Inténtalo de nuevo. O escribe info() para ver más opciones.

Prueba d2 <- as.Date(«1969-01-01»).

> d2 <- as.Date("1969-01-01")

> unclass(d2)
[1] -365

¡Lo hiciste bien!

|======================                                                          |  26%

Como quizá anticipaste, obtienes un número negativo. En este caso es -365, ya que 1969-01-01 está exactamente un año calendario, es decir, 365 días, ANTES de 1970-01-01.

Ahora veamos cómo R almacena las horas. Puedes acceder a la fecha y hora actuales usando la función Sys.time() sin argumentos. Hazlo y guarda el resultado en una variable llamada t1.

> t1<-Sys.time()

> t1
[1] "2015-03-28 20:15:26 CST"

> class(t1)
[1] "POSIXct" "POSIXt"

Como se mencionó antes, POSIXct es solo una de las dos formas en que R representa información temporal. Puedes ignorar el segundo valor anterior, POSIXt, que simplemente funciona como un lenguaje común entre POSIXct y POSIXlt. Usa unclass() para ver cómo luce t1 internamente: el gran número de segundos desde el inicio de 1970.

>
> unclass(t1)
[1] 1427595327

De forma predeterminada, Sys.time() devuelve un objeto de clase POSIXct, pero podemos convertir el resultado a POSIXlt con as.POSIXlt(Sys.time()). Inténtalo y guarda el resultado en t2.

> as.POSIXlt(Sys.time())
[1] "2015-03-28 20:16:40 CST"

> t2 <- as.POSIXlt(Sys.time())

> class(t2)
[1] "POSIXlt" "POSIXt"

> unclass(t2)
$sec
[1] 58.25504

$min
[1] 16

$hour
[1] 20

$mday
[1] 28

$mon
[1] 2

$year
[1] 115

$wday
[1] 6

$yday
[1] 86

$isdst
[1] 0

$zone
[1] "CST"

$gmtoff
[1] -21600

attr(,"tzone")
[1] ""    "CST" "CDT"

> str(unclass(t2))
List of 11
$ sec   : num 58.3
$ min   : int 16
$ hour  : int 20
$ mday  : int 28
$ mon   : int 2
$ year  : int 115
$ wday  : int 6
$ yday  : int 86
$ isdst : int 0
$ zone  : chr "CST"
$ gmtoff: int -21600
- attr(*, "tzone")= chr [1:3] "" "CST" "CDT"

Si, por ejemplo, queremos solo los minutos de la hora almacenada en t2, podemos acceder a ellos con t2$min. Inténtalo.

> t2$min
[1] 16

Ahora que hemos explorado los tres tipos de objetos de fecha y hora, veamos algunas funciones que extraen información útil de cualquiera de estos objetos: weekdays(), months() y quarters().

...
|==================================================                              |  60%

La función weekdays() devolverá el día de la semana de cualquier objeto de fecha u hora. Pruébala con d1, que es el objeto Date que contiene la fecha de hoy.

> weekdays(d1)
[1] "Saturday"

La función months() también funciona sobre cualquier objeto de fecha u hora. Pruébala con t1, que es el objeto POSIXct que contiene la hora actual, o más bien, la que era la hora actual cuando lo creaste.

> months(d1)
[1] "March"

Estás cerca… ¡puedo sentirlo! Inténtalo otra vez. O escribe info() para ver más opciones.

months(t1) te dará el mes actual.

> months(t1)
[1] "March"

La función quarters() devuelve el trimestre del año, de Q1 a Q4, a partir de cualquier objeto de fecha u hora. Pruébala con t2, que es el objeto POSIXlt que contiene la hora en que lo creaste.

> quarters(t2)
[1] "Q1"

¡Lo estás haciendo muy bien!

|==========================================================                      |  69%

A menudo, las fechas y horas en un conjunto de datos estarán en un formato que R no reconoce. La función strptime() puede ser útil en esta situación.

...
|============================================================                    |  71%

strptime() convierte vectores de caracteres a POSIXlt. En ese sentido, es similar a as.POSIXlt(), excepto que la entrada no tiene que estar en un formato particular como AAAA-MM-DD.

...
|==============================================================                  |  74%

Para ver cómo funciona, guarda la siguiente cadena de caracteres en una variable llamada t3: «October 17, 1986 08:24», con las comillas.

> t3<-"October 17, 1986 08:24"

Ahora usa strptime(t3, «%B %d, %Y %H:%M») para ayudar a R a convertir nuestro objeto de fecha y hora a un formato que pueda entender. Asigna el resultado a una nueva variable llamada t4. Puedes abrir la documentación de strptime() si quieres saber más sobre cómo funciona.

> strptime(t3, "%B %d, %Y %H:%M")
[1] "1986-10-17 08:24:00 CST"

Eso no es exactamente lo que estoy buscando. Inténtalo de nuevo. O escribe info() para ver más opciones.

t4 <- strptime(t3, «%B %d, %Y %H:%M») convertirá nuestro objeto de fecha y hora a un formato que R entiende.

> t4 <- strptime(t3, "%B %d, %Y %H:%M")

¡Lo lograste! ¡Buen trabajo!

|===================================================================             |  80%

Imprime el contenido de t4.

> t4
[1] "1986-10-17 08:24:00 CST"

> class(t4)
[1] "POSIXlt" "POSIXt"

Finalmente, hay varias operaciones que puedes realizar con fechas y horas, incluyendo operaciones aritméticas, como + y -, y comparaciones, como <, ==, etc.

...
|==========================================================================      |  89%

La variable t1 contiene la hora en que la creaste, recuerda que usaste Sys.time(). Confirma que ha pasado algo de tiempo desde que creaste t1 usando el operador “mayor que” para compararla con la hora actual: Sys.time() > t1

> Sys.time() > t1
[1] TRUE

> Sys.time() - t1
Time difference of 7.952651 mins

Usa difftime(Sys.time(), t1, units = ‘days’) para encontrar la cantidad de tiempo en DÍAS que ha pasado desde que creaste t1.

> difftime(Sys.time(), t1, units = 'days')
Time difference of 0.005763764 days
|====================================================================================| 100%

En esta lección aprendiste cómo trabajar con fechas y horas en R. Aunque es importante entender lo básico, si trabajas con fechas y horas con frecuencia, quizá quieras revisar el paquete lubridate de Hadley Wickham.

Gráficos base

Una de las mayores fortalezas de R, en comparación con otros lenguajes de programación, es la facilidad con la que podemos crear gráficos de calidad publicable. En esta lección aprenderás sobre los gráficos base en R.

No cubriremos las partes más avanzadas de los gráficos en R en esta lección. Estas incluyen lattice, ggplot2 y ggvis.

...
|====                                                                            |   4%

Existe una corriente de pensamiento que sostiene que este enfoque está al revés, y que deberíamos enseñar ggplot2 primero. Consulta http://varianceexplained.org/r/teach_ggplot2_to_beginners/ para ver un esquema de esta postura.

> head(cars)
  speed dist
1     4    2
2     4   10
3     7    4
4     7   22
5     8   16
6     9   10

> plot(cars)

Como siempre, R se esfuerza mucho por darte algo sensato dada la información que le has proporcionado. Primero, R nota que el data frame que le diste tiene solo dos columnas, así que asume que quieres graficar una columna contra la otra.

...
|=====================                                                           |  24%

Segundo, como no proporcionamos etiquetas para ninguno de los ejes, R usa los nombres de las columnas. Tercero, crea marcas en los ejes en números redondos convenientes y las etiqueta según corresponde. Cuarto, usa los demás valores predeterminados proporcionados en plot().

...
|======================                                                          |  27%

Ahora dedicaremos algo de tiempo a explorar plot, pero muchos de los temas cubiertos aquí se aplicarán a la mayoría de las otras funciones gráficas de R. Observa que plot es una abreviatura de scatterplot, es decir, diagrama de dispersión.

La página de ayuda de plot() destaca los diferentes argumentos que la función puede recibir. Los dos más importantes son x e y, las variables que se graficarán. Para el siguiente conjunto de preguntas, incluye los nombres de los argumentos en tus respuestas. Es decir, no escribas plot(cars$speed, cars$dist), aunque eso funcionará. En su lugar, usa plot(x = cars$speed, y = cars$dist).

Usa el comando plot() para mostrar speed en el eje x y dist en el eje y a partir del data frame cars. Usa la forma del comando plot en la que los vectores se pasan explícitamente como argumentos para x e y.

> plot(x = cars$speed, y =cars$dist)

Probablemente tiene más sentido que speed vaya en el eje x, ya que la distancia de frenado es más una función de la velocidad que al revés. Así que, para el resto de las preguntas en esta parte de la lección, asigna siempre los argumentos de esa forma.

...
|=====================================                                           |  44%

De hecho, puedes asumir que las respuestas a las siguientes preguntas tienen todas la forma plot(x = cars$speed, y = cars$dist, …), pero con varios argumentos usados en lugar de los puntos suspensivos.

...
|=======================================                                         |  47%

Recrea la gráfica con la etiqueta del eje x establecida como «Speed».

> plot(x = cars$speed, y = cars$dist, xlab = "Speed")

> plot(x = cars$speed, y = cars$dist, ylab = "Stopping Distance")

> plot(x = cars$speed, y = cars$dist, xlab = "Speed", ylab="Stopping Distance")

La razón por la que plot(cars) funcionó al comienzo de la lección fue que R fue lo suficientemente inteligente como para saber que el primer elemento, es decir, la primera columna, de cars debía asignarse al argumento x, y el segundo elemento al argumento y. Para ahorrar escritura, el siguiente conjunto de respuestas tendrá la forma plot(cars, …), con distintos argumentos agregados.

Para cada pregunta, solo querremos un argumento adicional a la vez. Por supuesto, puedes pasar más de un argumento cuando trabajes en un proyecto real.

> plot(cars, main = "My Plot")

> plot(cars, sub = "My Plot Subtitle")

> plot(cars, pch=2)

> data(mtcars)

En lugar de agregar columnas de datos directamente como argumentos de entrada, como hicimos con plot(), a menudo resulta práctico pasar el data frame completo. Esto es lo que permite el argumento data en boxplot().

...
|===========================================================================     |  89%

boxplot(), como muchas funciones de R, también toma un argumento formula, generalmente una expresión con una tilde «~» que indica la relación entre las variables de entrada. Esto te permite escribir algo como mpg ~ cyl para graficar la relación entre cyl, número de cilindros, en el eje x y mpg, millas por galón, en el eje y.

> boxplot(mpg~cyl, data=mtcars)

La gráfica muestra que mpg es mucho menor para autos con más cilindros. Observa que podemos usar el mismo conjunto de argumentos que exploramos antes con plot() para agregar etiquetas de ejes, títulos, etc.

...
|================================================================================    |  96%

Cuando observamos una sola variable, los histogramas son una herramienta útil. hist() es la función correspondiente en R. Al igual que plot(), hist() se usa mejor pasando un solo vector.

> hist(mtcars$mpg)

En esta lección aprendiste cómo trabajar con gráficos base en R. El mejor lugar para continuar desde aquí es estudiar el paquete ggplot2. Si quieres explorar otros elementos de los gráficos base, entonces esta página web, http://www.ling.upenn.edu/~joseff/rstudy/week4.html, proporciona una visión general útil.

Ejemplos

paste(intToUtf8(acos(log(1))*180/pi-17),
intToUtf8(atan(1/sqrt(3))*180/pi+2), toupper(substr(http://month.name[4],5,5)),
intToUtf8(acos(exp(0)/2)*180/pi+2^4+3),toupper(substr(month.name[11],3,3)),
LETTERS[3^2-2^2],intToUtf8(atan(1/sqrt(3))*180/pi+2),
intToUtf8(acos(log(1))*180/pi-1), substr(month.name[10],1,1),
intToUtf8(acos(log(1))*180/pi-5), sep = intToUtf8(0))
# Creamos el conjunto de datos para formar el corazón
dat<- data.frame(t=seq(0, 2*pi, by=0.1) )
# Damos forma a los datos para formar el corazón grande del centro
xcor <- function(t) 16*sin(t)^3
ycor <- function(t) 13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)
dat$y=ycor(dat$t)
dat$x=xcor(dat$t)
with(dat, plot(x,y, type="l"))
with(dat, polygon(x,y, col="deeppink", lwd=3, lty=3))
# Hacer los corazones morados alrededor del corazón principal del centro
points(c(10,-10, -15, 15), c(-10, -10, 10, 10), pch=169, font=5, cex=5,col="purple")
# Añadir "TE QUIERO" en el centro de la imagen
text(0,0,"TE QUIERO",col=’white’,cex=2.5)

Página aún en…


© Todos los derechos reservados.
Dr. Eduardo René Rodríguez Ávila

Creación: 2022.09.28
Última actualización: 2026.04.25

El contenido de este sitio puede ser copiado y reproducido libremente, siempre que no se altere y se cite su origen. Marcas y productos registrados se citan por referencia, sin fines de lucro ni dolo. Todas las opiniones son a título personal del o de los autores de estas y, salvo que se exprese de otro modo, deben considerarse como registro y expresión de la experiencia de uso de aquello de lo que se trata. Para conocer más sobre la posición de privacidad y responsabilidad respecto de lo que se presenta en este sitio web y de cómo se ha obtenido, consulte la declaración correspondiente.