1 Bienvenida y propósito

Esta clase introduce el entorno de trabajo que utilizaremos para programar, organizar, analizar y comunicar información económica y social con R. El objetivo no es memorizar comandos aislados, sino comenzar a construir una forma de trabajo reproducible: formular una pregunta, representar la información de manera adecuada, ejecutar transformaciones y documentar lo realizado.

Al finalizar este material deberías poder:

  • distinguir entre R, RStudio y Posit;
  • reconocer los componentes principales de RStudio y trabajar dentro de un proyecto;
  • comprender la lógica de un documento R Markdown;
  • ejecutar operaciones básicas y crear objetos en R;
  • identificar tipos y estructuras de datos;
  • inspeccionar, seleccionar, filtrar, transformar y resumir una base sencilla;
  • entender la lógica de una unión entre tablas;
  • reconocer la diferencia entre describir, explicar, predecir y sostener una afirmación causal.

1.1 Panorama del curso

El curso avanza desde los fundamentos de programación y manipulación de datos hacia herramientas de análisis más amplias. El recorrido incluye organización y transformación de información, visualización, calidad de datos, construcción de indicadores, métodos estadísticos, aprendizaje automático, aplicaciones de inteligencia artificial y comunicación reproducible de resultados.

En esta primera clase nos concentraremos en los fundamentos necesarios para que los contenidos posteriores puedan construirse sobre una base de código clara y ordenada. No estimaremos modelos econométricos ni desarrollaremos algoritmos avanzados en este documento.

1.2 Ruta de aprendizaje

  1. R en el análisis económico y social.
  2. Ciencia de Datos y tipos de preguntas empíricas.
  3. R, RStudio, Posit e instalación.
  4. Interfaz, proyectos, scripts y ayuda.
  5. R Markdown y reproducibilidad.
  6. Primeros comandos, objetos y tipos de datos.
  7. Vectores, factores, matrices y listas.
  8. Data frames, tibbles y la base simulada hogares.
  9. Paquetes, tidyverse y manipulación con dplyr.
  10. Importación, exportación, agregación y joins.
  11. Práctica integradora y cierre conceptual.

1.3 Pregunta diagnóstica

Pregunta. Imagina que recibes una base con información de hogares de varios municipios: ingresos, gastos, número de integrantes, zona de residencia y acceso a subsidios. ¿Qué tareas debería realizar una persona analista antes de presentar resultados?

Pista: piensa en la pregunta de investigación, la estructura de la base, la calidad de los datos, las transformaciones necesarias, el análisis y la comunicación.

Ver una respuesta posible

Una secuencia razonable sería: definir la pregunta, identificar la unidad de observación, inspeccionar variables y valores faltantes, verificar consistencia, transformar variables cuando sea necesario, producir estadísticas o modelos coherentes con la pregunta y documentar cada decisión. La programación permite que ese flujo pueda repetirse y auditarse.

2 R para Economía y Ciencias Sociales

R es especialmente útil cuando el análisis requiere combinar datos, estadística, programación y comunicación. Algunos ejemplos son:

  • construir indicadores de ingreso, pobreza, empleo o desempeño académico;
  • procesar encuestas y registros administrativos;
  • comparar municipios, hogares, empresas o grupos poblacionales;
  • automatizar reportes que se actualizan al cambiar los datos;
  • producir visualizaciones y tableros;
  • desarrollar análisis econométricos y modelos predictivos;
  • documentar una investigación de forma reproducible.
Idea clave. Aprender R no consiste solamente en aprender sintaxis. Consiste en aprender a convertir una pregunta económica o social en un flujo de análisis transparente y verificable.

2.1 Una introducción a la Ciencia de Datos

La Ciencia de Datos integra varias capacidades:

  • preguntar: traducir un problema sustantivo en una pregunta analítica;
  • obtener y organizar: trabajar con información proveniente de distintas fuentes;
  • transformar: convertir datos crudos en variables útiles para el análisis;
  • explorar: identificar patrones, distribuciones, faltantes e inconsistencias;
  • analizar o modelar: usar herramientas estadísticas, econométricas o predictivas;
  • comunicar: presentar resultados comprensibles y reproducibles.

En este curso, R será el lenguaje que conecte esas etapas.

2.2 Descripción, explicación, predicción y causalidad

Estas preguntas no son equivalentes.

Enfoque Pregunta típica Ejemplo económico o social Precaución
Descripción ¿Qué observamos? ¿Cuál es el ingreso promedio por zona? Resume patrones observados.
Explicación o asociación ¿Qué variables se relacionan con un resultado? ¿Cómo se relaciona el nivel educativo con el salario? Una asociación no implica causalidad.
Predicción ¿Qué valor esperamos para una observación nueva? ¿Qué hogares presentan mayor probabilidad estimada de deserción de un programa? Importa el desempeño fuera de la muestra usada para ajustar el modelo.
Causalidad ¿Qué habría ocurrido bajo una intervención alternativa? ¿Cuál es el efecto de un programa sobre un resultado? Requiere diseño, supuestos y métodos que permitan identificar un efecto causal.

3 ¿Qué son R, RStudio y Posit?

3.1 R: lenguaje y motor de cálculo

R es un lenguaje de programación y un entorno libre para computación estadística y gráficos. Puede ejecutar cálculos, manipular datos, estimar modelos, producir gráficos y automatizar tareas.

R puede utilizarse sin RStudio. El motor que evalúa instrucciones es R.

3.2 RStudio: entorno de desarrollo integrado

RStudio es un IDE (Integrated Development Environment): una aplicación que reúne editor de código, consola, archivos, gráficos, ayuda, paquetes y herramientas de proyecto en una misma interfaz.

Instalar RStudio no sustituye la instalación de R. Para el flujo habitual de este curso instalaremos primero R y luego RStudio.

3.3 Posit: la organización

Posit es la organización que desarrolla RStudio y otras herramientas de ciencia de datos y publicación técnica. Por eso es común encontrar documentación de RStudio alojada en dominios de Posit.

Elemento Función principal
R Lenguaje y motor de cálculo.
RStudio Interfaz de desarrollo para trabajar de forma más organizada con R.
Posit Organización que desarrolla RStudio y otras herramientas.

3.4 Instalación general

3.4.1 Windows

  1. Descargar e instalar R desde CRAN.
  2. Descargar e instalar RStudio Desktop desde Posit.
  3. Abrir RStudio y comprobar que la consola de R está disponible.

3.4.2 macOS

  1. Descargar el instalador de R compatible con el equipo desde CRAN.
  2. Instalar R.
  3. Descargar e instalar RStudio Desktop desde Posit.
  4. Abrir RStudio y comprobar que reconoce la instalación de R.

Fuentes oficiales:

Error frecuente. Instalar RStudio y asumir que R ya está instalado. RStudio ofrece la interfaz; R proporciona el motor de ejecución.

4 Reconociendo RStudio

La disposición exacta puede cambiar según la configuración, pero normalmente encontrarás varios paneles.

4.1 Source o editor

Aquí se abren archivos como:

  • scripts .R, que guardan código;
  • archivos .Rmd, que combinan texto, código y resultados;
  • otros archivos de texto asociados a un proyecto.
Herramienta Para qué sirve Qué conviene recordar
Consola Ejecutar instrucciones directamente. Es útil para explorar, pero no sustituye un archivo de trabajo.
Script .R Guardar y organizar código. Conserva instrucciones para volver a ejecutarlas.
Archivo .Rmd Integrar texto, código y resultados. Permite construir documentos reproducibles.

4.2 Console

La consola ejecuta instrucciones. Es útil para probar comandos, pero lo escrito allí no constituye por sí mismo un registro organizado del análisis.

2 + 2
## [1] 4

Resultado esperado: R devuelve 4.

4.3 Environment

El panel Environment muestra objetos creados en la sesión de R: vectores, bases, matrices, listas y otros objetos.

