Módulo 3: Estructuras de datos en R

Introducción

En el módulo anterior aprendimos que R almacena información en objetos.

Hasta ahora, la mayoría de nuestros objetos contenían solamente un valor:

number_of_cameras <- 12

species <- "Raccoon"

camera_active <- TRUE

Pero las bases de datos reales contienen mucha más información que un solo número o palabra.

R nos permite organizar múltiples valores utilizando diferentes estructuras de datos.

En este módulo nos enfocaremos en cuatro de las más comunes:

  • vectores,
  • matrices,
  • listas,
  • data frames.

Entender la diferencia entre estas estructuras hará mucho más sencillo comprender qué está haciendo R cuando comencemos a trabajar con bases de datos reales.

Comencemos

Abre tu proyecto intro-r-course y crea un nuevo script de R.

Guárdalo dentro de la carpeta scripts con el nombre:

module-03.R

Utilizaremos este script durante todo el módulo.

Vectores

Un vector es una de las estructuras de datos más básicas e importantes en R.

Un vector nos permite almacenar varios valores dentro de un mismo objeto.

Podemos crear un vector utilizando la función:

c()

La c significa combine (combinar).

Por ejemplo:

detections <- c(5, 12, 8, 0, 3)

detections
[1]  5 12  8  0  3

En lugar de crear cinco objetos diferentes:

detection_1 <- 5
detection_2 <- 12
detection_3 <- 8
detection_4 <- 0
detection_5 <- 3

podemos almacenar los cinco valores juntos:

detections <- c(5, 12, 8, 0, 3)

Esto hace mucho más sencillo trabajar con varias observaciones relacionadas.

Vectores numéricos

Los vectores pueden contener valores numéricos:

camera_days <- c(30, 28, 30, 25, 29)

camera_days
[1] 30 28 30 25 29

Podemos realizar operaciones directamente sobre todo el vector:

camera_days + 1
[1] 31 29 31 26 30

o:

camera_days * 2
[1] 60 56 60 50 58

R aplica la operación a cada elemento del vector.

También podemos utilizar funciones:

mean(camera_days)
[1] 28.4
sum(camera_days)
[1] 142
length(camera_days)
[1] 5

La función length() nos indica cuántos elementos contiene el vector.

Vectores de tipo character

Los vectores también pueden contener texto:

species <- c(
  "Raccoon",
  "Coyote",
  "Bobcat",
  "White-tailed deer"
)

species
[1] "Raccoon"           "Coyote"            "Bobcat"           
[4] "White-tailed deer"

Y valores lógicos:

camera_active <- c(TRUE, TRUE, FALSE, TRUE)

camera_active
[1]  TRUE  TRUE FALSE  TRUE
Los vectores contienen un solo tipo de datos

Un vector atómico puede contener solamente un tipo básico de datos.

Por ejemplo, todos sus valores pueden ser numéricos, todos pueden ser de tipo character o todos pueden ser lógicos.

¿Qué ocurre si mezclamos diferentes tipos?

mixed_vector <- c(1, 2, "Raccoon", 4)

mixed_vector
[1] "1"       "2"       "Raccoon" "4"      

R convierte todos los valores a un tipo compatible.

Compruébalo:

class(mixed_vector)
[1] "character"

Como el vector contiene texto, R también convirtió los números en valores de tipo character.

Esta conversión automática se conoce como coerción (coercion).

Tip

Si un objeto que esperabas que fuera numérico repentinamente aparece como character, revisa si alguno de sus valores contiene texto.

Crear secuencias

A veces no queremos escribir cada número individualmente.

Por ejemplo:

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

Podemos guardar esta secuencia como un objeto:

sampling_days <- 1:10

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

La función seq() nos proporciona mayor control. Esta función tiene varios argumentos: from indica el número inicial, to el número final, length.out permite controlar la longitud del vector y by nos permite especificar el intervalo de la secuencia.

seq(from = 0, to = 30, by = 5)
[1]  0  5 10 15 20 25 30

También podemos repetir valores utilizando rep(). El argumento times nos permite especificar el número de veces que se repetirá un valor o vector. Cuando utilizamos each, podemos controlar el número de veces que se repetirá cada valor dentro del vector.

rep("Camera", times = 5)
[1] "Camera" "Camera" "Camera" "Camera" "Camera"

