Introducción a Programación en R




Este material es una actualización y traducción de los materiales elaborados por los estudiantes del Departamento de Genética de ESALQ/USP - Brasil. Acceda al contenido en portugués impartido en otros eventos en este sitio.

Sugerimos que, antes de comenzar la práctica descrita aquí, siga este tutorial para la instalación de R y RStudio.

Familiarización con la interfaz de RStudio

Al abrir RStudio verás:

La interfaz está dividida en cuatro ventanas con funciones principales:

  • Edición de código
  • Entorno de trabajo e historial
  • Consola
  • Archivos, gráficos, paquetes y ayuda

Explora cada una de las ventanas. Hay numerosas funcionalidades para cada una de ellas, veremos algunas durante el curso.

Un primer script

La ventana de edición de código (probablemente ubicada en la esquina superior izquierda) se utilizará para escribir tu código. Abre un nuevo script haciendo clic en el + en la esquina superior izquierda y seleccionando R script.

Vamos a comenzar con el tradicional Hello World. Escribe en tu script:

cat("Hello world")
## Hello world

Ahora, selecciona la línea y presiona el botón Run o utiliza Ctrl + enter.

Al hacer esto, tu código será procesado en la ventana Console, donde aparecerá en azul (si tienes los colores predeterminados de R) el código escrito y, a continuación, el resultado deseado. La línea no será procesada en la consola si tiene el símbolo # delante. Ahora, prueba a poner # delante del código escrito. Nuevamente, selecciona la línea y presiona Run.

# cat("Hello world")

El símbolo # se utiliza para comentarios en el código. Esta es una excelente práctica de organización y ayuda a recordar, posteriormente, lo que estabas pensando cuando escribiste el código. También es esencial para que otras personas puedan entenderlo. Como en el ejemplo:

# Iniciando los trabajos en R
cat("Hello world")
## Hello world

Importante: siempre que quieras realizar alguna modificación, edita tu script y no directamente en la consola, ¡porque todo lo que se escribe en ella no se podrá guardar!

Para guardar tu script, puedes utilizar la pestaña Files ubicada (por defecto) en la esquina inferior derecha. Puedes buscar una ubicación de tu preferencia, crear una nueva carpeta con el nombre CursoR.

Consejo:

  • Evita poner espacios y puntuación en el nombre de las carpetas y archivos, esto puede dificultar el acceso a través de la línea de comandos en R. Por ejemplo, en lugar de Curso R, optamos por CursoR.

Después, simplemente haz clic en el disquete ubicado en el encabezado de RStudio o usa Ctrl + s y selecciona el directorio CursoR creado. Los scripts en R se guardan con la extensión .R.

Estableciendo el directorio de trabajo

Otra buena práctica en R es mantener el script en el mismo directorio donde están tus datos brutos (archivos de entrada en el script) y los datos procesados (gráficos, tablas, etc.). Para esto, vamos a hacer que R identifique el mismo directorio donde guardaste el script como directorio de trabajo. Así, entenderá que es de allí de donde se obtendrán los datos y es allí donde también irán los resultados.

Puedes hacer esto utilizando las facilidades de RStudio, simplemente localiza el directorio CursoR en la pestaña Files, haz clic en More y luego en “Set as Working Directory”. Observa que aparecerá en la consola algo como:

setwd("~/Documents/CursoR")

Es decir, puedes utilizar este mismo comando para realizar esta acción. El resultado será nuestra carpeta de trabajo. Cuando estés perdido/a o para asegurarte de que el directorio de trabajo fue cambiado, utiliza:

getwd()

Facilitando la vida con Tab

Ahora, imagina que tienes un directorio como ~/Documentos/maestria/semestre1/asignatura_tal/clase_tal/datos_28174/analisis_276182/resultados_161/. No es fácil recordar todo este camino para escribir en un comando setwd().

Además de la facilidad de la ventana de RStudio, también puedes utilizar la tecla Tab para completar el camino por ti. Prueba buscando alguna carpeta en tu computadora. ¡Solo tienes que empezar a escribir el camino y presionar Tab, y completará el nombre por ti! Si tienes más de un archivo con ese inicio de nombre, presiona Tab dos veces y te mostrará todas las opciones.

El Tab funciona no solo para indicar rutas, sino también para comandos y nombres de objetos. Es muy común cometer errores de escritura en el código. Utilizar el Tab reducirá significativamente estos errores.

El Tab puede ser aún más poderoso si tienes acceso a la herramienta GitHub Copilot. Con ella, puedes utilizar el Tab para completar el código que estás escribiendo. Es una herramienta basada en inteligencia artificial que sugiere el código que estás escribiendo. Es una herramienta de pago, pero puedes utilizarla gratuitamente durante 60 días.

Operaciones básicas

¡Vamos entonces al lenguaje!

R puede funcionar como una simple calculadora, que utiliza la misma sintaxis que otros programas (como Excel):

1+1.3                 #Decimal definido con "."
2*3
2^3
4/2

sqrt(4)              #raíz cuadrada
log(100, base = 10)  #Logaritmo en base 10
log(100)             #Logaritmo en base neperiana

Ahora, utiliza las operaciones básicas para resolver la expresión siguiente. Recuerda utilizar paréntesis () para establecer prioridades en las operaciones.

\((\frac{13+2+1.5}{3})+ log_{4}96\)

Resultado esperado:

## [1] 8.792481

Ten en cuenta que, si colocas los paréntesis de forma incorrecta, el código no resultará en ningún mensaje de error, pues este es un error que llamamos error lógico o error silencioso, es decir, el código se ejecuta pero no hace lo que quieres que haga. Este es el tipo de error más peligroso y difícil de corregir. Ve un ejemplo:

13+2+1.5/3 + log(96, base = 4)
## [1] 18.79248

Los errores que producen un mensaje, ya sea una advertencia (warning) o un error (error) se llaman errores de sintaxis. En estos casos, R devolverá un mensaje para ayudarte a corregirlos. Los warnings no comprometen el funcionamiento del código, pero llaman la atención sobre algún punto; los errors, por otro lado, necesitan ser corregidos necesariamente para que el código funcione.

Ejemplo de error:

((13+2+1,5)/3) + log(96, base = 4)

También puedes olvidar cerrar algún paréntesis, comillas, corchetes o llaves, en estos casos, R esperará el comando para cerrar el bloque de código señalizando con un +:

((13+2+1.5)/3 + log(96, base = 4)

Si esto sucede, ve a la consola y presiona ESC, y el bloque se finalizará para que puedas corregirlo.

Los comandos log y sqrt son dos de muchas otras funciones básicas que R posee. Las funciones son conjuntos de instrucciones organizadas para realizar una tarea. Para todas ellas, R tiene una descripción para ayudar en su uso. Para acceder a esta ayuda usa:

?log

Y se abrirá la descripción de la función en la ventana Help de RStudio.

Si la descripción del propio R no es suficiente para que entiendas cómo funciona la función, busca en Google (preferiblemente en inglés). Existen diversos sitios y foros con información didáctica sobre las funciones de R.

Operaciones con vectores

Los vectores son las estructuras más simples trabajadas en R. Construimos un vector con una secuencia numérica usando:

c(1,3,2,5,2)
## [1] 1 3 2 5 2

MUCHA ATENCIÓN: La c es la función de R (Combine Values into a Vector or List) con la cual construimos un vector!

Utilizamos el símbolo : para crear secuencias de números enteros, como:

1:10
##  [1]  1  2  3  4  5  6  7  8  9 10

Podemos utilizar otras funciones para generar secuencias, como:

seq(from=0, to=100, by=5)
##  [1]   0   5  10  15  20  25  30  35  40  45  50  55  60  65  70  75  80  85  90
## [20]  95 100
# o
seq(0,100,5) # Si ya conoces el orden de los argumentos de la función
##  [1]   0   5  10  15  20  25  30  35  40  45  50  55  60  65  70  75  80  85  90
## [20]  95 100
  • Crea una secuencia utilizando la función seq que varíe de 4 a 30, con intervalos de 3 en 3.
## [1]  4  7 10 13 16 19 22 25 28

La función rep genera secuencias con números repetidos:

rep(3:5, 2)
## [1] 3 4 5 3 4 5

Podemos realizar operaciones utilizando estos vectores:

c(1,4,3,2)*2
c(4,2,1,5)+c(5,2,6,1)
c(4,2,1,5)*c(5,2,6,1)

Observa que ya está resultando cansado escribir los mismos números repetidamente, vamos a resolver esto creando objetos para almacenar nuestros vectores y mucho más.

Creando objetos

El almacenamiento de información en objetos y su posible manipulación hace de R un lenguaje orientado a objetos. Para crear un objeto basta con asignar valores a las variables, como sigue:

x = c(30.1,30.4,40,30.2,30.6,40.1)
# o
x <- c(30.1,30.4,40,30.2,30.6,40.1)

y = c(0.26,0.3,0.36,0.24,0.27,0.35)

Los usuarios más antiguos suelen usar el signo <-, pero tiene la misma función que =. Hay quienes prefieren usar <- para asignación en objetos y = solo para definir los argumentos dentro de funciones. Organízate de la forma que prefieras.

Para acceder a los valores dentro del objeto simplemente:

x
## [1] 30.1 30.4 40.0 30.2 30.6 40.1

El lenguaje es sensible a mayúsculas y minúsculas. Por lo tanto, x es diferente de X:

X

El objeto X no fue creado.

El nombre de los objetos es una elección personal, la sugerencia es tratar de mantener un patrón para mejor organización. Aquí hay algunos consejos:

  • Usar nombres descriptivos
  • Evitar empezar con números
  • No usar espacios (usar _ o camelCase)
  • No usar caracteres especiales
  • Mantener consistencia en el patrón elegido
  • Evitar nombres muy largos
  • No usar acentos o caracteres no ASCII

Algunos nombres no pueden usarse por establecer roles fijos en R, son:

  • TRUE - Verdadero, valor lógico
  • FALSE - Falso, valor lógico
  • if, else, for, while, break, next - Palabras reservadas para estructuras condicionales y de repetición
  • for, while, repeat - Palabras reservadas para estructuras de repetición
  • function - Palabra reservada para definición de funciones
  • in, NA, NaN, NULL - Palabras reservadas para valores especiales
  • NA_integer_, NA_real, NA_character_, NA_complex_ - Valores especiales para representar datos ausentes

Podemos entonces realizar operaciones con el objeto creado:

x+2
## [1] 32.1 32.4 42.0 32.2 32.6 42.1
x*2
## [1] 60.2 60.8 80.0 60.4 61.2 80.2

Para realizar la operación, R alinea los dos vectores y realiza la operación elemento a elemento. Observa:

x + y
## [1] 30.36 30.70 40.36 30.44 30.87 40.45
x*y
## [1]  7.826  9.120 14.400  7.248  8.262 14.035

Si los vectores tienen tamaños diferentes, repetirá el menor para realizar la operación elemento a elemento con todos los del mayor.

x*2
x*c(1,2)

Si el vector menor no es múltiplo del mayor, obtendremos una advertencia:

x*c(1,2,3,4)
## Warning in x * c(1, 2, 3, 4): longer object length is not a multiple of shorter
## object length
## [1]  30.1  60.8 120.0 120.8  30.6  80.2

Nota que el warning no compromete el funcionamiento del código, solo da una pista de que algo puede no estar como quisieras.

También podemos almacenar la operación en otro objeto:

z <- (x+y)/2
z

También podemos aplicar algunas funciones, por ejemplo:

sum(z)  # suma de los valores de z
## [1] 101.59
mean(z) # media
## [1] 16.93167
var(z)  # varianza
## [1] 6.427507

Indexación

Accedemos solo al 3er valor del vector creado con []:

z[3]

También podemos acceder a los números de la posición 2 a 4 con:

z[2:4]
## [1] 15.35 20.18 15.22

Para obtener información del vector creado utiliza:

str(z)
##  num [1:6] 15.2 15.3 20.2 15.2 15.4 ...

La función str nos dice sobre la estructura del vector, que se trata de un vector numérico con 6 elementos.

Los vectores también pueden recibir otras categorías como caracteres:

clone <- c("GRA02", "URO01", "URO03", "GRA02", "GRA01", "URO01")

Otra clase son los factores, estos pueden ser un poco complejos de manejar.

En general, los factores son valores categorizados por levels, como ejemplo, si transformamos nuestro vector de caracteres clone en factor, se asignarán niveles para cada una de las palabras:

clone_factor <- as.factor(clone)
str(clone_factor)
##  Factor w/ 4 levels "GRA01","GRA02",..: 2 3 4 2 1 3
levels(clone_factor)
## [1] "GRA01" "GRA02" "URO01" "URO03"

De esta forma, tendremos solo 4 niveles para un vector con 6 elementos, ya que las palabras “GRA02” y “URO01” se repiten. Podemos obtener el número de elementos del vector o su longitud con:

length(clone_factor)
## [1] 6

También hay vectores lógicos, que reciben valores de verdadero o falso:

logico <- x > 40
logico   # ¿Los elementos son mayores que 40?
## [1] FALSE FALSE FALSE FALSE FALSE  TRUE

Con él podemos, por ejemplo, identificar cuáles son las posiciones de los elementos mayores que 40:

which(logico)  # Obteniendo las posiciones de los elementos TRUE
## [1] 6
x[which(logico)] # Obteniendo los números mayores que 40 del vector x por posición
## [1] 40.1
# o
x[which(x > 40)]
## [1] 40.1

También podemos localizar elementos específicos con:

clone %in% c("URO03", "GRA02")
## [1]  TRUE FALSE  TRUE  TRUE FALSE FALSE

También pueden ser útiles las funciones any y all. Investiga sobre ellas.

Encuentra más sobre otros operadores lógicos, como el > utilizado, en este enlace.

[Continúa con la siguiente parte del texto para los Warnings y consejos…]

Advertencia 1

Haz una secuencia numérica, conteniendo 10 valores enteros, y guárdala en un objeto llamado “a”.

(a <- 1:10)
##  [1]  1  2  3  4  5  6  7  8  9 10

Crea otra secuencia, utilizando números decimales y cualquier operación matemática, de tal forma que sus valores sean idénticos al objeto “a”.

b <- seq(from = 0.1, to = 1, 0.1)
(b <- b*10)
##  [1]  1  2  3  4  5  6  7  8  9 10

Los dos vectores parecen iguales, ¿no?

Entonces, utilizando un operador lógico, vamos a verificar si el objeto “b” es igual al objeto “a”.

a==b
##  [1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE

Algunos valores no son iguales. ¿Cómo es esto posible?

a==round(b)
##  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

Advertencia 2

No es posible mezclar diferentes clases dentro de un mismo vector. Al intentar hacer esto, observa que R intentará igualar a una única clase:

errado <- c(TRUE, "vish", 1)
errado
## [1] "TRUE" "vish" "1"

En este caso, todos los elementos fueron transformados en caracteres.

Algunos Consejos:

  • Ten cuidado con la prioridad de las operaciones, en caso de duda, siempre añade paréntesis según tu interés de prioridad.
  • Recuerda que, si olvidas cerrar algún ( o [ o ", la consola de R esperará que lo cierres indicando un +. Nada será procesado hasta que escribas directamente en la consola un ) o presiones ESC.
  • Ten cuidado de no sobreponer objetos ya creados creando otros con el mismo nombre. Usa, por ejemplo: altura1, altura2.
  • Mantén en tu script .R solo los comandos que funcionaron y, preferentemente, añade comentarios. Puedes, por ejemplo, comentar dificultades encontradas, para que no cometas los mismos errores más tarde.

Si estás adelantado/a respecto a tus compañeros, ya puedes hacer los ejercicios de la Sesión 1, si no, hazlos en otro momento y envíanos dudas.

Matrices

Las matrices son otra clase de objetos muy utilizados en R, con ellas podemos realizar operaciones de mayor escala de forma automatizada.

Por ser usadas en operaciones, normalmente almacenamos en ellas elementos numéricos. Para crear una matriz, determinamos una secuencia de números e indicamos el número de filas y columnas de la matriz:

X <- matrix(1:12, nrow = 6, ncol = 2)
X
##      [,1] [,2]
## [1,]    1    7
## [2,]    2    8
## [3,]    3    9
## [4,]    4   10
## [5,]    5   11
## [6,]    6   12

También podemos utilizar secuencias ya almacenadas en vectores para generar una matriz, siempre que sean numéricos:

W <- matrix(c(x,y), nrow = 6, ncol =2)
W
##      [,1] [,2]
## [1,] 30.1 0.26
## [2,] 30.4 0.30
## [3,] 40.0 0.36
## [4,] 30.2 0.24
## [5,] 30.6 0.27
## [6,] 40.1 0.35

Con ellas podemos realizar operaciones matriciales:

X*2
##      [,1] [,2]
## [1,]    2   14
## [2,]    4   16
## [3,]    6   18
## [4,]    8   20
## [5,]   10   22
## [6,]   12   24
X*X        
##      [,1] [,2]
## [1,]    1   49
## [2,]    4   64
## [3,]    9   81
## [4,]   16  100
## [5,]   25  121
## [6,]   36  144
X%*%t(X)          # Multiplicación matricial
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]   50   58   66   74   82   90
## [2,]   58   68   78   88   98  108
## [3,]   66   78   90  102  114  126
## [4,]   74   88  102  116  130  144
## [5,]   82   98  114  130  146  162
## [6,]   90  108  126  144  162  180

Utilizar estas operaciones exige conocimiento de álgebra de matrices, si quieres profundizar al respecto, el libro Linear Models in Statistics, Rencher (2008) tiene una buena revisión al respecto. También puedes explorar la sintaxis de R para estas operaciones en este enlace.

Accedemos a los números internos de la matriz dando las coordenadas [fila,columna], como en el ejemplo:

W[4,2] # Número posicionado en la fila 4 y columna 2
## [1] 0.24

A veces puede ser informativo dar nombres a las columnas y a las filas de la matriz, lo hacemos con:

colnames(W) <- c("altura", "diametro")
rownames(W) <- clone
W
##       altura diametro
## GRA02   30.1     0.26
## URO01   30.4     0.30
## URO03   40.0     0.36
## GRA02   30.2     0.24
## GRA01   30.6     0.27
## URO01   40.1     0.35

Estas funciones colnames y rownames también funcionan en los data.frames.

Data.frames

A diferencia de las matrices, no realizamos operaciones con los data.frames, pero permiten la unión de vectores con clases diferentes. Los data frames son similares a tablas generadas en otros programas, como Excel.

Los data frames son combinaciones de vectores de la misma longitud. Todos los que creamos hasta ahora tienen tamaño 6, verifica.

Podemos así combinarlos en columnas de un único data.frame:

campo1 <- data.frame("clone" = clone,     # Antes del signo "="  
                     "altura" = x,        # establecemos los nombres  
                     "diametro" = y,      # de las columnas
                     "edad" = rep(3:5, 2),
                     "corte"= logico) 
campo1
##   clone altura diametro edad corte
## 1 GRA02   30.1     0.26    3 FALSE
## 2 URO01   30.4     0.30    4 FALSE
## 3 URO03   40.0     0.36    5 FALSE
## 4 GRA02   30.2     0.24    3 FALSE
## 5 GRA01   30.6     0.27    4 FALSE
## 6 URO01   40.1     0.35    5  TRUE

Podemos acceder a cada una de las columnas con:

campo1$edad
## [1] 3 4 5 3 4 5

O también con:

campo1[,4] 
## [1] 3 4 5 3 4 5

Aquí, el número dentro de los corchetes se refiere a la columna, por ser el segundo elemento (separado por coma). El primer elemento se refiere a la fila. Como dejamos el primer elemento vacío, nos estaremos refiriendo a todas las filas para esa columna.

De esta forma, si queremos obtener un contenido específico podemos dar las coordenadas con [fila,columna]:

campo1[1,2] 
## [1] 30.1
  • Obtén el diámetro del clon “URO03”.
## [1] 0.36

Aunque se trate de un data frame, podemos realizar operaciones con los vectores numéricos que lo componen.

  • Con el diámetro y la altura de los árboles, calcula el volumen conforme a la fórmula siguiente y almacénalo en un objeto volumen:

\(3.14*(diametro/2)^2*altura\)

## [1] 1.597287 2.147760 4.069440 1.365523 1.751131 3.856116

Ahora, vamos a añadir el vector calculado con el volumen a nuestro data frame. Para esto usa la función cbind.

campo1 <- cbind(campo1, volumen)
campo1
##   clone altura diametro edad corte  volumen
## 1 GRA02   30.1     0.26    3 FALSE 1.597287
## 2 URO01   30.4     0.30    4 FALSE 2.147760
## 3 URO03   40.0     0.36    5 FALSE 4.069440
## 4 GRA02   30.2     0.24    3 FALSE 1.365523
## 5 GRA01   30.6     0.27    4 FALSE 1.751131
## 6 URO01   40.1     0.35    5  TRUE 3.856116
str(campo1)
## 'data.frame':    6 obs. of  6 variables:
##  $ clone   : chr  "GRA02" "URO01" "URO03" "GRA02" ...
##  $ altura  : num  30.1 30.4 40 30.2 30.6 40.1
##  $ diametro: num  0.26 0.3 0.36 0.24 0.27 0.35
##  $ edad    : int  3 4 5 3 4 5
##  $ corte   : logi  FALSE FALSE FALSE FALSE FALSE TRUE
##  $ volumen : num  1.6 2.15 4.07 1.37 1.75 ...

Algunos consejos:

  • Recuerda que, para construir matrices y data frames, las columnas deben presentar el mismo número de elementos.

  • En caso de que no sepas el operador o la función que debe ser utilizada, busca en Google o pregunta al chatgpt o cualquier otra herramienta. Por ejemplo, si tienes dudas sobre cómo calcular la desviación estándar, busca por “desviación estándar R” o, mejor aún, “standard deviation R”. La comunidad de R es bastante activa y gran parte de tus preguntas sobre él ya fueron respondidas en algún lugar de la web.

  • No olvides que todo lo que hagas en R necesita ser explícitamente indicado, como una multiplicación 4ac con 4*a*c. Para generar un vector 1,3,2,6 es necesario: c(1,3,2,6).

Listas

Las listas consisten en una colección de objetos, no necesariamente de la misma clase. En ellas podemos almacenar todos los otros objetos ya vistos y recuperarlos mediante la indexación con [[. Como ejemplo, vamos a utilizar algunos objetos que ya fueron generados.

mi_lista <- list(campo1 = campo1, media_alt = tapply(campo1$altura, campo1$edad, mean), matrix_ex = W)
str(mi_lista)
## List of 3
##  $ campo1   :'data.frame':   6 obs. of  6 variables:
##   ..$ clone   : chr [1:6] "GRA02" "URO01" "URO03" "GRA02" ...
##   ..$ altura  : num [1:6] 30.1 30.4 40 30.2 30.6 40.1
##   ..$ diametro: num [1:6] 0.26 0.3 0.36 0.24 0.27 0.35
##   ..$ edad    : int [1:6] 3 4 5 3 4 5
##   ..$ corte   : logi [1:6] FALSE FALSE FALSE FALSE FALSE TRUE
##   ..$ volumen : num [1:6] 1.6 2.15 4.07 1.37 1.75 ...
##  $ media_alt: num [1:3(1d)] 30.1 30.5 40
##   ..- attr(*, "dimnames")=List of 1
##   .. ..$ : chr [1:3] "3" "4" "5"
##  $ matrix_ex: num [1:6, 1:2] 30.1 30.4 40 30.2 30.6 40.1 0.26 0.3 0.36 0.24 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:6] "GRA02" "URO01" "URO03" "GRA02" ...
##   .. ..$ : chr [1:2] "altura" "diametro"

Quiero acceder al data.frame campo1

mi_lista[[1]] 
##   clone altura diametro edad corte  volumen
## 1 GRA02   30.1     0.26    3 FALSE 1.597287
## 2 URO01   30.4     0.30    4 FALSE 2.147760
## 3 URO03   40.0     0.36    5 FALSE 4.069440
## 4 GRA02   30.2     0.24    3 FALSE 1.365523
## 5 GRA01   30.6     0.27    4 FALSE 1.751131
## 6 URO01   40.1     0.35    5  TRUE 3.856116
# o
mi_lista$campo1
##   clone altura diametro edad corte  volumen
## 1 GRA02   30.1     0.26    3 FALSE 1.597287
## 2 URO01   30.4     0.30    4 FALSE 2.147760
## 3 URO03   40.0     0.36    5 FALSE 4.069440
## 4 GRA02   30.2     0.24    3 FALSE 1.365523
## 5 GRA01   30.6     0.27    4 FALSE 1.751131
## 6 URO01   40.1     0.35    5  TRUE 3.856116

Para acceder a una columna específica en el data.frame campo1, que está dentro de mi_lista:

mi_lista[[1]][[3]]
## [1] 0.26 0.30 0.36 0.24 0.27 0.35
# o
mi_lista[[1]]$diametro
## [1] 0.26 0.30 0.36 0.24 0.27 0.35
# o
mi_lista$campo1$diametro
## [1] 0.26 0.30 0.36 0.24 0.27 0.35

Las listas son muy útiles, por ejemplo, cuando vamos a utilizar/generar diversos objetos dentro de un bucle.

Arrays

Este es un tipo de objeto que probablemente no utilizarás ahora al principio, pero es bueno saber de su existencia. Se utilizan para almacenar datos con más de dos dimensiones. Por ejemplo, si creamos un array:

(mi_array <- array(1:24, dim = c(2,3,4)))
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]    7    9   11
## [2,]    8   10   12
## 
## , , 3
## 
##      [,1] [,2] [,3]
## [1,]   13   15   17
## [2,]   14   16   18
## 
## , , 4
## 
##      [,1] [,2] [,3]
## [1,]   19   21   23
## [2,]   20   22   24

Tendremos cuatro matrices con dos filas y tres columnas y los números del 1 al 24 estarán distribuidos en ellas por columnas.

Si estás adelantado/a respecto a tus compañeros, ya puedes hacer los ejercicios de la Sesión 2, si no, hazlos en otro momento y envíanos dudas por el foro.

Importando y exportando datos

Descarga los objetos creados hasta ahora haciendo clic aquí

Los archivos RData son exclusivos de R. Al hacer doble clic sobre el archivo o utilizando el siguiente comando:

load("data/dia_1.RData")

Recuperarás todos los objetos generados hasta este punto del tutorial.

Si tienes acceso a internet, también puedes usar el enlace web directamente:

load(url("https://breeding-insight.github.io/learn-hub/r-intro/data/dia_1.RData"))

Para generar este archivo RData, ejecuté todos los códigos desde el inicio hasta aquí y utilicé el siguiente comando:

save.image(file = "data/dia_1.RData")

Este comando guarda todo lo que tienes en tu Ambiente Global (todos los objetos que aparecen en la esquina superior derecha). También puedes guardar únicamente un objeto específico utilizando:

save(campo1, file = "data/campo1.RData")

Si eliminamos este objeto de nuestro Ambiente Global con el siguiente comando:

rm(campo1)  # Asegúrate de haber guardado el archivo RData antes de eliminarlo

Podremos recuperarlo fácilmente más tarde con:

load("data/campo1.RData")

El formato RData es exclusivo de R. Es útil para hacer lo que estamos haciendo: pausar el análisis un día y continuar otro día sin necesidad de ejecutar todo nuevamente. Sin embargo, muchas veces necesitamos exportar nuestros datos a otros programas que requieren otros formatos, como .txt o .csv. Para esto podemos usar los siguientes comandos:

write.table(campo1, file = "campo1.txt", sep = ";", dec = ".", row.names = FALSE)
write.csv(campo1, file = "campo1.csv", row.names = TRUE)

Nota: Puedes utilizar paquetes para importar y exportar datos en otros formatos. Por ejemplo, el paquete openxlsx permite trabajar con archivos en formato Excel, y el paquete vroom puede ser utilizado para manejar tablas grandes y comprimidas. Para instalar paquetes, utiliza la función install.packages("nombre_del_paquete") y para cargar un paquete utiliza library(nombre_del_paquete). Veremos más sobre paquetes en la sesión “Aplicación de Paquetes” de este curso.

# install.packages("openxlsx")
library(openxlsx)

write.xlsx(campo1, file = "campo1.xlsx")

Al exportar, hay diversas opciones para la formato del archivo, es importante considerarlas si el archivo será utilizado en otro programa posteriormente.

Abre los archivos generados en tu bloc de notas para visualizar su formato. Observa que el archivo .txt fue guardado con el separador ; y el separador decimal .. Por otro lado, el archivo .csv fue guardado con el separador de coma , y el separador decimal ..

Estos archivos pueden ser leídos nuevamente por R utilizando las funciones y sus especificaciones:

campo1_txt <- read.table(file = "campo1.txt", sep=";", dec=".", header = TRUE)
campo1_csv <- read.csv(file = "campo1.csv")
campo1_xlsx <- read.xlsx("campo1.xlsx", sheet = 1)
head(campo1_txt)
head(campo1_csv)
head(campo1_xlsx)

Ahora que hemos aprendido a importar datos, vamos a trabajar con el conjunto generado a partir del formulario enviado a ustedes y a estudiantes de otras ediciones de este curso.

La hoja de datos está disponible en el enlace a continuación, añádela a tu directorio de trabajo o especifica la ruta de la carpeta al importarla en R, como se muestra a continuación.

También podemos importar los datos directamente desde Github especificando la dirección web.

dados <- read.csv("https://breeding-insight.github.io/learn-hub/r-intro/data/dados_2025.csv")

Aquí utilizaremos también el argumento na.strings, que indicará cómo fueron representados los datos faltantes en el archivo.

dados <- read.csv(file = "data/dados_2025.csv")
head(dados)

Si el decimal de su tabla es una coma en lugar de un punto (predeterminado de la función), especifíquelo usando dec=",".

load("data/dados_2025.RData")

Vamos a explorar la estructura de los datos recopilados:

str(dados)
## 'data.frame':    124 obs. of  8 variables:
##  $ Timestamp                                                                                   : chr  "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" ...
##  $ Affiliation                                                                                 : chr  "Rcourse2021" "Rcourse2021" "Rcourse2021" "Rcourse2021" ...
##  $ Using.Google.Maps..please.provide.the.longitude.of.the.city.country.of.your.birth.          : chr  "-54.5724" "-47.6476" "-48.0547" "-106.6563" ...
##  $ Using.Google.Maps..please.provide.the.latitude.of.the.city.country.of.your.birth.           : chr  "-25.5263" "-22.725" "-15.911" "52.1418" ...
##  $ Background.Field..e.g..Molecular.Biology..Animal.Breeding..Genetics..etc..                  : chr  "Agronomia" "Agronomia" "Biotecnologia" "Licenciatura em Ciências Biológicas" ...
##  $ Current.professional.affiliation                                                            : chr  "PhD" "PhD" "Masters degree" "Other" ...
##  $ If.you.chose.other.in.the..current.professional.affiliation..question.above..please.specify.: chr  "" "" "" "" ...
##  $ Level.of.R.knowledge                                                                        : chr  "Intermediate" "Intermediate" "Beginner (some knowledge)" "Intermediate" ...
# también
dim(dados)
## [1] 124   8

Observa que los nombres de las columnas aún corresponden a las preguntas completas del formulario. Vamos a cambiarlos por nombres más fáciles de trabajar:

colnames(dados)
## [1] "Timestamp"                                                                                   
## [2] "Affiliation"                                                                                 
## [3] "Using.Google.Maps..please.provide.the.longitude.of.the.city.country.of.your.birth."          
## [4] "Using.Google.Maps..please.provide.the.latitude.of.the.city.country.of.your.birth."           
## [5] "Background.Field..e.g..Molecular.Biology..Animal.Breeding..Genetics..etc.."                  
## [6] "Current.professional.affiliation"                                                            
## [7] "If.you.chose.other.in.the..current.professional.affiliation..question.above..please.specify."
## [8] "Level.of.R.knowledge"
colnames(dados) = c("Fecha", "Afiliacion", "Longitud", "Latitud", "Antecedentes", "Ocupacion_Actual", "Explicacion", "Conocimiento_R")
colnames(dados)
## [1] "Fecha"            "Afiliacion"       "Longitud"         "Latitud"         
## [5] "Antecedentes"     "Ocupacion_Actual" "Explicacion"      "Conocimiento_R"
str(dados)
## 'data.frame':    124 obs. of  8 variables:
##  $ Fecha           : chr  "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" ...
##  $ Afiliacion      : chr  "Rcourse2021" "Rcourse2021" "Rcourse2021" "Rcourse2021" ...
##  $ Longitud        : chr  "-54.5724" "-47.6476" "-48.0547" "-106.6563" ...
##  $ Latitud         : chr  "-25.5263" "-22.725" "-15.911" "52.1418" ...
##  $ Antecedentes    : chr  "Agronomia" "Agronomia" "Biotecnologia" "Licenciatura em Ciências Biológicas" ...
##  $ Ocupacion_Actual: chr  "PhD" "PhD" "Masters degree" "Other" ...
##  $ Explicacion     : chr  "" "" "" "" ...
##  $ Conocimiento_R  : chr  "Intermediate" "Intermediate" "Beginner (some knowledge)" "Intermediate" ...

Ahora usaremos los datos que tenemos para aprender diferentes comandos y funciones en el entorno de R.

Primero, vamos verificar cuántos estudiantes respondieron al formulario del curso contando el número de filas. Para esto, utilizamos la función nrow.

nrow(dados)
## [1] 124

A continuación, verificaremos si en nuestro grupo hay personas que comparten la misma formación académica y ocupación.

Esto se puede hacer fácilmente con la función table, que indica la frecuencia de cada observación:

table(dados$Afiliacion)
table(dados$Ocupacion_Actual)

Estructuras condicionales

if y else

Para nuestra próxima actividad con los datos, primero vamos a entender cómo funcionan las estructuras if y else.

En las funciones condicionales if y else, establecemos una condición para if. Si la condición es verdadera, se realizará la actividad, y en caso contrario (else) se ejecutará otra tarea. Veamos un ejemplo:

if(2 > 3){
  cat("dos es mayor que tres")
} else {
  cat("dos no es mayor que tres")
}
## dos no es mayor que tres
  • Actividad: Descubre el Nivel de Conocimientos en R de la segunda persona que respondió (línea 2). Envía un mensaje de “Nivel Intermedio” si es intermedio, o un mensaje de “Nivel Básico o Avanzado” en caso contrario. (pista: el signo == significa “exactamente igual a”).
head(dados)
##                 Fecha  Afiliacion  Longitud  Latitud
## 1 07/04/2025 14:00:00 Rcourse2021  -54.5724 -25.5263
## 2 07/04/2025 14:00:00 Rcourse2021  -47.6476  -22.725
## 3 07/04/2025 14:00:00 Rcourse2021  -48.0547  -15.911
## 4 07/04/2025 14:00:00 Rcourse2021 -106.6563  52.1418
## 5 07/04/2025 14:00:00 Rcourse2021  -47.6604 -22.7641
## 6 07/04/2025 14:00:00 Rcourse2021  -47.6434 -22.7118
##                          Antecedentes Ocupacion_Actual Explicacion
## 1                           Agronomia              PhD            
## 2                           Agronomia              PhD            
## 3                       Biotecnologia   Masters degree            
## 4 Licenciatura em Ciências Biológicas            Other            
## 5                           Agronomia     Profissional            
## 6                           Agronomia              PhD            
##              Conocimiento_R
## 1              Intermediate
## 2              Intermediate
## 3 Beginner (some knowledge)
## 4              Intermediate
## 5 Beginner (some knowledge)
## 6              Intermediate
if(dados$Conocimiento_R[2] == "Intermediate"){
  cat("Nivel Intermedio")
} else {
  cat("Nivel Básico o Avanzado")
}
## Nivel Intermedio

Podemos especificar más de una condición repitiendo la estructura if y else.
Ahora analizaremos la ocupación de las personas que respondieron al cuestionario.

if(dados$Ocupacion_Actual[2] == "Other"){
  print(dados$Explicacion[2])
} else if (dados$Ocupacion_Actual[2] == "PhD"){
  print(paste("¿Tu doctorado es en:", dados$Antecedentes[2], "?"))
} else {
  print(paste("¿Aún trabajas con:", dados$Antecedentes[2], "?"))
}
## [1] "¿Tu doctorado es en: Agronomia ?"

Sin embargo, observa que solo es posible utilizar estas estructuras para un elemento individual de un vector o para todo el vector. Si queremos recorrer los elementos individualmente, necesitaremos utilizar otro recurso.

Estructuras de repetición

For

Este recurso puede ser la función for, una función muy utilizada y poderosa. Constituye una estructura de bucle, ya que aplicará la misma actividad repetidamente hasta alcanzar una determinada condición. Observa los ejemplos:

for(i in 1:10){
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
test <- vector()
for(i in 1:10){
  test[i] <- i + 4 
}
test
##  [1]  5  6  7  8  9 10 11 12 13 14

En los casos anteriores, i funciona como un índice que varía de 1 a 10 en la operación determinada entre llaves.

Con esta estructura, podemos repetir la operación realizada con las estructuras if y else para todo el vector:

for(i in 1:nrow(dados)){
  if(dados$Ocupacion_Actual[i] == "Other"){
    print(dados$Explicacion[i])
  } else if (dados$Ocupacion_Actual[i] == "PhD"){
    print(paste("¿Tu doctorado es en:", dados$Antecedentes[i], "?"))
  } else {
    print(paste("¿Aún trabajas con:", dados$Antecedentes[i], "?"))
  }
}

Observa que algunas personas no respondieron cuando preguntadas sobre lo que querían decir con “Otros”. Para evitar que R devuelva un error en este caso, podemos usar la función is.na para verificar si la respuesta es NA (no disponible).

for(i in 1:nrow(dados)){
  if(dados$Ocupacion_Actual[i] == "Other"){
    if(is.na(dados$Explicacion[i])) {
      print("Esta persona no explicó qué quiso decir con 'Otros'") 
    } else {
      print(dados$Explicacion[i])
    }
  } else if (dados$Ocupacion_Actual[i] == "PhD"){
    print(paste("¿Tu doctorado es en:", dados$Antecedentes[i], "?"))
  } else {
    print(paste("¿Aún trabajas con:", dados$Antecedentes[i], "?"))
  }
}
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Biotecnologia ?"
## [1] ""
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Zootecnia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Engenharia Florestal ?"
## [1] "¿Tu doctorado es en: Bacharel em Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Engenharia Agrícola ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Engenharia Agronômica ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Engenheiro Florestal ?"
## [1] "¿Aún trabajas con: Genética e Melhoramento de Plantas ?"
## [1] ""
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Engenharia Agronômica ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Tu doctorado es en: Engenharia Agronômica ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Biotecnologia ?"
## [1] "¿Tu doctorado es en: Biologia ?"
## [1] ""
## [1] "¿Aún trabajas con: Biologia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Biologia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Melhoramento genético ?"
## [1] ""
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Aún trabajas con: Ciências Biológicas ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Biologia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Zootecnia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Biotecnologia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Genética e Melhoramento ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Biologia ?"
## [1] "¿Tu doctorado es en: Genética e Melhoramento de Plantas ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Engeheria em Biotecnologia Vegetal (Chile) ?"
## [1] "¿Aún trabajas con: AGRONOMIA ?"
## [1] "¿Aún trabajas con: Engenharia Agronômica ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Biología ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Ciências Biológicas ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Ciências Biológicas ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Engenharia Biotecnológica ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Engenharia Florestal ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Biologia ?"
## [1] "¿Tu doctorado es en: Biologia ?"
## [1] "¿Tu doctorado es en: Ciências Biológicas ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Engenheiro Agrônomo ?"
## [1] "¿Tu doctorado es en: Genética e Melhoramento ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] ""
## [1] "¿Tu doctorado es en: Agronomia ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] ""
## [1] "¿Aún trabajas con: Engenharia Agronômica / Licenciatura em Ciências Agrárias ?"
## [1] "¿Aún trabajas con: Ciencias Biologicas ?"
## [1] "¿Aún trabajas con: Agronomia ?"
## [1] "¿Tu doctorado es en: Eng. Florestal ?"
## [1] "Postdoc"
## [1] "¿Aún trabajas con: Professional ?"
## [1] "¿Aún trabajas con: Plant Genetics ?"
## [1] "¿Aún trabajas con: Professional ?"
## [1] "Finished my Masters "
## [1] "¿Aún trabajas con: Soil Sciencies  ?"
## [1] "Masters degree"
## [1] "PhD"

Consejo: Identación

Observa la diferencia:

# Sin identación
for(i in 1:nrow(dados)){
if(dados$Ocupacion_Actual[i] == "Other"){
if(is.na(dados$Explicacion[i])) {
print("Esta persona no explicó qué quiso decir con 'Otros'") 
} else {
print(dados$Explicacion[i])
}
} else if (dados$Ocupacion_Actual[i] == "PhD"){
print(paste("¿Tu doctorado es en:", dados$Antecedentes[i], "?"))
} else {
print(paste("¿Aún trabajas con:", dados$Antecedentes[i], "?"))
}
}

El editor de código de RStudio tiene una función que facilita la identación de códigos en R. Selecciona el área que deseas identar y presiona Ctrl+i.

Ahora trabajaremos con la columna 5, que contiene información sobre el background de los participantes. Observa que la función table devuelve diferentes categorías que pueden resumirse en una sola, como “Ingeniería Agronómica” y “Agronomía”. Vamos usar un loop para identificar quiénes seleccionaron áreas relacionadas con “Agro”, luego verificar cuáles respuestas no fueron “Agronomía” y pedirle al participante que modifique su respuesta escribiendo solo “Agronomía”.

Consejo: Para identificar el patrón “Agro”, podemos usar la función grepl.

# Ejemplo del uso de la función grepl
dados[,5]
##   [1] "Agronomia"                                                
##   [2] "Agronomia"                                                
##   [3] "Biotecnologia"                                            
##   [4] "Licenciatura em Ciências Biológicas"                      
##   [5] "Agronomia"                                                
##   [6] "Agronomia"                                                
##   [7] "Zootecnia"                                                
##   [8] "Agronomia"                                                
##   [9] "Agronomia"                                                
##  [10] "Engenharia Florestal"                                     
##  [11] "Bacharel em Agronomia"                                    
##  [12] "Engenharia Agronômica"                                    
##  [13] "Engenharia Agrícola"                                      
##  [14] "Agronomia"                                                
##  [15] "Engenharia Agronômica"                                    
##  [16] "Ciências Biológicas"                                      
##  [17] "Agronomia"                                                
##  [18] "Engenheiro Florestal"                                     
##  [19] "Genética e Melhoramento de Plantas"                       
##  [20] "Agronomia"                                                
##  [21] "Agronomia"                                                
##  [22] "Engenharia Agronômica"                                    
##  [23] "Agronomia"                                                
##  [24] "Agronomia"                                                
##  [25] "Ciências Biológicas"                                      
##  [26] "Agronomia"                                                
##  [27] "Ciências Biológicas"                                      
##  [28] "Engenharia Agronômica"                                    
##  [29] "Ciências Biológicas"                                      
##  [30] "Agronomia"                                                
##  [31] "Biotecnologia"                                            
##  [32] "Biologia"                                                 
##  [33] "Agronomia"                                                
##  [34] "Biologia"                                                 
##  [35] "Agronomia"                                                
##  [36] "Biologia"                                                 
##  [37] "Agronomia"                                                
##  [38] "Melhoramento genético"                                    
##  [39] "FAEM - UFPEL"                                             
##  [40] "Agronomia"                                                
##  [41] "Ciências Biológicas"                                      
##  [42] "Ciências Biológicas"                                      
##  [43] "Agronomia"                                                
##  [44] "Biologia"                                                 
##  [45] "Agronomia"                                                
##  [46] "Agronomia"                                                
##  [47] "Eng. Agronômica"                                          
##  [48] "Zootecnia"                                                
##  [49] "Agronomia"                                                
##  [50] "Biotecnologia"                                            
##  [51] "Agronomia"                                                
##  [52] "Agronomia"                                                
##  [53] "Agronomia"                                                
##  [54] "Agronomia"                                                
##  [55] "Agronomia"                                                
##  [56] "Agronomia"                                                
##  [57] "Agronomia"                                                
##  [58] "Genética e Melhoramento"                                  
##  [59] "Agronomia"                                                
##  [60] "Agronomia"                                                
##  [61] "Agronomia"                                                
##  [62] "Ciências bilógicas"                                       
##  [63] "Agronomia"                                                
##  [64] "Biologia"                                                 
##  [65] "Genética e Melhoramento de Plantas"                       
##  [66] "Agronomia"                                                
##  [67] "Agronomia"                                                
##  [68] "Agronomia"                                                
##  [69] "Agronomia"                                                
##  [70] "Agronomia"                                                
##  [71] "Agronomia"                                                
##  [72] "Agronomia"                                                
##  [73] "Agronomia"                                                
##  [74] "Engeheria em Biotecnologia Vegetal (Chile)"               
##  [75] "AGRONOMIA"                                                
##  [76] "Engenharia Agronômica"                                    
##  [77] "Agronomia"                                                
##  [78] "Agronomia"                                                
##  [79] "Biología"                                                 
##  [80] "Agronomia"                                                
##  [81] "Ciências Biológicas"                                      
##  [82] "Agronomia"                                                
##  [83] "Agronomia"                                                
##  [84] "Agronomia"                                                
##  [85] "Agronomia"                                                
##  [86] "Agronomia"                                                
##  [87] "Ciências Biológicas"                                      
##  [88] "Agronomia"                                                
##  [89] "Agronomia"                                                
##  [90] "Agronomia"                                                
##  [91] "Agronomia"                                                
##  [92] "Agronomia"                                                
##  [93] "Engenharia Biotecnológica"                                
##  [94] "Agronomia"                                                
##  [95] "Agronomia"                                                
##  [96] "Engenharia Florestal"                                     
##  [97] "Agronomia"                                                
##  [98] "Agronomia"                                                
##  [99] "Biologia"                                                 
## [100] "Biologia"                                                 
## [101] "Ciências Biológicas"                                      
## [102] "Agronomia"                                                
## [103] "Agronomia"                                                
## [104] "Agronomia"                                                
## [105] "Engenheiro Agrônomo"                                      
## [106] "Genética e Melhoramento"                                  
## [107] "Agronomia"                                                
## [108] "Agronomia"                                                
## [109] "Biotecnologia"                                            
## [110] "Agronomia"                                                
## [111] "Agronomia"                                                
## [112] "Biotecnologia"                                            
## [113] "Engenharia Agronômica / Licenciatura em Ciências Agrárias"
## [114] "Ciencias Biologicas"                                      
## [115] "Agronomia"                                                
## [116] "Eng. Florestal"                                           
## [117] "Genetics"                                                 
## [118] "Professional"                                             
## [119] "Plant Genetics"                                           
## [120] "Professional"                                             
## [121] "Climate Change"                                           
## [122] "Soil Sciencies "                                          
## [123] "Animal Science"                                           
## [124] "Animal Breeding and Genetics"
grepl("Agro", dados[,5]) # Identifica qué filas contienen el texto "Agro"
##   [1]  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
##  [13] FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
##  [25] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE
##  [37]  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE
##  [49]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
##  [61]  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
##  [73]  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE
##  [85]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
##  [97]  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE
## [109] FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
## [121] FALSE FALSE FALSE FALSE
dados[grepl("Agro", dados[,5]),5]
##  [1] "Agronomia"                                                
##  [2] "Agronomia"                                                
##  [3] "Agronomia"                                                
##  [4] "Agronomia"                                                
##  [5] "Agronomia"                                                
##  [6] "Agronomia"                                                
##  [7] "Bacharel em Agronomia"                                    
##  [8] "Engenharia Agronômica"                                    
##  [9] "Agronomia"                                                
## [10] "Engenharia Agronômica"                                    
## [11] "Agronomia"                                                
## [12] "Agronomia"                                                
## [13] "Agronomia"                                                
## [14] "Engenharia Agronômica"                                    
## [15] "Agronomia"                                                
## [16] "Agronomia"                                                
## [17] "Agronomia"                                                
## [18] "Engenharia Agronômica"                                    
## [19] "Agronomia"                                                
## [20] "Agronomia"                                                
## [21] "Agronomia"                                                
## [22] "Agronomia"                                                
## [23] "Agronomia"                                                
## [24] "Agronomia"                                                
## [25] "Agronomia"                                                
## [26] "Agronomia"                                                
## [27] "Eng. Agronômica"                                          
## [28] "Agronomia"                                                
## [29] "Agronomia"                                                
## [30] "Agronomia"                                                
## [31] "Agronomia"                                                
## [32] "Agronomia"                                                
## [33] "Agronomia"                                                
## [34] "Agronomia"                                                
## [35] "Agronomia"                                                
## [36] "Agronomia"                                                
## [37] "Agronomia"                                                
## [38] "Agronomia"                                                
## [39] "Agronomia"                                                
## [40] "Agronomia"                                                
## [41] "Agronomia"                                                
## [42] "Agronomia"                                                
## [43] "Agronomia"                                                
## [44] "Agronomia"                                                
## [45] "Agronomia"                                                
## [46] "Agronomia"                                                
## [47] "Agronomia"                                                
## [48] "Engenharia Agronômica"                                    
## [49] "Agronomia"                                                
## [50] "Agronomia"                                                
## [51] "Agronomia"                                                
## [52] "Agronomia"                                                
## [53] "Agronomia"                                                
## [54] "Agronomia"                                                
## [55] "Agronomia"                                                
## [56] "Agronomia"                                                
## [57] "Agronomia"                                                
## [58] "Agronomia"                                                
## [59] "Agronomia"                                                
## [60] "Agronomia"                                                
## [61] "Agronomia"                                                
## [62] "Agronomia"                                                
## [63] "Agronomia"                                                
## [64] "Agronomia"                                                
## [65] "Agronomia"                                                
## [66] "Agronomia"                                                
## [67] "Agronomia"                                                
## [68] "Agronomia"                                                
## [69] "Agronomia"                                                
## [70] "Agronomia"                                                
## [71] "Agronomia"                                                
## [72] "Agronomia"                                                
## [73] "Engenharia Agronômica / Licenciatura em Ciências Agrárias"
## [74] "Agronomia"
for(i in 1:nrow(dados)){
  if(grepl("Agro", dados[i,5])){
    if(dados[i,5] != "Agronomia"){
      print("Por favor, sustituye tu respuesta por Agronomía.") 
    }
  } 
}
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."

Observa que el código anterior no retorna las filas incorrectas, solo imprime el mensaje. Para solucionar esto, debemos almacenar estas filas en un vector y luego acceder a ellas.

homog <- vector()
for(i in 1:nrow(dados)){
  if(grepl("Agro", dados[i,5])){
    if(dados[i,5] != "Agronomia"){
      print("Por favor, sustituye tu respuesta por Agronomía.") 
      homog <- c(homog, i)
    }
  } 
}
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
## [1] "Por favor, sustituye tu respuesta por Agronomía."
homog
## [1]  11  12  15  22  28  47  76 113

¿Cómo corregirías estos elementos incorrectos? ¡Inténtalo!

‘dados[homog, 5] <- “Agronomía”’

While

En este tipo de estructura de repetición, la tarea se realizará hasta que se alcance determinada condición.

x <- 1

while(x < 5){
  x <- x + 1
  cat(x)
}
## 2345

Es muy importante que en esta estructura se alcance la condición, en caso contrario el bucle funcionará infinitamente y tendrás que interrumpirlo por medios externos. Un ejemplo de estos “medios externos” en RStudio es hacer clic en el símbolo en rojo en la esquina superior derecha de la ventana de la consola. También puedes presionar Ctrl+C en la consola.

No es muy difícil que esto suceda, basta un pequeño error como:

x <- 1

while(x < 5){
  x + 1
  cat(x)
}

Aquí podemos utilizar los comandos break y next para atender otras condiciones, como:

x <- 1

while(x < 5){
  x <- x + 1
  if(x==4) break
  cat(x)
}
## 23
x <- 1

while(x < 5){
  x <- x + 1
  if(x==4) next
  cat(x)
}
## 235

Repeat

Esta estructura también exige una condición de parada, pero esta condición se coloca necesariamente dentro del bloque de código con el uso de break. Entonces repite el bloque de código hasta que la condición lo interrumpa.

x <- 1
repeat{
  x <- x+1
  cat(x)
  if(x==4) break
}
## 234

Bucles dentro de bucles

También es posible utilizar estructuras de repetición dentro de estructuras de repetición. Por ejemplo, si queremos trabajar tanto en las columnas como en las filas de una matriz.

# Creando una matriz vacía
ex_mat <- matrix(nrow=10, ncol=10)

# cada número dentro de la matriz será el producto del índice de la columna por el índice de la fila
for(i in 1:dim(ex_mat)[1]) {
  for(j in 1:dim(ex_mat)[2]) {
    ex_mat[i,j] = i*j
  }
}

Otro ejemplo de uso:

var1 <- c("fertilizante1", "fertilizante2")
var2 <- c("ESS", "URO", "GRA")

w <- 1
for(i in var1){
  for(j in var2){
    nombre_archivo <- paste0(i,"_planta_",j,".txt")
    archivo <- data.frame("bloque" = "fake_data", "tratamiento" ="fake_data")
    write.table(archivo, file = nombre_archivo)
    w <- w + 1
  }
}

# Verifica tu directorio de trabajo, los archivos deben haber sido generados

Si estás adelantado/a respecto a tus compañeros, ya puedes hacer los ejercicios de la Sesión 3, si no, hazlos en otro momento y envíanos dudas por el foro.

Algunos consejos:

  • Ten cuidado al ejecutar el mismo comando más de una vez, algunas variables pueden no ser como eran antes. Para que el comando funcione de la misma forma es necesario que los objetos de entrada estén de la forma que esperas.
  • Recuerda que = es para definir objetos y == es el signo de igualdad.
  • En las estructuras condicionales y de repetición, recuerda que es necesario mantener la sintaxis esperada: If(){} y for(i in 1:10){}. En el for, podemos cambiar la letra que será el índice, pero siempre es necesario proporcionar una secuencia de enteros o caracteres.
  • Usar indentación ayuda a visualizar el principio y fin de cada estructura de código y facilita el abrir y cerrar llaves. Indentación son esos espacios que usamos antes de la línea, como:
# Creando una matriz vacía
ex_mat <- matrix(nrow=10, ncol=10)

# cada número dentro de la matriz será el producto del índice de la columna por el índice de la fila
for(i in 1:dim(ex_mat)[1]) {   # Primer nivel, no tiene espacio
  for(j in 1:dim(ex_mat)[2]) { # Segundo nivel tiene un espacio (tab)
    ex_mat[i,j] = i*j          # Tercer nivel tiene dos espacios
  }                            # Cerré el segundo nivel
}                              # Cerré el primer nivel

Vectorización

Aunque los bucles son intuitivos y más fáciles de entender, son más lentos y menos eficientes que la vectorización. La vectorización es una técnica que permite aplicar operaciones en todos los elementos de un vector o matriz de una sola vez, sin la necesidad de iterar sobre cada elemento individualmente.

Aquí está un simple ejemplo de un código no vectorizado (utilizando bucle) y su versión vectorizada:

# No vectorizado (usando bucle)
numeros <- 1:5
resultado_bucle <- numeric(length(numeros))
for(i in 1:length(numeros)) {
    resultado_bucle[i] <- numeros[i] * 2
}

# Enfoque vectorizado
numeros <- 1:5
resultado_vectorizado <- numeros * 2

resultado_bucle == resultado_vectorizado
## [1] TRUE TRUE TRUE TRUE TRUE

Esta transformación de código puede volverse más compleja dependiendo del escenario. Por ejemplo, piensa cómo sería una versión vectorizada del bucle anterior:

# No vectorizado (usando bucle)
ex_mat <- matrix(nrow=10, ncol=10)

for(i in 1:dim(ex_mat)[1]) {   
  for(j in 1:dim(ex_mat)[2]) { 
    ex_mat[i,j] = i*j          
  }                            
}

# ¿Vectorizado?

Aquí es un buen momento para que practiques el uso de una herramienta de IA para ayudarte a transformar el código en una versión vectorizada. Puedes utilizar chatgpt o copilot, por ejemplo. Compara el resultado del código proporcionado con el que generaste con el bucle para verificar si la herramienta está realmente haciendo lo que deseas. Esta transformación vale la pena si el código en bucle está muy lento o si tienes que ejecutar el mismo código muchas veces.

’ex_mat <- outer(1:10, 1:10, “*“)’

Creando funciones

Si ya te sientes cómodo usando bucles, podrías preguntarte: “¿Y si quiero hacer esto varias veces?” o
“¿Y si quiero aplicar esta lógica a diferentes conjuntos de datos?”. Aquí es donde entran las funciones.

Podemos crear funciones personalizadas para realizar tareas específicas. La sintaxis básica para crear una función en R es la siguiente:

mi_funcion <- function(arg1, arg2) {
  # Código de la función
  resultado <- arg1 + arg2
  return(resultado)
}

La función mi_funcion recibe dos argumentos (arg1 y arg2) y devuelve su suma. Puedes llamar a esta función pasando los valores deseados:

resultado <- mi_funcion(3, 5)
resultado  # Salida: 8
## [1] 8

Ejemplo de función personalizada con vectorización:

suma_vectorizada <- function(vector) {
  # Verifica si el vector es numérico
  if (!is.numeric(vector)) {
    stop("El vector debe ser numérico.")
  }
  # Realiza la suma de los elementos del vector
  suma <- sum(vector)
  
  # Estandarización z-score
  z_score <- (vector - mean(vector)) / sd(vector)

  resultado <- list(suma = suma, z_score = z_score)
  return(resultado)
}

# Llamando la función
resultado_vectorizado <- suma_vectorizada(c(1, 2, 3, 4, 5))
resultado_vectorizado 
## $suma
## [1] 15
## 
## $z_score
## [1] -1.2649111 -0.6324555  0.0000000  0.6324555  1.2649111

Ejemplo de función utilizando un data.frame como entrada. Observa que el uso repetitivo de la función requerirá que el dato de entrada tenga el mismo formato o al menos las columnas utilizadas. Una buena práctica es verificar si las columnas necesarias están presentes antes de realizar cálculos.

calc_volumen <- function(data_frame) {
  if (!all(c("diametro", "altura") %in% colnames(data_frame))) {
    stop("Las columnas 'diametro' y 'altura' deben estar presentes en el data frame.")
  }
  
  volumen <- 3.14 * ((data_frame$diametro / 2)^2) * data_frame$altura
  return(volumen)
}

Es recomendable documentar siempre las funciones. Existe un paquete llamado roxygen2 que genera páginas de manual automáticamente para la documentación de las funciones en un paquete. Requiere una sintaxis adecuada, como se indica a continuación:

#' Calculate the volume of cylinders based on diameter and height
#'
#' @param data_frame A data frame containing the columns "diametro" (diameter) and "altura" (height).
#'
#' @return A numeric vector with the calculated volumes for each cylinder.
#'
#' @details
#' Calculates the volume using the formula:
#' \deqn{Volumen = \pi \times \left(\frac{diametro}{2}\right)^2 \times altura}
#' If the required columns are missing, the function will stop with an error.
#'
#' @examples
#' df <- data.frame(diametro = c(4, 6), altura = c(10, 15))
#' calc_volumen(df)
#' # [1] 125.6 424.2
#'
#' @note
#' The function uses 3.14 as an approximation for pi. For higher precision, consider replacing 3.14 with the built-in `pi` constant.
calc_volumen <- function(data_frame) {
  if (!all(c("diametro", "altura") %in% colnames(data_frame))) {
    stop("Las columnas 'diametro' y 'altura' deben estar presentes en el data frame.")
  }
  
  volumen <- 3.14 * ((data_frame$diametro / 2)^2) * data_frame$altura
  return(volumen)
}

Si esta función formara parte de un paquete, solo necesitaríamos usar el comando roxygen2::roxygenise() para crear la página de ayuda. Vea un ejemplo de la estructura de un paquete de R en https://github.com/Breeding-Insight/BIGr

Familia de funciones apply

La familia de funciones apply también puede funcionar como estructura de repetición. Su sintaxis es más concisa comparada con for o while y puede facilitar la elaboración del código.

apply

La función apply es la base de todas las demás de su familia, por lo que entenderla es esencial. Su sintaxis es: apply(X, MARGIN, FUN, …), donde X es un array (incluyendo matrices), MARGIN es 1 si se aplica a filas, 2 a columnas y c(1,2) a ambas; FUN es la función a aplicar.

Ejemplo simple con una matriz:

ex_mat <- matrix(seq(0,21,3), nrow = 2)

Suma de columnas:

apply(ex_mat, 2, sum)
## [1]  3 15 27 39

Suma de filas:

apply(ex_mat, 1, sum)
## [1] 36 48

Equivalente con for:

for(i in 1:dim(ex_mat)[2]){
  print(sum(ex_mat[,i]))
}
## [1] 3
## [1] 15
## [1] 27
## [1] 39
for(i in 1:dim(ex_mat)[1]){
  print(sum(ex_mat[i,]))
}
## [1] 36
## [1] 48

Ejemplo con función personalizada:

zscore <- function(vector) {
  resultado <- (vector - mean(vector)) / sd(vector)
  return(resultado)
}

resultados_filas <- apply(ex_mat, 1, function(x) zscore(x)) 
resultados_columnas <- apply(ex_mat, 2, function(x) zscore(x)) 

lapply

A diferencia de apply, lapply puede recibir vectores y listas (se usa principalmente con listas) y devuelve una lista.

ex_list <- list(A=matrix(seq(0,21,3), nrow = 2), 
                B=matrix(seq(0,14,2), nrow = 2), 
                C=matrix(seq(0,39,5), nrow = 2))
str(ex_list)
## List of 3
##  $ A: num [1:2, 1:4] 0 3 6 9 12 15 18 21
##  $ B: num [1:2, 1:4] 0 2 4 6 8 10 12 14
##  $ C: num [1:2, 1:4] 0 5 10 15 20 25 30 35

Seleccionar segunda columna de todas las matrices:

lapply(ex_list, "[", 2)
## $A
## [1] 3
## 
## $B
## [1] 2
## 
## $C
## [1] 5

Usando una función personalizada:

resultados <- lapply(ex_list, function(x) apply(x, 1, zscore))
resultados
## $A
##            [,1]       [,2]
## [1,] -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950
## 
## $B
##            [,1]       [,2]
## [1,] -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950
## 
## $C
##            [,1]       [,2]
## [1,] -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950

sapply

sapply es una variación de lapply que intenta simplificar el resultado devolviendo un vector, matriz o array.

sapply(ex_list, "[", 2)
## A B C 
## 3 2 5
resultados <- sapply(ex_list, function(x) apply(x, 1, zscore))
resultados
##               A          B          C
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950  1.1618950
## [5,] -1.1618950 -1.1618950 -1.1618950
## [6,] -0.3872983 -0.3872983 -0.3872983
## [7,]  0.3872983  0.3872983  0.3872983
## [8,]  1.1618950  1.1618950  1.1618950

tapply

tapply aplica funciones basadas en niveles de una variable categórica (factor), comúnmente en data.frames.

str(dados)
## 'data.frame':    124 obs. of  8 variables:
##  $ Fecha           : chr  "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" "07/04/2025 14:00:00" ...
##  $ Afiliacion      : chr  "Rcourse2021" "Rcourse2021" "Rcourse2021" "Rcourse2021" ...
##  $ Longitud        : chr  "-54.5724" "-47.6476" "-48.0547" "-106.6563" ...
##  $ Latitud         : chr  "-25.5263" "-22.725" "-15.911" "52.1418" ...
##  $ Antecedentes    : chr  "Agronomia" "Agronomia" "Biotecnologia" "Licenciatura em Ciências Biológicas" ...
##  $ Ocupacion_Actual: chr  "PhD" "PhD" "Masters degree" "Other" ...
##  $ Explicacion     : chr  "" "" "" "" ...
##  $ Conocimiento_R  : chr  "Intermediate" "Intermediate" "Beginner (some knowledge)" "Intermediate" ...
dados$Afiliacion  <- as.factor(dados$Afiliacion)

dados$Conocimiento_R_num <- NA
dados$Conocimiento_R_num[dados$Conocimiento_R == "Advanced"] <- 3
dados$Conocimiento_R_num[dados$Conocimiento_R == "Intermediate"] <- 2
dados$Conocimiento_R_num[dados$Conocimiento_R == "Beginner (some knowledge)"] <- 1
dados$Conocimiento_R_num[dados$Conocimiento_R == "No R knowledge"] <- 0
dados$Conocimiento_R_num[dados$Conocimiento_R == ""] <- NA

tapply(dados$Conocimiento_R_num, dados$Afiliacion, mean)
##                                                       Agronomic Engineer 
##                                                                       NA 
##                                                         Breeding Insight 
##                                                                       NA 
##                                                     CIA Central Pecuario 
##                                                                  0.00000 
##                                                                     INTA 
##                                                                  1.00000 
## National Institute of Innovation and Transfer in Agricultural Technology 
##                                                                  2.00000 
##                                                              Rcourse2021 
##                                                                  1.87069
tapply(dados$Conocimiento_R_num, dados$Afiliacion, function(x) mean(x, na.rm = TRUE))
##                                                       Agronomic Engineer 
##                                                                      NaN 
##                                                         Breeding Insight 
##                                                                  2.00000 
##                                                     CIA Central Pecuario 
##                                                                  0.00000 
##                                                                     INTA 
##                                                                  1.00000 
## National Institute of Innovation and Transfer in Agricultural Technology 
##                                                                  2.00000 
##                                                              Rcourse2021 
##                                                                  1.87069

mapply

mapply es una versión multivariada de sapply, permite aplicar funciones a múltiples vectores.

suma <- function(x, y) {
  return(x + y)
}
vetor1 <- c(1, 2, 3)
vetor2 <- c(4, 5, 6)
resultado_mapply <- mapply(suma, vetor1, vetor2)
print(resultado_mapply)
## [1] 5 7 9
multiplicacion <- function(x, y, z) {
  return(x * y * z)
}
vetor3 <- c(7, 8, 9)
resultado_mapply <- mapply(multiplicacion, vetor1, vetor2, vetor3)
print(resultado_mapply)
## [1]  28  80 162
resultado_mapply <- mapply(function(x, y) {
  return(c(suma = x + y, producto = x * y))
}, vetor1, vetor2)
print(resultado_mapply)
##          [,1] [,2] [,3]
## suma        5    7    9
## producto    4   10   18
suma_producto <- function(x, y) {
  return(c(suma = x + y, producto = x * y))
}
resultado_mapply <- mapply(suma_producto, vetor1, vetor2)
print(resultado_mapply)
##          [,1] [,2] [,3]
## suma        5    7    9
## producto    4   10   18
suma_producto <- function(x, y, z) {
  return(c(suma = x + y + z, producto = x * y * z))
}
resultado_mapply <- mapply(suma_producto, vetor1, vetor2, vetor3)
print(resultado_mapply)
##          [,1] [,2] [,3]
## suma       12   15   18
## producto   28   80  162

Si estás adelantado respecto a tus compañeros, ya puedes hacer los ejercicios de la Sesión extra. Si no, hazlos desde casa con calma y envíanos tus dudas por el foro.

Formatos Largo y Ancho

En el análisis y visualización de datos con R, especialmente usando el tidyverse, la estructura de los datos es muy importante. tidyverse es una colección de paquetes de R diseñados para la ciencia de datos, y enfatiza el uso de principios de datos ordenados (tidy data). Los datos ordenados son una forma estandarizada de organizar los datos que facilita su uso. Aquí hay una lista de todos los paquetes del tidyverse:

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
tidyverse_packages()
##  [1] "broom"         "conflicted"    "cli"           "dbplyr"       
##  [5] "dplyr"         "dtplyr"        "forcats"       "ggplot2"      
##  [9] "googledrive"   "googlesheets4" "haven"         "hms"          
## [13] "httr"          "jsonlite"      "lubridate"     "magrittr"     
## [17] "modelr"        "pillar"        "purrr"         "ragg"         
## [21] "readr"         "readxl"        "reprex"        "rlang"        
## [25] "rstudioapi"    "rvest"         "stringr"       "tibble"       
## [29] "tidyr"         "xml2"          "tidyverse"

Existen dos formatos principales para organizar los datos:

  • Formato ancho (wide): una fila por sujeto con múltiples columnas para medidas repetidas.
  • Formato largo (long): una fila por observación, haciendo que los datos sean ordenados y compatibles con herramientas como ggplot2.

Veamos un conjunto de datos que muestra la población (en millones) de 10 países a lo largo de tres años:

# Datos en formato ancho (estimaciones realistas de población en millones)
data_wide <- tibble(
  country = c(
    "EEUU", "Canadá", "México", "Brasil", "Costa Rica",
    "Uruguay", "China", "Japón", "India", "Groenlandia"
  ),
  `2000` = c(282, 31, 98, 174, 4, 3.3, 1267, 127, 1050, 0.056),
  `2010` = c(309, 34, 112, 196, 4.5, 3.4, 1340, 128, 1230, 0.057),
  `2020` = c(331, 38, 126, 213, 5, 3.5, 1402, 126, 1380, 0.056)
)

data_wide
## # A tibble: 10 × 4
##    country       `2000`   `2010`   `2020`
##    <chr>          <dbl>    <dbl>    <dbl>
##  1 EEUU         282      309      331    
##  2 Canadá        31       34       38    
##  3 México        98      112      126    
##  4 Brasil       174      196      213    
##  5 Costa Rica     4        4.5      5    
##  6 Uruguay        3.3      3.4      3.5  
##  7 China       1267     1340     1402    
##  8 Japón        127      128      126    
##  9 India       1050     1230     1380    
## 10 Groenlandia    0.056    0.057    0.056

Usa pivot_longer() para convertir de formato ancho a formato largo:

data_long <- pivot_longer(data_wide,
  cols = -country,
  names_to = "year",
  values_to = "population"
)

data_long
## # A tibble: 30 × 3
##    country year  population
##    <chr>   <chr>      <dbl>
##  1 EEUU    2000         282
##  2 EEUU    2010         309
##  3 EEUU    2020         331
##  4 Canadá  2000          31
##  5 Canadá  2010          34
##  6 Canadá  2020          38
##  7 México  2000          98
##  8 México  2010         112
##  9 México  2020         126
## 10 Brasil  2000         174
## # ℹ 20 more rows

Nota que nuestro data.frame fue convertido al formato tibble. Los tibbles son una versión moderna de los data.frames, diseñados para ser más fáciles de usar y más eficientes. Son parte del tidyverse y se utilizan frecuentemente en análisis de datos. Aquí una comparación práctica:

Característica data.frame tibble (del paquete tibble)
Base o Tidyverse Base R Parte del tidyverse
Impresión Muestra todo Muestra previa (10 filas)
Tipos de columna Conversión automática posible No convierte automáticamente
Indexación df[,1] da un vector tibble[,1] da un tibble
Nombres de fila Siempre tiene No utiliza nombres de fila

La mayoría de las funciones de tidyverse, especialmente ggplot2, funcionan mejor con datos en formato largo.

El formato ancho es generalmente más fácil de exportar a CSV o Excel. Puedes volver al formato ancho con pivot_wider():

data_wide_back <- pivot_wider(data_long,
  names_from = year,
  values_from = population
)

data_wide_back
## # A tibble: 10 × 4
##    country       `2000`   `2010`   `2020`
##    <chr>          <dbl>    <dbl>    <dbl>
##  1 EEUU         282      309      331    
##  2 Canadá        31       34       38    
##  3 México        98      112      126    
##  4 Brasil       174      196      213    
##  5 Costa Rica     4        4.5      5    
##  6 Uruguay        3.3      3.4      3.5  
##  7 China       1267     1340     1402    
##  8 Japón        127      128      126    
##  9 India       1050     1230     1380    
## 10 Groenlandia    0.056    0.057    0.056

Introducción al uso del pipe

El operador pipe (%>%) es una herramienta poderosa en R, especialmente cuando se utiliza el paquete tidyverse. Permite encadenar múltiples funciones de una manera clara y legible. En lugar de anidar funciones unas dentro de otras, puedes usar el pipe para pasar la salida de una función directamente como entrada a la siguiente. Aquí tienes un ejemplo:

data_wide_back <- data_long %>%
  pivot_wider(
    names_from = year,
    values_from = population
  )

El uso del operador pipe tiene sentido cuando se tiene una secuencia de operaciones que realizar sobre un conjunto de datos. Vamos a explorar algunas otras funciones del tidyverse que pueden utilizarse con el operador pipe.

data_wide_back <- data_long %>%
  pivot_wider(
    names_from = year,
    values_from = population
  ) %>%
  mutate(total_population = `2000` + `2010` + `2020`) %>%
  arrange(desc(total_population))

La función mutate se utiliza para crear nuevas variables o modificar variables existentes, mientras que la función arrange se usa para ordenar el marco de datos según una o más variables.

Otras funciones útiles incluyen filter (para filtrar filas según condiciones) y select (para seleccionar columnas específicas).

data_wide_back <- data_long %>%
  pivot_wider(
    names_from = year,
    values_from = population
  ) %>%
  mutate(total_population = `2000` + `2010` + `2020`) %>%
  arrange(desc(total_population)) %>%
  filter(total_population > 1000) %>%
  select(country, total_population)

Usando el formato largo, también podemos resumir los datos utilizando la función summarise. Esto es útil para calcular estadísticas resumidas como la media, la mediana o la población total por año.

data_summary <- data_long %>%
  group_by(year) %>%
  summarise(
    total_population = sum(population, na.rm = TRUE),
    avg_population = mean(population, na.rm = TRUE),
    max_population = max(population, na.rm = TRUE)
  )

data_summary
## # A tibble: 3 × 4
##   year  total_population avg_population max_population
##   <chr>            <dbl>          <dbl>          <dbl>
## 1 2000             3036.           304.           1267
## 2 2010             3357.           336.           1340
## 3 2020             3625.           362.           1402

El operador pipe se popularizó con el paquete dplyr y, en versiones más recientes de R, también está disponible en la versión base de R. En R base, el operador pipe es |>,
pero funciona de manera similar al operador %>% del paquete dplyr.

La principal diferencia es que el operador pipe de R base (|>) no requiere el paquete magrittr, el cual sí es necesario para usar %>%.
Otra diferencia es que el operador %>% permite usar el marcador de posición . para indicar dónde debe colocarse la entrada en la siguiente función.
Esto es especialmente útil cuando la entrada no corresponde al primer argumento de la función:

data_wide %>%
  lm(`2020` ~ `2000`, data = .) %>%
  summary()
## 
## Call:
## lm(formula = `2020` ~ `2000`, data = .)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -105.426   -4.807   -1.463    3.357  130.481 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   1.5810    23.1470   0.068    0.947    
## `2000`        1.1885     0.0434  27.384 3.41e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 60.18 on 8 degrees of freedom
## Multiple R-squared:  0.9894, Adjusted R-squared:  0.9881 
## F-statistic: 749.9 on 1 and 8 DF,  p-value: 3.409e-09

La función lm se utiliza para ajustar un modelo lineal, y la función summary proporciona un resumen del modelo ajustado.
Cabe destacar que el marcador de posición . indica que los datos de entrada del paso anterior deben utilizarse como argumento data en la función lm.

El operador pipe de R base no admite el uso del marcador .. En su lugar, puedes usar funciones anónimas entre paréntesis para especificar dónde debe ir la entrada:

data_wide |>
  (\(df) lm(`2020` ~ `2000`, data = df))() |>
  summary()
## 
## Call:
## lm(formula = `2020` ~ `2000`, data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -105.426   -4.807   -1.463    3.357  130.481 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   1.5810    23.1470   0.068    0.947    
## `2000`        1.1885     0.0434  27.384 3.41e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 60.18 on 8 degrees of freedom
## Multiple R-squared:  0.9894, Adjusted R-squared:  0.9881 
## F-statistic: 749.9 on 1 and 8 DF,  p-value: 3.409e-09

Introducción a ggplot2

ggplot2 es un paquete poderoso y flexible para crear visualizaciones en R. Forma parte de la colección de paquetes del tidyverse y es ampliamente utilizado en la visualización de datos.
La idea principal detrás de ggplot2 es crear gráficos basados en la Gramática de los Gráficos (Grammar of Graphics), un marco teórico para la visualización de datos que descompone un gráfico en un conjunto de componentes independientes y estructurados.
En ggplot2, encontrarás los siguientes componentes principales:

  • Datos (Data): El conjunto de datos que estás utilizando.
  • Estéticas (aes): Propiedades visuales (como posición, color, tamaño) que se asignan a los datos.
  • Geometrías (geom): Tipos de elementos visuales en un gráfico, como puntos, líneas, barras, etc.
  • Estadísticas (stat): Transformaciones estadísticas aplicadas a los datos, como suavizado o agrupamiento.
  • Escalas (scale): Ajustes para mapear los datos a las estéticas (por ejemplo, escalas de color).
  • Coordenadas (coord): El sistema de coordenadas, como cartesiano o polar.
  • Facetas (facet): División del conjunto de datos en subconjuntos para crear múltiples paneles (como pequeños múltiplos).

A continuación se presenta un desglose de los elementos clave de la Gramática de los Gráficos:

Aquí tienes la traducción al español manteniendo el formato R Markdown (.Rmd):

Datos

Los datos son la base de cualquier gráfico. Contienen las variables que deseas visualizar. En ggplot2, se especifican los datos usando el argumento data:

ggplot(data = data_long)

Estéticas (aes)

Las estéticas definen cómo se asignan los datos a las propiedades visuales del gráfico, como:

  • posición en x e y (x, y)
  • color (color)
  • tamaño (size)
  • forma (shape)
  • relleno (fill)

Por ejemplo:

ggplot(data = data_long, aes(x = year, y = population))

Aquí:

  • x = year (eje horizontal)
  • y = population (eje vertical)

Geometrías (geom)

Las geometrías son los elementos visuales del gráfico. Diferentes tipos de geometrías permiten crear diferentes tipos de gráficos:

  • geom_point() para gráficos de dispersión
  • geom_line() para gráficos de líneas
  • geom_bar() para gráficos de barras
  • geom_histogram() para histogramas
  • geom_boxplot() para diagramas de caja

Por ejemplo:

ggplot(data = data_long, aes(x = year, y = population)) +
  geom_point() 

ggplot(data = data_long, aes(x = year, y = population, group = country)) +
  geom_line() + geom_point()

# agregar color por país
ggplot(data = data_long, aes(x = year, y = population, group = country, color = country)) +
  geom_line() 

Esto crea un gráfico de dispersión donde los puntos representan los datos.

Estadísticas (stat)

Algunos gráficos requieren transformaciones estadísticas, como suavizado o agrupamiento. Puedes aplicar estas transformaciones usando las funciones stat_*.

Podemos aplicar resúmenes estadísticos. Por ejemplo, agregar una tendencia suavizada:

ggplot(data_long, aes(x = year, y = population)) +
  stat_summary(fun = mean, geom = "line", group = 1) +
  labs(title = "Tendencia Promedio de la Población")

Escalas (scale)

Modifica cómo los datos se asignan a las estéticas visuales (por ejemplo, color):

ggplot(data_long, aes(x = year, y = population, group = country, color = country)) +
  geom_line(size = 1.2) +
  scale_color_manual(values = c("China" = "red", "India" = "orange", "USA" = "blue")) +
  labs(title = "Población por País (Colores Seleccionados)")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Aquí, scale_color_manual() personaliza la asignación de colores para cada país. También puedes usar paletas listas del paquete viridis, que es amigable para personas con daltonismo:

library(viridis)
## Loading required package: viridisLite
ggplot(data_long, aes(x = year, y = population, group = country, color = country)) +
  geom_line(size = 1.2) +
  scale_color_viridis_d() +
  labs(title = "Población por País (Colores Viridis)")

Esto utiliza la paleta de colores viridis, que está diseñada para ser perceptualmente uniforme y accesible.

Coordenadas (coord)

El sistema de coordenadas define la disposición del gráfico. El sistema más común es el cartesiano (ejes x e y), pero también puedes usar coordenadas polares o transformar los ejes.

Por ejemplo:

ggplot(data_long %>% filter(year == "2020"), aes(x = country, y = population)) +
  geom_col() +
  coord_flip() +
  labs(title = "Población por País en 2020 (Ejes Invertidos)")

Esto invierte los ejes, de modo que el eje x se convierte en el eje y y viceversa.

Facetas (facet)

Las facetas permiten dividir un gráfico en múltiples paneles, lo cual es útil para comparar subconjuntos de los datos. Las facetas pueden hacerse por filas o columnas:

ggplot(data_long, aes(x = year, y = population)) +
  geom_line(group = 1) +
  facet_wrap(~ country) +
  labs(title = "Tendencia de Población por País (Faceteado)")

Etiquetas y Temas

Puedes personalizar el gráfico con etiquetas y temas. Las etiquetas incluyen títulos, etiquetas de ejes y leyendas. Los temas controlan la apariencia general del gráfico.

ggplot(data_long, aes(x = year, y = population, group = country, color = country)) +
  geom_line(size = 1.2) +
  labs(
    title = "Población a lo Largo del Tiempo por País",
    subtitle = "Basado en datos simulados (millones)",
    x = "Año",
    y = "Población (Millones)",
    caption = "Fuente de datos: Simulado"
  ) +
  theme_minimal()

Puedes personalizar las fuentes y el estilo:

ggplot(data_long, aes(x = year, y = population, color = country, group = country)) +
  geom_line(size = 1.2) +
  labs(
    title = "Población a lo Largo del Tiempo por País",
    subtitle = "Basado en datos simulados (millones)",
    x = "Año",
    y = "Población (Millones)",
    caption = "Fuente de datos: Simulado"
  ) +
  theme_minimal() + theme(
    plot.title = element_text(size = 18, face = "bold"),
    axis.title = element_text(size = 14),
    legend.title = element_text(size = 12)
  )

Creación de gráficos de mapas con ggplot2

# Load necessary datasets
dados <- read.csv("https://breeding-insight.github.io/learn-hub/r-intro/data/dados_2025.csv")

colnames(dados) <- c("Date", "Affiliation", "Longitude", "Latitude", "Background", "Present_Occupation", "Explain", "KnowledgeR")

# Create quantitative measure
dados$KnowledgeR_num <- NA
dados$KnowledgeR_num[dados$KnowledgeR == "Advanced"] <- 3
dados$KnowledgeR_num[dados$KnowledgeR == "Intermediate"] <- 2
dados$KnowledgeR_num[dados$KnowledgeR == "Beginner (some knowledge)"] <- 1
dados$KnowledgeR_num[dados$KnowledgeR == "No R knowledge"] <- 0
dados$KnowledgeR_num[dados$KnowledgeR == ""] <- NA


# Cargar los datos del mapa del mundo
world_map <- map_data("world")

dados$Latitude <- as.numeric(dados$Latitude) 
## Warning: NAs introduced by coercion
dados$Longitude <- as.numeric(dados$Longitude)
## Warning: NAs introduced by coercion
# Crear el gráfico
ggplot() +
  geom_polygon(data = world_map, aes(x = long, y = lat, group = group), fill = "lightgray", color = "white") +
  # Graficar los puntos ubicación de cada persona
  geom_point(data = dados, aes(x = Longitude, y = Latitude), alpha = 0.7) +
  labs(title = "R course students", x = "Longitud", y = "Latitud") +
  theme_minimal() +
  theme(legend.position = "bottom")
## Warning: Removed 4 rows containing missing values or values outside the scale range
## (`geom_point()`).

# color por profesión
ggplot() +
  geom_polygon(data = world_map, aes(x = long, y = lat, group = group), fill = "lightgray", color = "white") +
  # Graficar los puntos ubicación de cada persona
  geom_point(data = dados, aes(x = Longitude, y = Latitude, color = Present_Occupation), alpha = 0.7) +
  labs(title = "R course students", x = "Longitud", y = "Latitud") +
  theme_minimal() +
  theme(legend.position = "bottom")
## Warning: Removed 4 rows containing missing values or values outside the scale range
## (`geom_point()`).

# amigable con daltonismo
ggplot() +
  geom_polygon(data = world_map, aes(x = long, y = lat, group = group), fill = "lightgray", color = "white") +
  # Graficar los puntos ubicación de cada persona
  geom_point(data = dados, aes(x = Longitude, y = Latitude, color = Present_Occupation), alpha = 0.7) +
  scale_colour_viridis_d() +
  labs(title = "R course students", x = "Longitud", y = "Latitud") +
  theme_minimal() +
  theme(legend.position = "bottom")
## Warning: Removed 4 rows containing missing values or values outside the scale range
## (`geom_point()`).