4.4 Files, Plots, Packages y Help

  • Files: permite navegar por los archivos del proyecto.
  • Plots: muestra gráficos producidos por R.
  • Packages: ayuda a revisar paquetes instalados o cargados.
  • Help: muestra documentación de funciones y paquetes.

Para consultar la ayuda de una función:

?sqrt
help("mean")

4.5 Atajos útiles de RStudio

Acción Windows/Linux macOS
Ejecutar línea o selección Ctrl + Enter Cmd + Enter
Insertar chunk en R Markdown Ctrl + Alt + I Cmd + Option + I
Renderizar/Knit Ctrl + Shift + K Cmd + Shift + K
Insertar <- Alt + - Option + -
Ir al editor Ctrl + 1 Cmd + 1
Ir a la consola Ctrl + 2 Cmd + 2

5 Proyectos de RStudio y rutas reproducibles

Un proyecto de RStudio organiza archivos relacionados con un análisis dentro de una carpeta de proyecto. Esto facilita trabajar con rutas relativas y evita depender de carpetas particulares de un computador.

Una estructura sencilla podría ser:

mi_proyecto/
├── mi_proyecto.Rproj
├── datos/
├── salidas/
└── analisis.Rmd

Si analisis.Rmd está en la raíz del proyecto, una ruta como:

"datos/encuesta.csv"

se interpreta desde la carpeta del proyecto.

Buena práctica: evita escribir rutas personales completas y evita depender de setwd() dentro de los scripts. Los proyectos permiten que el análisis sea más portable.

Para crear un proyecto en RStudio puedes usar File > New Project… y elegir una carpeta nueva o existente.

6 R Markdown y reproducibilidad

6.1 ¿Qué es R Markdown?

R Markdown es un formato de autoría que integra en un mismo archivo:

  1. metadatos en YAML;
  2. texto escrito con Markdown;
  3. código ejecutable organizado en chunks;
  4. resultados generados por el propio código.

Un archivo .Rmd puede convertirse, entre otros formatos, en HTML, Word o PDF. En esta clase utilizamos HTML porque permite navegación, código, tablas y soluciones desplegables de forma cómoda.

6.2 ¿Qué significa reproducibilidad?

Un análisis es reproducible cuando otra persona —o la misma persona en otro equipo— puede partir del archivo, los datos y las dependencias necesarias y reconstruir los resultados siguiendo el mismo procedimiento.

Reproducibilidad implica, entre otras cosas:

  • guardar el código;
  • documentar decisiones;
  • evitar pasos manuales innecesarios;
  • usar rutas organizadas;
  • hacer explícitas las transformaciones;
  • poder volver a generar tablas y resultados a partir de las instrucciones del documento.

6.3 ¿Qué hacen rmarkdown, knitr y Pandoc?

De forma simplificada:

  1. rmarkdown coordina el proceso de renderizado y el formato de salida.
  2. knitr ejecuta los chunks y combina código, resultados y texto.
  3. Pandoc convierte el documento intermedio al formato final solicitado.

La idea puede representarse así:

archivo .Rmd
     ↓
   knitr
     ↓
Markdown intermedio
     ↓
  Pandoc
     ↓
HTML / Word / PDF / otros formatos

6.4 Ejecutar no es lo mismo que renderizar

  • Ejecutar una línea: envía una instrucción seleccionada a la consola.
  • Ejecutar un chunk: ejecuta el bloque de código correspondiente.
  • Renderizar/Knit: procesa el documento completo siguiendo el orden del archivo y genera la salida definida en YAML.
Idea clave. Que un chunk funcione de forma aislada no garantiza que el documento completo sea reproducible. Al renderizar, los objetos deben crearse en el orden correcto.

6.5 YAML: la cabecera del documento

El YAML se encuentra entre dos líneas --- al inicio del archivo. En este documento define título, autor, idioma y opciones de salida.

---
title: "Clase 1: Introducción a R, RStudio y R Markdown"
subtitle: "Programación y análisis de datos económicos y sociales con R"
author: "Brayan David Castro Rodríguez"
date: "2026-09-10"
lang: es
output:
  html_document:
    toc: true
    toc_depth: 3
    toc_float: true
    number_sections: true
    theme: flatly
    highlight: tango
    self_contained: true
---

La expresión 2026-09-10 inserta la fecha del sistema al renderizar. self_contained: true busca producir un HTML que incorpore sus recursos en el propio archivo de salida.

6.6 Markdown básico

6.6.1 Títulos y subtítulos

# Título principal
## Subtítulo
### Nivel siguiente

6.6.2 Énfasis

**negrita**
*cursiva*
`código en línea`

Resultado visual: negrita, cursiva y código en línea.

6.6.3 Listas

- Elemento
- Elemento

1. Primer elemento
2. Segundo elemento

6.6.4 Enlaces