Estas funciones son útiles cuando necesitamos generar valores repetidos o secuenciales sin escribirlos manualmente.

Seleccionar elementos de un vector

A veces solamente queremos uno o algunos valores de un vector.

Podemos seleccionar elementos utilizando corchetes:

[]

Vamos a crear un vector:

detections <- c(5, 12, 8, 0, 3)

Para seleccionar el primer valor:

detections[1]
[1] 5

Para seleccionar el tercero:

detections[3]
[1] 8

Podemos seleccionar varias posiciones:

detections[c(1, 3, 5)]
[1] 5 8 3

O una secuencia de posiciones:

detections[1:3]
[1]  5 12  8
Importante

R comienza a contar desde 1, no desde 0.

El primer elemento de un vector es:

x[1]

También podemos excluir posiciones utilizando números negativos:

detections[-1]
[1] 12  8  0  3

Esto devuelve todos los valores excepto el primero.

Seleccionar valores utilizando una condición

¿Recuerdas los operadores relacionales del módulo anterior?

Podemos utilizarlos con vectores.

Por ejemplo:

detections > 5
[1] FALSE  TRUE  TRUE FALSE FALSE

R devuelve un valor lógico para cada elemento.

Podemos utilizar esta condición dentro de los corchetes:

detections[detections > 5]
[1] 12  8

R devuelve solamente los valores que cumplen la condición.

Esta idea será extremadamente importante más adelante cuando comencemos a filtrar bases de datos.

Inténtalo tú mismo

Crea este vector:

camera_days <- c(30, 21, 29, 15, 30, 27)

Después:

  1. Selecciona el segundo valor.
  2. Selecciona los primeros tres valores.
  3. Selecciona los valores en las posiciones 2, 4 y 6.
  4. Identifica cuáles valores son mayores que 25.
  5. Devuelve solamente los valores mayores que 25.

Matrices

Una matriz es una estructura de datos de dos dimensiones.

A diferencia de un vector, que tiene solamente una dimensión, una matriz tiene:

  • filas,
  • columnas.

Podemos crear una matriz utilizando la función matrix().

Por ejemplo:

camera_matrix <- matrix(
  1:12,
  nrow = 4,
  ncol = 3
)

camera_matrix
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

De manera predeterminada, R llena las matrices por columnas.

Podemos cambiar este comportamiento utilizando:

camera_matrix <- matrix(
  1:12,
  nrow = 4,
  ncol = 3,
  byrow = TRUE
)

camera_matrix
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12

Ahora los valores se llenan por filas.

Dimensiones de una matriz

Podemos revisar las dimensiones de una matriz utilizando:

dim(camera_matrix)
[1] 4 3

Esto devuelve el número de filas y columnas.

También podemos utilizar:

nrow(camera_matrix)
[1] 4

y:

ncol(camera_matrix)
[1] 3

Crear matrices a partir de vectores

Podemos combinar vectores para crear una matriz.

Por ejemplo:

station_1 <- c(5, 2, 0)
station_2 <- c(3, 1, 4)
station_3 <- c(8, 0, 2)

Podemos combinarlos por filas:

detection_matrix <- rbind(
  station_1,
  station_2,
  station_3
)

detection_matrix
          [,1] [,2] [,3]
station_1    5    2    0
station_2    3    1    4
station_3    8    0    2

O por columnas:

cbind(
  station_1,
  station_2,
  station_3
)
     station_1 station_2 station_3
[1,]         5         3         8
[2,]         2         1         0
[3,]         0         4         2

Las funciones son fáciles de recordar:

rbind = row bind

cbind = column bind

Nombrar filas y columnas

Las matrices pueden tener nombres para sus filas y columnas.

Por ejemplo:

rownames(detection_matrix) <- c(
  "CT01",
  "CT02",
  "CT03"
)

colnames(detection_matrix) <- c(
  "Raccoon",
  "Coyote",
  "Bobcat"
)

detection_matrix
     Raccoon Coyote Bobcat
CT01       5      2      0
CT02       3      1      4
CT03       8      0      2

Ahora nuestra matriz es mucho más fácil de interpretar.

Este tipo de estructura nos resultará familiar más adelante cuando trabajemos con historias de detección de cámaras trampa.

Seleccionar elementos de una matriz

Como las matrices tienen dos dimensiones, debemos especificar:

[fila, columna]

Por ejemplo:

detection_matrix[1, 2]
[1] 2

significa:

fila 1, columna 2

Podemos seleccionar una fila completa:

detection_matrix[1, ]
Raccoon  Coyote  Bobcat 
      5       2       0 

O una columna completa:

detection_matrix[, 2]
CT01 CT02 CT03 
   2    1    0 

También podemos utilizar los nombres:

detection_matrix["CT01", "Coyote"]
[1] 2

o:

detection_matrix[, "Raccoon"]
CT01 CT02 CT03 
   5    3    8 
Tip

Una forma útil de recordar cómo seleccionar elementos de una matriz es:

objeto[fila, columna]

La coma separa las dos dimensiones.

También podemos seleccionar los valores de la diagonal utilizando diag:

diag(detection_matrix)
[1] 5 1 2

Eliminar la fila 1 y seleccionar la columna 2:

detection_matrix[-1,2]
CT02 CT03 
   1    0 

Eliminar las filas 1 y 4, y seleccionar las columnas 1 y 3:

detection_matrix[c(-1,-4), c(1,3)]
     Raccoon Bobcat
CT02       3      4
CT03       8      2

Cuando utilizamos los selectores en el lado izquierdo de la definición de un objeto, podemos reemplazar los valores por aquellos que estamos especificando.

# Podemos reemplazar valores en matrices

# Reemplazar la segunda fila con 100, 200 y 300
detection_matrix[2,] <- c(100,200, 300)
detection_matrix
     Raccoon Coyote Bobcat
CT01       5      2      0
CT02     100    200    300
CT03       8      0      2

Las matrices contienen un solo tipo de datos

Al igual que los vectores, las matrices contienen solamente un tipo básico de datos.

Por ejemplo:

numeric_matrix <- matrix(1:9, nrow = 3)

numeric_matrix
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

Pero si introducimos texto:

mixed_matrix <- cbind(
  station = c("CT01", "CT02", "CT03"),
  detections = c(5, 8, 2)
)

mixed_matrix
     station detections
[1,] "CT01"  "5"       
[2,] "CT02"  "8"       
[3,] "CT03"  "2"       

R convierte los valores numéricos a character porque toda la matriz debe compartir un mismo tipo de datos.

Esta limitación es una de las razones por las que los data frames son más útiles para la mayoría de las bases de datos.

Listas

Una lista es una estructura de datos muy flexible en R.

A diferencia de los vectores y matrices, las listas pueden contener objetos de diferentes tipos y estructuras.

Por ejemplo:

camera_information <- list(
  station = "CT01",
  active = TRUE,
  detections = 25,
  species = c("Raccoon", "Coyote", "Bobcat")
)

camera_information
$station
[1] "CT01"

$active
[1] TRUE

$detections
[1] 25

$species
[1] "Raccoon" "Coyote"  "Bobcat" 

Esta única lista contiene:

  • un valor de tipo character,
  • un valor lógico,
  • un valor numérico,
  • un vector de tipo character.

Una lista incluso puede contener matrices, data frames u otras listas.

Por ejemplo:

example_list <- list(
  numbers = c(1, 2, 3),
  matrix = matrix(1:4, nrow = 2),
  message = "Hello R"
)

example_list
$numbers
[1] 1 2 3

$matrix
     [,1] [,2]
[1,]    1    3
[2,]    2    4

$message
[1] "Hello R"

Esta flexibilidad hace que las listas sean extremadamente comunes en R.

Muchas funciones y modelos estadísticos devuelven sus resultados como listas que contienen varios objetos diferentes.

Seleccionar elementos de una lista

Si los elementos tienen nombres, podemos utilizar $:

camera_information$station
[1] "CT01"
camera_information$species
[1] "Raccoon" "Coyote"  "Bobcat" 

También podemos utilizar corchetes dobles:

camera_information[[1]]
[1] "CT01"

o:

camera_information[["station"]]
[1] "CT01"

También puedes encontrar algo como:

camera_information[1]
$station
[1] "CT01"

pero existe una diferencia importante.

camera_information[1] devuelve una lista que contiene el primer elemento.

En cambio:

camera_information[[1]]
[1] "CT01"

devuelve el contenido del primer elemento.

Esta diferencia puede ser confusa al principio y no necesitas memorizarla todavía.

Por ahora, la idea más importante es:

Las listas pueden almacenar muchos tipos diferentes de objetos juntos.

Data frames

Para el tipo de trabajo que realizaremos en este curso, los data frames probablemente serán la estructura de datos más importante.

Un data frame es una tabla rectangular con:

  • filas,
  • columnas.

Se parece mucho a una hoja de cálculo de Excel.

Sin embargo, a diferencia de una matriz, las diferentes columnas de un data frame pueden contener diferentes tipos de datos.

Por ejemplo, vamos a crear información de un muestreo hipotético con cámaras trampa:

station <- c(
  "CT01",
  "CT02",
  "CT03",
  "CT04"
)

habitat <- c(
  "Forest",
  "Forest",
  "Pasture",
  "Pasture"
)

camera_days <- c(
  30,
  28,
  30,
  27
)

active <- c(
  TRUE,
  TRUE,
  FALSE,
  TRUE
)

Cada objeto es un vector.

Ahora podemos combinarlos en un data frame:

camera_data <- data.frame(
  station,
  habitat,
  camera_days,
  active
)

camera_data
  station habitat camera_days active
1    CT01  Forest          30   TRUE
2    CT02  Forest          28   TRUE
3    CT03 Pasture          30  FALSE
4    CT04 Pasture          27   TRUE

Ahora tenemos un solo objeto que contiene varias columnas.

Cada columna puede tener un tipo diferente:

class(camera_data$station)
[1] "character"
class(camera_data$camera_days)
[1] "numeric"
class(camera_data$active)
[1] "logical"

El data frame también tiene su propia clase:

class(camera_data)
[1] "data.frame"

R devuelve:

Las filas y columnas normalmente representan cosas diferentes

En la mayoría de las bases de datos ecológicas:

  • las filas representan observaciones,
  • las columnas representan variables.

Entender esta organización es extremadamente importante al preparar datos para un análisis.

En nuestro ejemplo:

station      habitat      camera_days      active

son variables.

Cada fila representa una estación de cámara.

Inspeccionar un data frame

Antes de analizar una base de datos, siempre deberíamos revisar su estructura.

Algunas funciones útiles son:

camera_data
  station habitat camera_days active
1    CT01  Forest          30   TRUE
2    CT02  Forest          28   TRUE
3    CT03 Pasture          30  FALSE
4    CT04 Pasture          27   TRUE

str()

Muestra la estructura interna del objeto:

str(camera_data)
'data.frame':   4 obs. of  4 variables:
 $ station    : chr  "CT01" "CT02" "CT03" "CT04"
 $ habitat    : chr  "Forest" "Forest" "Pasture" "Pasture"
 $ camera_days: num  30 28 30 27
 $ active     : logi  TRUE TRUE FALSE TRUE

dim()

Devuelve el número de filas y columnas:

dim(camera_data)
[1] 4 4

nrow()

Devuelve el número de filas:

nrow(camera_data)
[1] 4

ncol()

Devuelve el número de columnas:

ncol(camera_data)
[1] 4

names()

Devuelve los nombres de las columnas:

names(camera_data)
[1] "station"     "habitat"     "camera_days" "active"     
Tip

Cuando cargues una nueva base de datos en R, una de las primeras cosas que deberías hacer es inspeccionarla.

Funciones como:

head()
str()
dim()
names()

pueden ayudarte a identificar problemas antes de comenzar un análisis.

Seleccionar columnas de un data frame

Podemos acceder a una columna utilizando $:

camera_data$station
[1] "CT01" "CT02" "CT03" "CT04"
camera_data$camera_days
[1] 30 28 30 27

Observa qué ocurre cuando hacemos esto:

class(camera_data$camera_days)
[1] "numeric"

La columna en sí misma es un vector.

Esta es una idea importante:

Un data frame es esencialmente una colección de vectores de la misma longitud organizados como columnas.

También podemos utilizar corchetes.

Por ejemplo:

camera_data[1, 2]
[1] "Forest"

significa:

fila 1, columna 2

Para seleccionar la primera fila:

camera_data[1, ]
  station habitat camera_days active
1    CT01  Forest          30   TRUE

Para seleccionar la segunda columna:

camera_data[, 2]
[1] "Forest"  "Forest"  "Pasture" "Pasture"

También podemos utilizar los nombres de las columnas:

camera_data[, "habitat"]
[1] "Forest"  "Forest"  "Pasture" "Pasture"

O seleccionar varias columnas:

camera_data[, c("station", "camera_days")]
  station camera_days
1    CT01          30
2    CT02          28
3    CT03          30
4    CT04          27

Más adelante aprenderemos formas más sencillas y legibles de seleccionar filas y columnas utilizando dplyr.

Por ahora, es importante entender cómo funcionan los data frames en R base.

Agregar una columna

Podemos agregar una nueva columna utilizando $.

Por ejemplo:

camera_data$elevation <- c(
  105,
  112,
  98,
  101
)

camera_data
  station habitat camera_days active elevation
1    CT01  Forest          30   TRUE       105
2    CT02  Forest          28   TRUE       112
3    CT03 Pasture          30  FALSE        98
4    CT04 Pasture          27   TRUE       101

La nueva columna debe tener el número correcto de valores.

Nuestro data frame tiene cuatro filas, por lo que la nueva columna necesita cuatro valores.

Podemos comprobar el número de filas utilizando:

nrow(camera_data)
[1] 4

Cambiar valores

También podemos reemplazar valores específicos.

Por ejemplo:

camera_data$camera_days[2] <- 30

camera_data
  station habitat camera_days active elevation
1    CT01  Forest          30   TRUE       105
2    CT02  Forest          30   TRUE       112
3    CT03 Pasture          30  FALSE        98
4    CT04 Pasture          27   TRUE       101

O utilizando posiciones de fila y columna:

camera_data[2, "camera_days"] <- 30

Esto puede ser útil, pero debemos tener cuidado al modificar manualmente nuestros datos originales.

Importante

Siempre que sea posible, evita modificar manualmente tu archivo de datos original.

Generalmente es mejor realizar las correcciones mediante código para que los cambios queden documentados y sean reproducibles.

Diferentes estructuras, diferentes propósitos

Vamos a resumir las cuatro estructuras que hemos visto.

Estructura Dimensiones ¿Mismo tipo de datos? Uso común
Vector 1 Sí Almacenar una secuencia de valores
Matriz 2 Sí Datos numéricos o rectangulares estructurados
Lista 1 No Almacenar diferentes objetos juntos
Data frame 2 Diferentes tipos permitidos por columna Almacenar bases de datos

La estructura que elegimos depende del tipo de información que queremos almacenar.

Durante el resto del curso:

  • trabajaremos constantemente con vectores,
  • la mayoría de nuestras bases de datos serán data frames,
  • ocasionalmente encontraremos matrices,
  • y muchas funciones de R devolverán sus resultados como listas.

Cuando finalmente trabajemos con camtrapR, estas cuatro ideas volverán a aparecer.

Convertir entre estructuras de datos

R también proporciona funciones para convertir objetos de una estructura a otra.

Por ejemplo:

as.data.frame(detection_matrix)
     Raccoon Coyote Bobcat
CT01       5      2      0
CT02     100    200    300
CT03       8      0      2

convierte una matriz en un data frame.

Podemos guardar el resultado:

detection_data <- as.data.frame(detection_matrix)

class(detection_data)
[1] "data.frame"

De manera similar:

as.matrix(camera_data)
     station habitat   camera_days active  elevation
[1,] "CT01"  "Forest"  "30"        "TRUE"  "105"    
[2,] "CT02"  "Forest"  "30"        "TRUE"  "112"    
[3,] "CT03"  "Pasture" "30"        "FALSE" " 98"    
[4,] "CT04"  "Pasture" "27"        "TRUE"  "101"    

intenta convertir un data frame en una matriz.

Sin embargo, recuerda que una matriz solamente puede contener un tipo básico de datos.

Como camera_data contiene texto, números y valores lógicos, R necesitará convertir todo a un tipo común.

Prueba:

camera_matrix_2 <- as.matrix(camera_data)

camera_matrix_2
     station habitat   camera_days active  elevation
[1,] "CT01"  "Forest"  "30"        "TRUE"  "105"    
[2,] "CT02"  "Forest"  "30"        "TRUE"  "112"    
[3,] "CT03"  "Pasture" "30"        "FALSE" " 98"    
[4,] "CT04"  "Pasture" "27"        "TRUE"  "101"    

y después:

class(camera_matrix_2)
[1] "matrix" "array" 

Este es otro buen ejemplo de por qué es importante entender las estructuras de datos.