[Texto del enlace](https://www.r-project.org/)

6.6.5 Imágenes

La sintaxis general es:

![Texto alternativo](figuras/mi_imagen.png)

La ruta debe apuntar a un archivo que realmente exista. Usar una ruta relativa al proyecto facilita compartir el análisis.

6.6.6 Tabla sencilla en Markdown

| Variable | Significado |
|---|---|
| ingreso | Recursos monetarios del hogar |
| gasto | Erogaciones del hogar |
| zona | Clasificación territorial |
Variable Significado
ingreso Recursos monetarios del hogar
gasto Erogaciones del hogar
zona Clasificación territorial

6.6.7 Código R en línea

R Markdown permite insertar un resultado pequeño dentro de una frase. Por ejemplo, el valor de \(2^5\) es 32.

6.6.8 Fórmulas con LaTeX

Una expresión en línea puede escribirse como \(A_i = Y_i - G_i\), donde \(A_i\) representa ahorro, \(Y_i\) ingreso y \(G_i\) gasto.

También podemos usar una ecuación centrada:

\[
\pi_t = 100\left(\frac{P_t-P_{t-1}}{P_{t-1}}\right),
\]

\[ \pi_t = 100\left(\frac{P_t-P_{t-1}}{P_{t-1}}\right), \]

que representa una tasa de variación porcentual de un índice de precios entre dos observaciones consecutivas.

6.7 Chunks de código

Un chunk es un bloque ejecutable. En este mismo archivo cada bloque comienza con una cabecera de R, contiene instrucciones y termina con el cierre correspondiente.

Los chunks pueden tener un nombre único. Nombrarlos ayuda a identificar errores y organizar documentos extensos.

Dentro de un chunk, # inicia un comentario de R:

# Este comentario explica la instrucción siguiente.
valor_ejemplo <- 1250
valor_ejemplo * 2
## [1] 2500

6.8 Opciones de chunks

Opción Qué controla
echo Si el código aparece en la salida.
eval Si el código se ejecuta.
include Si código y resultados se incorporan a la salida.
results Cómo se muestran los resultados de texto.
message Si se muestran mensajes generados por R o paquetes.
warning Si se muestran advertencias.
fig.width Ancho de figuras.
fig.height Alto de figuras.
fig.align Alineación de figuras.

6.8.1 echo = FALSE: ejecuta, pero oculta el código en el HTML

## [1] 25

El resultado se muestra, aunque el código no aparece en el documento renderizado.

6.8.2 eval = FALSE: muestra el código, pero no lo ejecuta

objeto_no_creado <- 50 * 3
objeto_no_creado

Es útil para enseñar una instrucción que no queremos ejecutar.

6.8.3 include = FALSE: ejecuta y oculta código y resultados

El objeto valor_interno existe después del chunk, pero el chunk no aparece en el HTML.

valor_interno
## [1] 56

6.8.4 results = "hide": ejecuta, muestra el código y oculta su salida de texto

resultado_guardado <- sqrt(144)
resultado_guardado

El cálculo sí ocurrió:

resultado_guardado
## [1] 12

6.8.5 message y warning

message("Mensaje de demostración")
warning("Advertencia de demostración")
"El resultado ordinario sí permanece visible"
## [1] "El resultado ordinario sí permanece visible"

Estas opciones ayudan a evitar que mensajes técnicos secundarios recarguen un informe. No deben utilizarse para ignorar problemas que deberían corregirse.

6.8.6 Tamaño y alineación de figuras

El siguiente ejemplo solo ilustra opciones gráficas; la visualización de datos se estudiará más adelante.

barplot(
  c(42, 36, 51),
  names.arg = c("Municipio A", "Municipio B", "Municipio C"),
  ylab = "Indicador simulado"
)

7 Primeros comandos en R

7.1 R como calculadora

10 + 5
## [1] 15
10 - 5
## [1] 5
10 * 5
## [1] 50
10 / 5
## [1] 2
2^4
## [1] 16
(10 + 5) / 3
## [1] 5

Funciones matemáticas frecuentes:

sqrt(81)
## [1] 9
log(10)
## [1] 2.302585
exp(1)
## [1] 2.718282
round(10 / 3, 2)
## [1] 3.33
17 %% 5
## [1] 2
17 %/% 5
## [1] 3
  • %% devuelve el residuo de una división.
  • %/% devuelve la parte entera del cociente.

7.1.1 Inténtalo tú: calculadora

Enunciado. Un hogar tiene ingreso mensual de 3.200.000 y gasto mensual de 2.450.000. Calcula el ahorro y la proporción del ingreso destinada al gasto.

Pista. Ahorro = ingreso − gasto. La proporción de gasto se obtiene dividiendo gasto entre ingreso.

# Escribe aquí tu solución.
Ver solución
ingreso_ejercicio <- 3200000
gasto_ejercicio <- 2450000

ahorro_ejercicio <- ingreso_ejercicio - gasto_ejercicio
proporcion_gasto <- gasto_ejercicio / ingreso_ejercicio

ahorro_ejercicio
## [1] 750000
round(proporcion_gasto, 3)
## [1] 0.766

Explicación. Primero se guardan ingreso y gasto en objetos. Después se construyen dos resultados derivados.

Interpretación. El ahorro calculado es una identidad contable para este ejemplo. La proporción de gasto describe cuánto representa el gasto frente al ingreso; no explica por qué el hogar gasta esa proporción.

8 Objetos y asignación

Un objeto permite guardar un valor para reutilizarlo. En R utilizaremos principalmente <- para asignar.

salario_mensual <- 2800000
tasa_ahorro <- 0.12
ahorro_estimado <- salario_mensual * tasa_ahorro

ahorro_estimado
## [1] 336000

8.1 Reasignación

Un objeto puede recibir un valor nuevo:

salario_mensual <- 3000000
salario_mensual
## [1] 3000000

8.2 Mayúsculas y minúsculas importan

ingreso <- 2500000
Ingreso <- 2700000

ingreso
## [1] 2500000
Ingreso
## [1] 2700000

ingreso e Ingreso son objetos distintos.

8.3 Nombres claros con snake_case

Preferiremos nombres como:

ingreso_mensual
gasto_total
numero_personas
tasa_desempleo

Evita nombres ambiguos y evita crear objetos con nombres de funciones fundamentales como c, mean, data, matrix o list.

8.4 Ver y eliminar objetos

ls()
##  [1] "ahorro_ejercicio"   "ahorro_estimado"    "gasto_ejercicio"   
##  [4] "ingreso"            "Ingreso"            "ingreso_ejercicio" 
##  [7] "proporcion_gasto"   "resultado_echo"     "resultado_guardado"
## [10] "salario_mensual"    "tasa_ahorro"        "valor_ejemplo"     
## [13] "valor_interno"

ls() muestra los nombres de objetos existentes.

rm() elimina objetos. Conviene usarlo de manera deliberada, no como sustituto de una organización adecuada.

objeto_temporal <- 999
rm(objeto_temporal)
exists("objeto_temporal")
## [1] FALSE

El panel Environment permite observar estos cambios de forma gráfica.

9 Tipos de datos y valores faltantes

9.1 double e integer

En R, un número escrito como 3 normalmente es de tipo double. Para crear explícitamente un entero se utiliza el sufijo L.

numero_decimal <- 3
numero_entero <- 3L

typeof(numero_decimal)
## [1] "double"
typeof(numero_entero)
## [1] "integer"
class(numero_decimal)
## [1] "numeric"
class(numero_entero)
## [1] "integer"

9.2 character, logical y Date

municipio_ejemplo <- "Cali"
recibe_apoyo <- TRUE
fecha_registro <- as.Date("2026-09-01")

class(municipio_ejemplo)
## [1] "character"
class(recibe_apoyo)
## [1] "logical"
class(fecha_registro)
## [1] "Date"

9.3 Comprobaciones de tipo

is.numeric(numero_decimal)
## [1] TRUE
is.character(municipio_ejemplo)
## [1] TRUE
is.logical(recibe_apoyo)
## [1] TRUE

9.4 Valores faltantes: NA

NA representa un valor faltante. No debe confundirse con cero, con una cadena vacía ni con la palabra "NA".

ingresos_demo <- c(2100000, 2600000, NA, 3400000)

is.na(ingresos_demo)
## [1] FALSE FALSE  TRUE FALSE
mean(ingresos_demo)
## [1] NA
mean(ingresos_demo, na.rm = TRUE)
## [1] 2700000

Sin na.rm = TRUE, muchas funciones devuelven NA si encuentran faltantes.

Técnicamente, NA puede adoptar un tipo compatible con el vector que lo contiene. R también dispone de formas tipadas como NA_real_ o NA_character_.

9.5 Coerción automática

Los vectores atómicos almacenan elementos de un tipo compatible. Si mezclamos números y texto, R puede convertir todo a texto.

vector_mixto <- c(1, 2, "3")
vector_mixto
## [1] "1" "2" "3"
typeof(vector_mixto)
## [1] "character"
Error frecuente. Suponer que un número escrito entre comillas sigue siendo numérico. "2500" es texto; 2500 es numérico.

9.5.1 Inténtalo tú: tipos y faltantes

Enunciado. Crea un vector con cuatro tasas de desempleo simuladas: 9.2, 10.1, un valor faltante y 8.7. Identifica los faltantes y calcula el promedio ignorándolos.

Pista. Usa c(), is.na() y mean(..., na.rm = TRUE).

# Escribe aquí tu solución.
Ver solución
tasas_desempleo <- c(9.2, 10.1, NA, 8.7)

is.na(tasas_desempleo)
## [1] FALSE FALSE  TRUE FALSE
mean(tasas_desempleo, na.rm = TRUE)
## [1] 9.333333

Explicación. is.na() produce un vector lógico que marca la posición faltante. na.rm = TRUE indica que el resumen se calcule con los valores disponibles.

Interpretación. El promedio describe únicamente las observaciones no faltantes. Antes de interpretar una base real habría que estudiar por qué faltan datos; en esta clase no realizaremos imputación.

10 Operadores en R

10.1 Aritméticos

8 + 2
## [1] 10
8 - 2
## [1] 6
8 * 2
## [1] 16
8 / 2
## [1] 4
8^2
## [1] 64

10.2 Relacionales

3000000 > 2500000
## [1] TRUE
3000000 >= 3000000
## [1] TRUE
3000000 == 3000000
## [1] TRUE
3000000 != 2500000
## [1] TRUE

10.3 Lógicos

ingreso_alto <- TRUE
zona_urbana <- FALSE

ingreso_alto & zona_urbana
## [1] FALSE
ingreso_alto | zona_urbana
## [1] TRUE
!zona_urbana
## [1] TRUE

Estos operadores serán esenciales para filtrar observaciones.

11 Funciones básicas y argumentos

Una función recibe uno o más argumentos, realiza una operación y devuelve un resultado. Su forma general puede pensarse como nombre_funcion(argumentos).

valores_sociales <- c(12, 15, 11, 14)

mean(valores_sociales)
## [1] 13
round(mean(valores_sociales), digits = 1)
## [1] 13
length(valores_sociales)
## [1] 4

Los argumentos pueden pasarse por posición o por nombre. Escribir nombres como digits = 1 o na.rm = TRUE suele mejorar la legibilidad cuando el significado del argumento no es evidente.

Para aprender una función, combina tres preguntas: ¿qué recibe?, ¿qué devuelve? y ¿qué argumentos cambian su comportamiento? La ayuda oficial disponible dentro de R es una referencia habitual.

12 Vectores

Un vector es una estructura unidimensional. En un vector atómico, sus elementos comparten un tipo compatible.

12.1 Crear vectores

salarios <- c(1900000, 2400000, 3100000, 2750000)
secuencia_simple <- 1:5
secuencia_pares <- seq(2, 10, by = 2)
zonas_repetidas <- rep(c("Urbana", "Rural"), each = 2)

salarios
## [1] 1900000 2400000 3100000 2750000
secuencia_simple
## [1] 1 2 3 4 5
secuencia_pares
## [1]  2  4  6  8 10
zonas_repetidas
## [1] "Urbana" "Urbana" "Rural"  "Rural"

12.2 Longitud e indexación

R comienza a indexar en 1.

length(salarios)
## [1] 4
salarios[1]
## [1] 1900000
salarios[c(1, 3)]
## [1] 1900000 3100000
salarios[-2]
## [1] 1900000 3100000 2750000
salarios[salarios > 2500000]
## [1] 3100000 2750000
  • índice positivo: conserva posiciones;
  • índice negativo: excluye posiciones;
  • índice lógico: conserva elementos que cumplen una condición.

12.3 Operaciones vectorizadas

salarios * 1.05
## [1] 1995000 2520000 3255000 2887500
salarios - 500000
## [1] 1400000 1900000 2600000 2250000

R aplica la operación a cada elemento sin requerir un ciclo explícito.

12.4 Reciclaje de vectores

base_corta <- c(100, 200, 300, 400)
ajuste_corto <- c(10, 20)
base_corta + ajuste_corto
## [1] 110 220 310 420

R reutiliza c(10, 20) hasta alcanzar la longitud del vector mayor. El reciclaje puede ser útil, pero también producir resultados no deseados si las longitudes no corresponden a la lógica del problema.

12.5 Resúmenes numéricos

ingresos_con_na <- c(2200000, 2650000, 2900000, NA, 4100000)

sum(ingresos_con_na, na.rm = TRUE)
## [1] 11850000
mean(ingresos_con_na, na.rm = TRUE)
## [1] 2962500
median(ingresos_con_na, na.rm = TRUE)
## [1] 2775000
min(ingresos_con_na, na.rm = TRUE)
## [1] 2200000
max(ingresos_con_na, na.rm = TRUE)
## [1] 4100000
sd(ingresos_con_na, na.rm = TRUE)
## [1] 811762.5

12.5.1 Inténtalo tú: vectores

Enunciado. Con el vector gastos <- c(1500000, 2100000, 1950000, 2800000, 2300000), obtén: el tercer valor, todos los valores mayores a 2.000.000 y el gasto mediano.

Pista. Usa [ ], una condición lógica y median().

# Escribe aquí tu solución.
Ver solución
gastos <- c(1500000, 2100000, 1950000, 2800000, 2300000)

gastos[3]
## [1] 1950000
gastos[gastos > 2000000]
## [1] 2100000 2800000 2300000
median(gastos)
## [1] 2100000

Explicación. La indexación puede basarse en una posición concreta o en una condición. median() resume el centro de la distribución y es menos sensible a valores extremos que la media.

Interpretación. Estas operaciones describen el conjunto de gastos proporcionado; no permiten inferir por sí solas patrones de una población más amplia.

13 Factores

Los factores representan variables categóricas con un conjunto definido de niveles.

13.1 Factor nominal

condicion_laboral <- factor(
  c("Ocupado", "Desocupado", "Ocupado", "Inactivo", "Ocupado")
)

condicion_laboral
## [1] Ocupado    Desocupado Ocupado    Inactivo   Ocupado   
## Levels: Desocupado Inactivo Ocupado
levels(condicion_laboral)
## [1] "Desocupado" "Inactivo"   "Ocupado"
summary(condicion_laboral)
## Desocupado   Inactivo    Ocupado 
##          1          1          3

En una variable nominal no asumimos un orden natural entre categorías.

13.2 Factor ordenado

nivel_educativo <- factor(
  c("Secundaria", "Universitaria", "Primaria", "Posgrado", "Universitaria"),
  levels = c("Primaria", "Secundaria", "Universitaria", "Posgrado"),
  ordered = TRUE
)

nivel_educativo
## [1] Secundaria    Universitaria Primaria      Posgrado      Universitaria
## Levels: Primaria < Secundaria < Universitaria < Posgrado
levels(nivel_educativo)
## [1] "Primaria"      "Secundaria"    "Universitaria" "Posgrado"
summary(nivel_educativo)
##      Primaria    Secundaria Universitaria      Posgrado 
##             1             1             2             1

Aquí los niveles sí siguen un orden conceptual definido explícitamente.

Error frecuente. Asumir que el orden alfabético de las etiquetas coincide con el orden sustantivo de una variable. Cuando el orden importa, debe definirse de manera explícita.

14 Matrices

Una matriz es una estructura bidimensional homogénea: todos sus elementos deben ser de un tipo compatible.

matriz_indicadores <- matrix(
  c(12, 8, 15, 10, 9, 11),
  nrow = 3,
  byrow = TRUE
)

rownames(matriz_indicadores) <- c("Municipio A", "Municipio B", "Municipio C")
colnames(matriz_indicadores) <- c("Indicador 1", "Indicador 2")

matriz_indicadores
##             Indicador 1 Indicador 2
## Municipio A          12           8
## Municipio B          15          10
## Municipio C           9          11
nrow(matriz_indicadores)
## [1] 3
ncol(matriz_indicadores)
## [1] 2
dim(matriz_indicadores)
## [1] 3 2

14.1 Indexación matricial

La sintaxis general es [fila, columna].

matriz_indicadores[1, 2]
## [1] 8
matriz_indicadores[2, ]
## Indicador 1 Indicador 2 
##          15          10
matriz_indicadores[, 1]
## Municipio A Municipio B Municipio C 
##          12          15           9

14.2 * frente a %*%

matriz_a <- matrix(c(1, 2, 3, 4), nrow = 2, byrow = TRUE)
matriz_b <- matrix(c(5, 6, 7, 8), nrow = 2, byrow = TRUE)

matriz_a * matriz_b
##      [,1] [,2]
## [1,]    5   12
## [2,]   21   32
matriz_a %*% matriz_b
##      [,1] [,2]
## [1,]   19   22
## [2,]   43   50
  • * multiplica elemento a elemento.
  • %*% realiza multiplicación matricial.

En Econometría, las matrices permiten representar de forma compacta variables explicativas, parámetros y operaciones algebraicas. Aquí solo necesitamos reconocer su estructura; la estimación de modelos se abordará después.

15 Listas

Una lista puede almacenar objetos de tipos y tamaños diferentes.

resultado_ejemplo <- list(
  nombre = "Resumen social",
  valores = c(12.4, 15.1, 13.8),
  aprobado = TRUE
)

resultado_ejemplo
## $nombre
## [1] "Resumen social"
## 
## $valores
## [1] 12.4 15.1 13.8
## 
## $aprobado
## [1] TRUE

15.1 [ ], [[ ]] y $

resultado_ejemplo[1]
## $nombre
## [1] "Resumen social"
resultado_ejemplo[[1]]
## [1] "Resumen social"
resultado_ejemplo$valores
## [1] 12.4 15.1 13.8
  • [1] devuelve una sublista.
  • [[1]] extrae el contenido del primer elemento.
  • $nombre extrae un elemento nombrado.

Muchos resultados de funciones y modelos en R se almacenan internamente como listas. Por eso esta estructura será importante más adelante.

16 Data frames, tibbles y bases de datos

16.1 Data frame

Un data frame es una estructura tabular donde:

  • cada fila suele representar una observación;
  • cada columna suele representar una variable;
  • diferentes columnas pueden tener tipos diferentes.
hogares_base <- data.frame(
  id_hogar = 1:3,
  municipio = c("Cali", "Palmira", "Yumbo"),
  ingreso_mensual = c(2500000, 3300000, 2200000)
)

hogares_base
##   id_hogar municipio ingreso_mensual
## 1        1      Cali         2500000
## 2        2   Palmira         3300000
## 3        3     Yumbo         2200000
str(hogares_base)
## 'data.frame':    3 obs. of  3 variables:
##  $ id_hogar       : int  1 2 3
##  $ municipio      : chr  "Cali" "Palmira" "Yumbo"
##  $ ingreso_mensual: num  2500000 3300000 2200000

16.2 Paquetes: instalar no es cargar

R incorpora muchas funciones, pero su ecosistema se amplía con paquetes.

La instalación se realiza normalmente una sola vez en cada instalación de R:

install.packages("tidyverse")

La carga se realiza en las sesiones en las que necesitamos sus funciones:

library(tidyverse)

Si R indica que tidyverse no está instalado, ejecuta una sola vez el comando de instalación mostrado en el bloque anterior y vuelve a renderizar el documento.

El tidyverse es un ecosistema de paquetes para ciencia de datos. En esta clase usaremos principalmente herramientas de tibble, dplyr y readr, cargadas mediante library(tidyverse).

Error frecuente. Poner install.packages() dentro de un documento que se renderiza repetidamente. Instalar y cargar son acciones distintas.

16.3 Tibble

Un tibble es una variante moderna de data frame. Mantiene la lógica tabular, pero su impresión y algunos comportamientos están diseñados para facilitar flujos de ciencia de datos.

hogares_tibble_demo <- tibble(
  id_hogar = 1:3,
  municipio = c("Cali", "Palmira", "Yumbo"),
  ingreso_mensual = c(2500000, 3300000, 2200000)
)

hogares_tibble_demo
## # A tibble: 3 × 3
##   id_hogar municipio ingreso_mensual
##      <int> <chr>               <dbl>
## 1        1 Cali              2500000
## 2        2 Palmira           3300000
## 3        3 Yumbo             2200000

17 Base simulada hogares

A partir de este punto utilizaremos una única base llamada hogares.

Advertencia pedagógica: todos los datos son simulados y fueron construidos exclusivamente para aprender R. No corresponden a estadísticas reales de Cali, Palmira, Buenaventura, Yumbo o Jamundí y no deben utilizarse para caracterizar esos municipios.

hogares <- tibble(
  id_hogar = 1:12,
  municipio = c(
    "Cali", "Cali", "Palmira", "Palmira", "Buenaventura", "Buenaventura",
    "Yumbo", "Yumbo", "Jamundí", "Jamundí", "Cali", "Palmira"
  ),
  ingreso_mensual = c(
    2500000, 3900000, 2300000, NA, 2100000, 2950000,
    3600000, 2750000, 4200000, 2450000, 5100000, 3250000
  ),
  gasto_mensual = c(
    2050000, 3150000, 1950000, 2600000, 1850000, NA,
    2850000, 2400000, 3300000, 2250000, 4000000, 2700000
  ),
  numero_personas = c(3L, 4L, 2L, 5L, 4L, 3L, 2L, 5L, 3L, 4L, 5L, 2L),
  zona = factor(
    c("Urbana", "Urbana", "Urbana", "Rural", "Urbana", "Rural",
      "Urbana", "Rural", "Urbana", "Rural", "Urbana", "Urbana"),
    levels = c("Urbana", "Rural")
  ),
  recibe_subsidio = factor(
    c("No", "No", "Sí", "Sí", "Sí", NA, "No", "Sí", "No", "Sí", "No", "Sí"),
    levels = c("No", "Sí")
  )
)

hogares
## # A tibble: 12 × 7
##    id_hogar municipio    ingreso_mensual gasto_mensual numero_personas zona  
##       <int> <chr>                  <dbl>         <dbl>           <int> <fct> 
##  1        1 Cali                 2500000       2050000               3 Urbana
##  2        2 Cali                 3900000       3150000               4 Urbana
##  3        3 Palmira              2300000       1950000               2 Urbana
##  4        4 Palmira                   NA       2600000               5 Rural 
##  5        5 Buenaventura         2100000       1850000               4 Urbana
##  6        6 Buenaventura         2950000            NA               3 Rural 
##  7        7 Yumbo                3600000       2850000               2 Urbana
##  8        8 Yumbo                2750000       2400000               5 Rural 
##  9        9 Jamundí              4200000       3300000               3 Urbana
## 10       10 Jamundí              2450000       2250000               4 Rural 
## 11       11 Cali                 5100000       4000000               5 Urbana
## 12       12 Palmira              3250000       2700000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>

17.1 ¿Qué representa cada dimensión?

  • Fila: un hogar simulado.
  • Columna: una característica o variable.
  • id_hogar: identificador.
  • municipio: variable categórica almacenada como texto.
  • ingreso_mensual y gasto_mensual: variables numéricas.
  • numero_personas: variable entera.
  • zona y recibe_subsidio: variables categóricas representadas como factores.

17.2 Inspección básica

head(hogares)
## # A tibble: 6 × 7
##   id_hogar municipio    ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>                  <dbl>         <dbl>           <int> <fct> 
## 1        1 Cali                 2500000       2050000               3 Urbana
## 2        2 Cali                 3900000       3150000               4 Urbana
## 3        3 Palmira              2300000       1950000               2 Urbana
## 4        4 Palmira                   NA       2600000               5 Rural 
## 5        5 Buenaventura         2100000       1850000               4 Urbana
## 6        6 Buenaventura         2950000            NA               3 Rural 
## # ℹ 1 more variable: recibe_subsidio <fct>
tail(hogares)
## # A tibble: 6 × 7
##   id_hogar municipio ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>               <dbl>         <dbl>           <int> <fct> 
## 1        7 Yumbo             3600000       2850000               2 Urbana
## 2        8 Yumbo             2750000       2400000               5 Rural 
## 3        9 Jamundí           4200000       3300000               3 Urbana
## 4       10 Jamundí           2450000       2250000               4 Rural 
## 5       11 Cali              5100000       4000000               5 Urbana
## 6       12 Palmira           3250000       2700000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>
names(hogares)
## [1] "id_hogar"        "municipio"       "ingreso_mensual" "gasto_mensual"  
## [5] "numero_personas" "zona"            "recibe_subsidio"
dim(hogares)
## [1] 12  7
nrow(hogares)
## [1] 12
ncol(hogares)
## [1] 7
str(hogares)
## tibble [12 × 7] (S3: tbl_df/tbl/data.frame)
##  $ id_hogar       : int [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ municipio      : chr [1:12] "Cali" "Cali" "Palmira" "Palmira" ...
##  $ ingreso_mensual: num [1:12] 2500000 3900000 2300000 NA 2100000 2950000 3600000 2750000 4200000 2450000 ...
##  $ gasto_mensual  : num [1:12] 2050000 3150000 1950000 2600000 1850000 NA 2850000 2400000 3300000 2250000 ...
##  $ numero_personas: int [1:12] 3 4 2 5 4 3 2 5 3 4 ...
##  $ zona           : Factor w/ 2 levels "Urbana","Rural": 1 1 1 2 1 2 1 2 1 2 ...
##  $ recibe_subsidio: Factor w/ 2 levels "No","Sí": 1 1 2 2 2 NA 1 2 1 2 ...
summary(hogares)
##     id_hogar      municipio         ingreso_mensual   gasto_mensual    
##  Min.   : 1.00   Length:12          Min.   :2100000   Min.   :1850000  
##  1st Qu.: 3.75   Class :character   1st Qu.:2475000   1st Qu.:2150000  
##  Median : 6.50   Mode  :character   Median :2950000   Median :2600000  
##  Mean   : 6.50                      Mean   :3190909   Mean   :2645455  
##  3rd Qu.: 9.25                      3rd Qu.:3750000   3rd Qu.:3000000  
##  Max.   :12.00                      Max.   :5100000   Max.   :4000000  
##                                     NA's   :1         NA's   :1        
##  numero_personas     zona   recibe_subsidio
##  Min.   :2.00    Urbana:8   No  :5         
##  1st Qu.:2.75    Rural :4   Sí  :6         
##  Median :3.50               NA's:1         
##  Mean   :3.50                              
##  3rd Qu.:4.25                              
##  Max.   :5.00                              
## 

En RStudio, View() abre una vista tabular interactiva. No la ejecutamos al renderizar porque pertenece al flujo interactivo del IDE.

View(hogares)

17.3 Acceso con $ y [fila, columna]

hogares$municipio
##  [1] "Cali"         "Cali"         "Palmira"      "Palmira"      "Buenaventura"
##  [6] "Buenaventura" "Yumbo"        "Yumbo"        "Jamundí"      "Jamundí"     
## [11] "Cali"         "Palmira"
hogares[1, 3]
## # A tibble: 1 × 1
##   ingreso_mensual
##             <dbl>
## 1         2500000
hogares[1:4, c("municipio", "ingreso_mensual")]
## # A tibble: 4 × 2
##   municipio ingreso_mensual
##   <chr>               <dbl>
## 1 Cali              2500000
## 2 Cali              3900000
## 3 Palmira           2300000
## 4 Palmira                NA

17.4 Detectar faltantes en la base

is.na(hogares$ingreso_mensual)
##  [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
sum(is.na(hogares$ingreso_mensual))
## [1] 1
sum(is.na(hogares$gasto_mensual))
## [1] 1
sum(is.na(hogares$recibe_subsidio))
## [1] 1

No imputaremos valores en esta clase. Primero aprendemos a reconocerlos y a controlar cómo afectan los cálculos.

18 dplyr: seleccionar, filtrar y transformar

dplyr organiza muchas tareas frecuentes mediante verbos. Utilizaremos el pipe nativo |> para leer una secuencia de izquierda a derecha.

18.1 Pipe nativo |>

La expresión:

objeto |> funcion()

puede leerse como: “toma objeto y pásalo a la siguiente operación”.

El operador %>%, popularizado por magrittr y usado ampliamente en código tidyverse, también está disponible al cargar tidyverse.

hogares |>
  head(3)
## # A tibble: 3 × 7
##   id_hogar municipio ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>               <dbl>         <dbl>           <int> <fct> 
## 1        1 Cali              2500000       2050000               3 Urbana
## 2        2 Cali              3900000       3150000               4 Urbana
## 3        3 Palmira           2300000       1950000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>
hogares %>%
  head(3)
## # A tibble: 3 × 7
##   id_hogar municipio ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>               <dbl>         <dbl>           <int> <fct> 
## 1        1 Cali              2500000       2050000               3 Urbana
## 2        2 Cali              3900000       3150000               4 Urbana
## 3        3 Palmira           2300000       1950000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>

En esta clase priorizaremos |> y reconoceremos %>% para poder leer código existente.

18.2 select(): seleccionar columnas

Con R base:

hogares[, c("municipio", "ingreso_mensual", "gasto_mensual")]
## # A tibble: 12 × 3
##    municipio    ingreso_mensual gasto_mensual
##    <chr>                  <dbl>         <dbl>
##  1 Cali                 2500000       2050000
##  2 Cali                 3900000       3150000
##  3 Palmira              2300000       1950000
##  4 Palmira                   NA       2600000
##  5 Buenaventura         2100000       1850000
##  6 Buenaventura         2950000            NA
##  7 Yumbo                3600000       2850000
##  8 Yumbo                2750000       2400000
##  9 Jamundí              4200000       3300000
## 10 Jamundí              2450000       2250000
## 11 Cali                 5100000       4000000
## 12 Palmira              3250000       2700000

Con dplyr:

hogares |>
  select(municipio, ingreso_mensual, gasto_mensual)
## # A tibble: 12 × 3
##    municipio    ingreso_mensual gasto_mensual
##    <chr>                  <dbl>         <dbl>
##  1 Cali                 2500000       2050000
##  2 Cali                 3900000       3150000
##  3 Palmira              2300000       1950000
##  4 Palmira                   NA       2600000
##  5 Buenaventura         2100000       1850000
##  6 Buenaventura         2950000            NA
##  7 Yumbo                3600000       2850000
##  8 Yumbo                2750000       2400000
##  9 Jamundí              4200000       3300000
## 10 Jamundí              2450000       2250000
## 11 Cali                 5100000       4000000
## 12 Palmira              3250000       2700000

18.3 filter(): filtrar filas

Seleccionemos hogares con ingreso observado y superior a 3.000.000.

Con R base:

hogares[
  !is.na(hogares$ingreso_mensual) & hogares$ingreso_mensual > 3000000,
]
## # A tibble: 5 × 7
##   id_hogar municipio ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>               <dbl>         <dbl>           <int> <fct> 
## 1        2 Cali              3900000       3150000               4 Urbana
## 2        7 Yumbo             3600000       2850000               2 Urbana
## 3        9 Jamundí           4200000       3300000               3 Urbana
## 4       11 Cali              5100000       4000000               5 Urbana
## 5       12 Palmira           3250000       2700000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>

Con dplyr:

hogares |>
  filter(!is.na(ingreso_mensual), ingreso_mensual > 3000000)
## # A tibble: 5 × 7
##   id_hogar municipio ingreso_mensual gasto_mensual numero_personas zona  
##      <int> <chr>               <dbl>         <dbl>           <int> <fct> 
## 1        2 Cali              3900000       3150000               4 Urbana
## 2        7 Yumbo             3600000       2850000               2 Urbana
## 3        9 Jamundí           4200000       3300000               3 Urbana
## 4       11 Cali              5100000       4000000               5 Urbana
## 5       12 Palmira           3250000       2700000               2 Urbana
## # ℹ 1 more variable: recibe_subsidio <fct>
Interpretación. El filtro identifica observaciones que cumplen una regla. No está estimando un efecto ni explicando por qué esos hogares tienen determinado ingreso.

18.4 mutate(): crear o transformar variables

Crearemos dos variables:

\[ \text{ahorro mensual} = \text{ingreso mensual} - \text{gasto mensual} \]

\[ \text{ingreso per cápita} = \frac{\text{ingreso mensual}}{\text{número de personas}} \]

hogares_analisis <- hogares |>
  mutate(
    ahorro_mensual = ingreso_mensual - gasto_mensual,
    ingreso_per_capita = ingreso_mensual / numero_personas
  )

hogares_analisis |>
  select(
    id_hogar, municipio, ingreso_mensual, gasto_mensual,
    ahorro_mensual, ingreso_per_capita
  )
## # A tibble: 12 × 6
##    id_hogar municipio    ingreso_mensual gasto_mensual ahorro_mensual
##       <int> <chr>                  <dbl>         <dbl>          <dbl>
##  1        1 Cali                 2500000       2050000         450000
##  2        2 Cali                 3900000       3150000         750000
##  3        3 Palmira              2300000       1950000         350000
##  4        4 Palmira                   NA       2600000             NA
##  5        5 Buenaventura         2100000       1850000         250000
##  6        6 Buenaventura         2950000            NA             NA
##  7        7 Yumbo                3600000       2850000         750000
##  8        8 Yumbo                2750000       2400000         350000
##  9        9 Jamundí              4200000       3300000         900000
## 10       10 Jamundí              2450000       2250000         200000
## 11       11 Cali                 5100000       4000000        1100000
## 12       12 Palmira              3250000       2700000         550000
## # ℹ 1 more variable: ingreso_per_capita <dbl>

Los NA se propagan cuando una operación depende de un dato faltante.

18.5 arrange(): ordenar observaciones

hogares_analisis |>
  arrange(desc(ingreso_per_capita)) |>
  select(id_hogar, municipio, ingreso_per_capita)
## # A tibble: 12 × 3
##    id_hogar municipio    ingreso_per_capita
##       <int> <chr>                     <dbl>
##  1        7 Yumbo                  1800000 
##  2       12 Palmira                1625000 
##  3        9 Jamundí                1400000 
##  4        3 Palmira                1150000 
##  5       11 Cali                   1020000 
##  6        6 Buenaventura            983333.
##  7        2 Cali                    975000 
##  8        1 Cali                    833333.
##  9       10 Jamundí                 612500 
## 10        8 Yumbo                   550000 
## 11        5 Buenaventura            525000 
## 12        4 Palmira                     NA

18.6 group_by() y summarise(): agregación

resumen_zona <- hogares_analisis |>
  group_by(zona) |>
  summarise(
    hogares_observados = n(),
    ingreso_promedio = mean(ingreso_mensual, na.rm = TRUE),
    gasto_promedio = mean(gasto_mensual, na.rm = TRUE),
    ahorro_promedio = mean(ahorro_mensual, na.rm = TRUE),
    .groups = "drop"
  )

resumen_zona
## # A tibble: 2 × 5
##   zona   hogares_observados ingreso_promedio gasto_promedio ahorro_promedio
##   <fct>               <int>            <dbl>          <dbl>           <dbl>
## 1 Urbana                  8         3368750        2731250           637500
## 2 Rural                   4         2716667.       2416667.          275000

Interpretación correcta: en estos datos simulados, los promedios resumen las observaciones disponibles de cada zona.

Interpretación incorrecta: afirmar que vivir en una zona “causa” un mayor ingreso o ahorro a partir de esta tabla. La comparación es descriptiva.

18.7 Una tabla presentable

knitr::kable(
  resumen_zona,
  digits = 0,
  caption = "Resumen descriptivo por zona — datos simulados"
)
Resumen descriptivo por zona — datos simulados
zona hogares_observados ingreso_promedio gasto_promedio ahorro_promedio
Urbana 8 3368750 2731250 637500
Rural 4 2716667 2416667 275000

18.7.1 Inténtalo tú: manipulación de hogares

Enunciado. A partir de hogares_analisis:

  1. selecciona municipio, zona, ingreso_mensual e ingreso_per_capita;
  2. conserva únicamente observaciones con ingreso no faltante;
  3. ordena de mayor a menor por ingreso per cápita.

Pista. Encadena select(), filter() y arrange(desc(...)) con |>.

# Escribe aquí tu solución.
Ver solución
hogares_analisis |>
  select(municipio, zona, ingreso_mensual, ingreso_per_capita) |>
  filter(!is.na(ingreso_mensual)) |>
  arrange(desc(ingreso_per_capita))
## # A tibble: 11 × 4
##    municipio    zona   ingreso_mensual ingreso_per_capita
##    <chr>        <fct>            <dbl>              <dbl>
##  1 Yumbo        Urbana         3600000           1800000 
##  2 Palmira      Urbana         3250000           1625000 
##  3 Jamundí      Urbana         4200000           1400000 
##  4 Palmira      Urbana         2300000           1150000 
##  5 Cali         Urbana         5100000           1020000 
##  6 Buenaventura Rural          2950000            983333.
##  7 Cali         Urbana         3900000            975000 
##  8 Cali         Urbana         2500000            833333.
##  9 Jamundí      Rural          2450000            612500 
## 10 Yumbo        Rural          2750000            550000 
## 11 Buenaventura Urbana         2100000            525000

Explicación. La secuencia reduce columnas, elimina del resultado las filas cuyo ingreso no está observado y ordena las observaciones restantes según una variable derivada.

Interpretación. El ordenamiento permite comparar posiciones dentro de esta base simulada. No constituye una clasificación oficial de hogares o municipios.

19 Joins: unir información de dos tablas

En proyectos reales, una base suele estar distribuida en varias tablas. Un join combina información utilizando una o más variables llave.

Crearemos una tabla pequeña con información territorial también simulada.

info_municipios <- tibble(
  municipio = c("Cali", "Palmira", "Buenaventura", "Yumbo", "Jamundí"),
  grupo_territorial = c("A", "B", "C", "B", "A")
)

info_municipios
## # A tibble: 5 × 2
##   municipio    grupo_territorial
##   <chr>        <chr>            
## 1 Cali         A                
## 2 Palmira      B                
## 3 Buenaventura C                
## 4 Yumbo        B                
## 5 Jamundí      A

La variable grupo_territorial es ficticia y solo existe para ilustrar la unión.

19.1 left_join()

hogares_unidos <- hogares_analisis |>
  left_join(info_municipios, by = "municipio")

hogares_unidos |>
  select(id_hogar, municipio, grupo_territorial, ingreso_mensual)
## # A tibble: 12 × 4
##    id_hogar municipio    grupo_territorial ingreso_mensual
##       <int> <chr>        <chr>                       <dbl>
##  1        1 Cali         A                         2500000
##  2        2 Cali         A                         3900000
##  3        3 Palmira      B                         2300000
##  4        4 Palmira      B                              NA
##  5        5 Buenaventura C                         2100000
##  6        6 Buenaventura C                         2950000
##  7        7 Yumbo        B                         3600000
##  8        8 Yumbo        B                         2750000
##  9        9 Jamundí      A                         4200000
## 10       10 Jamundí      A                         2450000
## 11       11 Cali         A                         5100000
## 12       12 Palmira      B                         3250000

left_join() conserva todas las filas de la tabla de la izquierda y añade columnas coincidentes de la tabla de la derecha.

Error frecuente. Hacer un join sin comprobar si la variable llave identifica de forma adecuada las filas de la tabla auxiliar. Las llaves duplicadas pueden multiplicar observaciones.

19.1.1 Inténtalo tú: unión sencilla

Enunciado. Crea una tabla etiquetas_zona con dos filas: Urbana y Rural, y una columna codigo_zona con valores 1L y 2L. Únela a hogares_analisis mediante left_join().

Pista. La llave común debe llamarse zona en ambas tablas.

# Escribe aquí tu solución.
Ver solución
etiquetas_zona <- tibble(
  zona = factor(c("Urbana", "Rural"), levels = c("Urbana", "Rural")),
  codigo_zona = c(1L, 2L)
)

hogares_analisis |>
  left_join(etiquetas_zona, by = "zona") |>
  select(id_hogar, zona, codigo_zona)
## # A tibble: 12 × 3
##    id_hogar zona   codigo_zona
##       <int> <fct>        <int>
##  1        1 Urbana           1
##  2        2 Urbana           1
##  3        3 Urbana           1
##  4        4 Rural            2
##  5        5 Urbana           1
##  6        6 Rural            2
##  7        7 Urbana           1
##  8        8 Rural            2
##  9        9 Urbana           1
## 10       10 Rural            2
## 11       11 Urbana           1
## 12       12 Urbana           1

Explicación. left_join() busca coincidencias en zona y añade codigo_zona a cada hogar.

Interpretación. El código solo etiqueta categorías. No añade información sustantiva nueva sobre el comportamiento económico de los hogares.

20 Importación y exportación de datos

En esta sección los comandos se muestran, pero no se ejecutan, porque el documento no depende de archivos externos.

20.1 CSV con readr

encuesta_csv <- readr::read_csv("datos/encuesta.csv")
readr::write_csv(hogares, "salidas/hogares.csv")

20.2 Excel con readxl y writexl

indicadores_excel <- readxl::read_excel(
  "datos/indicadores.xlsx",
  sheet = 1
)

writexl::write_xlsx(hogares, "salidas/hogares.xlsx")

20.3 Stata con haven

encuesta_stata <- haven::read_dta("datos/encuesta.dta")
haven::write_dta(hogares, "salidas/hogares.dta")

Si estos paquetes aún no están instalados, su instalación se hace fuera del flujo de renderizado. Por ejemplo:

install.packages(c("readxl", "writexl", "haven"))

20.4 Rutas relativas

Si el proyecto contiene las carpetas datos/ y salidas/, las rutas anteriores funcionan de forma portable siempre que la estructura se conserve.

Evita rutas como:

C:/Users/NombrePersonal/Escritorio/mi_archivo.csv

Una ruta relativa como datos/encuesta.csv comunica dónde está el archivo dentro del proyecto, no dónde se encuentra el proyecto en un computador particular.

21 Práctica integradora

Trabaja con hogares_analisis, que ya contiene ahorro_mensual e ingreso_per_capita.

Enunciado. Construye una tabla por municipio que contenga:

  • número de hogares en la base;
  • ingreso mensual promedio ignorando faltantes;
  • gasto mensual promedio ignorando faltantes;
  • ingreso per cápita promedio ignorando faltantes;
  • proporción de hogares con subsidio entre las respuestas observadas.

Después, ordena la tabla de mayor a menor según ingreso per cápita promedio.

Pista. Utiliza group_by(), summarise(), n(), mean(..., na.rm = TRUE) y arrange(desc(...)). Para la proporción puedes promediar la expresión lógica recibe_subsidio == "Sí".

# Escribe aquí tu solución.
Ver solución
resumen_municipio <- hogares_analisis |>
  group_by(municipio) |>
  summarise(
    numero_hogares = n(),
    ingreso_promedio = mean(ingreso_mensual, na.rm = TRUE),
    gasto_promedio = mean(gasto_mensual, na.rm = TRUE),
    ingreso_per_capita_promedio = mean(ingreso_per_capita, na.rm = TRUE),
    proporcion_subsidio = mean(recibe_subsidio == "Sí", na.rm = TRUE),
    .groups = "drop"
  ) |>
  arrange(desc(ingreso_per_capita_promedio))

resumen_municipio
## # A tibble: 5 × 6
##   municipio    numero_hogares ingreso_promedio gasto_promedio
##   <chr>                 <int>            <dbl>          <dbl>
## 1 Palmira                   3         2775000        2416667.
## 2 Yumbo                     2         3175000        2625000 
## 3 Jamundí                   2         3325000        2775000 
## 4 Cali                      3         3833333.       3066667.
## 5 Buenaventura              2         2525000        1850000 
## # ℹ 2 more variables: ingreso_per_capita_promedio <dbl>,
## #   proporcion_subsidio <dbl>

Explicación. group_by(municipio) define los grupos y summarise() reduce cada grupo a una fila de estadísticas. Una comparación lógica como recibe_subsidio == "Sí" produce TRUE y FALSE; al calcular su media, R trata TRUE como 1 y FALSE como 0, por lo que se obtiene una proporción entre respuestas observadas.

Interpretación. La tabla resume una base pequeña y simulada. Las diferencias entre municipios no deben presentarse como estadísticas reales ni como efectos causales. En una investigación real también habría que considerar diseño de muestra, calidad de medición, cobertura y otros factores relevantes.

22 Ejercicio individual

Enunciado. Con hogares_analisis, identifica los hogares que cumplen simultáneamente estas condiciones:

  • tienen ingreso mensual observado;
  • tienen gasto mensual observado;
  • su ahorro mensual es positivo;
  • pertenecen a zona "Urbana".

Muestra únicamente id_hogar, municipio, ingreso_mensual, gasto_mensual y ahorro_mensual. Ordena el resultado de mayor a menor ahorro.

Pista. Usa filter() con varias condiciones, luego select() y arrange(desc(...)).

# Escribe aquí tu solución.
Ver solución
hogares_analisis |>
  filter(
    !is.na(ingreso_mensual),
    !is.na(gasto_mensual),
    ahorro_mensual > 0,
    zona == "Urbana"
  ) |>
  select(
    id_hogar, municipio, ingreso_mensual,
    gasto_mensual, ahorro_mensual
  ) |>
  arrange(desc(ahorro_mensual))
## # A tibble: 8 × 5
##   id_hogar municipio    ingreso_mensual gasto_mensual ahorro_mensual
##      <int> <chr>                  <dbl>         <dbl>          <dbl>
## 1       11 Cali                 5100000       4000000        1100000
## 2        9 Jamundí              4200000       3300000         900000
## 3        2 Cali                 3900000       3150000         750000
## 4        7 Yumbo                3600000       2850000         750000
## 5       12 Palmira              3250000       2700000         550000
## 6        1 Cali                 2500000       2050000         450000
## 7        3 Palmira              2300000       1950000         350000
## 8        5 Buenaventura         2100000       1850000         250000

Explicación. filter() puede recibir varias condiciones separadas por comas; dplyr exige que todas se cumplan. Luego se conservan las columnas necesarias y se ordena el resultado.

Interpretación. El resultado identifica hogares simulados que cumplen una regla definida. No establece que la zona explique el ahorro ni que esos patrones representen a una población real.

23 Reto opcional

Enunciado. Construye una tabla por zona que muestre:

  • número de hogares;
  • mediana del ingreso mensual;
  • mediana del gasto mensual;
  • mediana del ahorro mensual;
  • valor mínimo y máximo del ingreso per cápita.

Usa na.rm = TRUE donde sea necesario.

Pista. La lógica es similar a resumen_zona, cambiando las funciones de resumen.

# Escribe aquí tu solución.
Ver solución
hogares_analisis |>
  group_by(zona) |>
  summarise(
    numero_hogares = n(),
    mediana_ingreso = median(ingreso_mensual, na.rm = TRUE),
    mediana_gasto = median(gasto_mensual, na.rm = TRUE),
    mediana_ahorro = median(ahorro_mensual, na.rm = TRUE),
    minimo_ingreso_per_capita = min(ingreso_per_capita, na.rm = TRUE),
    maximo_ingreso_per_capita = max(ingreso_per_capita, na.rm = TRUE),
    .groups = "drop"
  )
## # A tibble: 2 × 7
##   zona   numero_hogares mediana_ingreso mediana_gasto mediana_ahorro
##   <fct>           <int>           <dbl>         <dbl>          <dbl>
## 1 Urbana              8         3425000       2775000         650000
## 2 Rural               4         2750000       2400000         275000
## # ℹ 2 more variables: minimo_ingreso_per_capita <dbl>,
## #   maximo_ingreso_per_capita <dbl>

Explicación. summarise() admite múltiples estadísticas por grupo. La mediana es una medida de posición central y min()/max() describen el rango observado.

Interpretación. Estas cifras siguen siendo descriptivas y pertenecen exclusivamente al conjunto simulado.

24 Errores frecuentes que conviene reconocer

  • Escribir view() en lugar de View().
  • Confundir = o <- con == dentro de una comparación.
  • Escribir números entre comillas y esperar operaciones numéricas.
  • Olvidar que R diferencia mayúsculas y minúsculas.
  • Intentar calcular una media con NA sin definir cómo tratar los faltantes.
  • Usar una variable antes de crearla.
  • Ejecutar chunks fuera de orden y creer que el documento completo funcionará igual.
  • Repetir nombres de chunks.
  • Instalar paquetes dentro de un documento reproducible en cada renderizado.
  • Utilizar rutas personales completas en vez de rutas relativas al proyecto.
  • Interpretar una comparación descriptiva como evidencia causal.