Una función puede convertir técnicamente un objeto, pero el resultado puede no tener la estructura o el tipo de datos que esperabas.

Práctica en casa

Tip

Esta actividad debería tomar aproximadamente 15–20 minutos.

Abre tu proyecto intro-r-course y crea un nuevo script llamado:

practice-03.R

Guárdalo dentro de la carpeta scripts.

Parte 1: Vectores

Crea estos objetos:

stations <- c("CT01", "CT02", "CT03", "CT04", "CT05")

detections <- c(12, 5, 0, 18, 7)

camera_days <- c(30, 28, 30, 25, 29)

Después:

  1. Devuelve el tercer elemento de stations.
  2. Devuelve los primeros tres valores de detections.
  3. Determina cuáles valores de detections son mayores que 10.
  4. Devuelve solamente las detecciones mayores que 10.
  5. Calcula el promedio del número de días-cámara.

Parte 2: Data frame

Crea un data frame llamado survey_data utilizando:

stations
[1] "CT01" "CT02" "CT03" "CT04" "CT05"
detections
[1] 12  5  0 18  7
camera_days
[1] 30 28 30 25 29

Después:

  1. Muestra el data frame.
  2. Utiliza str() para inspeccionar su estructura.
  3. Utiliza dim() para encontrar sus dimensiones.
  4. Utiliza names() para obtener los nombres de las columnas.
  5. Selecciona solamente la columna detections.
  6. Selecciona la información correspondiente a la segunda estación de cámara.
  7. Agrega una nueva columna llamada habitat que contenga:
c("Forest", "Forest", "Pasture", "Forest", "Pasture")
[1] "Forest"  "Forest"  "Pasture" "Forest"  "Pasture"
  1. Utiliza nuevamente str() y observa el tipo de cada columna.
# Práctica del Módulo 3

# ---------------------------
# Parte 1: Vectores
# ---------------------------

stations <- c(
  "CT01",
  "CT02",
  "CT03",
  "CT04",
  "CT05"
)

detections <- c(
  12,
  5,
  0,
  18,
  7
)

camera_days <- c(
  30,
  28,
  30,
  25,
  29
)

# Tercera estación
stations[3]
[1] "CT03"
# Primeros tres valores de detecciones
detections[1:3]
[1] 12  5  0
# ¿Cuáles valores son mayores que 10?
detections > 10
[1]  TRUE FALSE FALSE  TRUE FALSE
# Devolver solamente detecciones mayores que 10
detections[detections > 10]
[1] 12 18
# Promedio de días-cámara
mean(camera_days)
[1] 28.4
# ---------------------------
# Parte 2: Data frame
# ---------------------------

survey_data <- data.frame(
  stations,
  detections,
  camera_days
)

# Mostrar los datos
survey_data
  stations detections camera_days
1     CT01         12          30
2     CT02          5          28
3     CT03          0          30
4     CT04         18          25
5     CT05          7          29
# Inspeccionar la estructura
str(survey_data)
'data.frame':   5 obs. of  3 variables:
 $ stations   : chr  "CT01" "CT02" "CT03" "CT04" ...
 $ detections : num  12 5 0 18 7
 $ camera_days: num  30 28 30 25 29
# Dimensiones
dim(survey_data)
[1] 5 3
# Nombres de las columnas
names(survey_data)
[1] "stations"    "detections"  "camera_days"
# Seleccionar detecciones
survey_data$detections
[1] 12  5  0 18  7
# Seleccionar la segunda estación de cámara
survey_data[2, ]
  stations detections camera_days
2     CT02          5          28
# Agregar hábitat
survey_data$habitat <- c(
  "Forest",
  "Forest",
  "Pasture",
  "Forest",
  "Pasture"
)

# Inspeccionar el data frame actualizado
str(survey_data)
'data.frame':   5 obs. of  4 variables:
 $ stations   : chr  "CT01" "CT02" "CT03" "CT04" ...
 $ detections : num  12 5 0 18 7
 $ camera_days: num  30 28 30 25 29
 $ habitat    : chr  "Forest" "Forest" "Pasture" "Forest" ...

Lo importante es que entiendas la diferencia entre:

  • un vector,
  • una matriz,
  • una lista,
  • y un data frame,

y que puedas reconocer cómo se utilizan filas, columnas y posiciones para acceder a la información.