← Catálogo
· Capítulo 8
T08_mECO_Script_Cualitativos.R
# =============================================================================
# TEMA 08 — Datos Cualitativos: Dummies, ANOVA, Multinomial y Ordenado
# =============================================================================
# Manual de Microeconometría — Carlos de Anta Puig
# Profesor de Econometría y Microeconometría — UNIR
# https://github.com/carlanta/MicroEconometrics Versión 1.0 — 2026
#
# OBJETIVO: Ilustrar el uso de variables cualitativas como regresores
# (dummies, ANOVA, ANCOVA) y como variable dependiente
# (Logit Multinomial, Probit Ordenado).
# INSTRUCCIONES: Session > Set Working Directory > To Source File Location
# =============================================================================
pausa <- function(msg="\n>>> Pulsa ENTER para continuar...") {
if (interactive()) readline(msg) else Sys.sleep(0.5)
}
pkgs <- c("nnet","MASS","marginaleffects")
for (p in pkgs) if (!requireNamespace(p, quietly=TRUE)) install.packages(p, quiet=TRUE)
suppressPackageStartupMessages({ library(nnet); library(MASS) })
.get_script_dir <- function() {
args <- commandArgs(trailingOnly = FALSE)
for (a in args) {
if (startsWith(a, "--file=")) return(dirname(normalizePath(substring(a, 8))))
}
for (i in seq_len(sys.nframe())) {
ofile <- tryCatch(sys.frame(i)$ofile, error = function(e) NULL)
if (!is.null(ofile)) return(dirname(normalizePath(ofile)))
}
return(normalizePath("."))
}
.sdir <- .get_script_dir()
DATA_DIR <- normalizePath(file.path(.sdir, "..", "data"), mustWork=FALSE)
OUTPUT_DIR <- normalizePath(file.path(.sdir, "..", "output"), mustWork=FALSE)
if (!dir.exists(OUTPUT_DIR)) dir.create(OUTPUT_DIR, recursive=TRUE)
cat("\n================================================================\n")
cat(" TEMA 08 — Datos Cualitativos\n")
cat("================================================================\n\n")
# =============================================================================
# PARTE I: VARIABLES CUALITATIVAS COMO REGRESORES
# =============================================================================
cat("=== PARTE I: Variables cualitativas como regresores ===\n\n")
# --- DATOS SIMULADOS ---
set.seed(2026)
n <- 300
sector <- sample(c("Industria","Servicios","Tecnología","Construcción"), n, replace=TRUE)
experiencia <- runif(n, 1, 30)
sexo <- sample(c("Hombre","Mujer"), n, replace=TRUE)
salario <- 1200 + 400*(sector=="Tecnología") + 100*(sector=="Industria") -
100*(sector=="Construcción") + 30*experiencia -
150*(sexo=="Mujer") + rnorm(n, 0, 200)
datos <- data.frame(salario, sector=factor(sector), experiencia, sexo=factor(sexo))
cat(" Datos simulados: salario en función de sector, experiencia y sexo.\n")
cat(sprintf(" n = %d observaciones\n\n", n))
# --- ANOVA ---
cat("--- ANOVA: ¿Difiere el salario por sector? ---\n\n")
mod_anova <- lm(salario ~ sector, data=datos)
tab_anova <- anova(mod_anova)
cat(" Tabla ANOVA:\n")
cat(sprintf(" %-16s GL=%d SC=%.0f F=%.2f p=%.6f\n",
"sector", tab_anova$Df[1], tab_anova$"Sum Sq"[1],
tab_anova$"F value"[1], tab_anova$"Pr(>F)"[1]))
if (tab_anova$"Pr(>F)"[1] < 0.05) {
cat(" ✓ Se rechaza H0: al menos un sector tiene media distinta.\n\n")
} else {
cat(" ✗ No se rechaza H0.\n\n")
}
cat(" Coeficientes (base = Construcción):\n")
ct <- coef(summary(mod_anova))
for (i in seq_len(nrow(ct))) {
cat(sprintf(" %-24s %8.1f (p = %.4f)\n",
rownames(ct)[i], ct[i,1], ct[i,4]))
}
cat("\n Tecnología gana significativamente más que la base.\n")
pausa()
# --- GRÁFICO ANOVA ---
png(file.path(OUTPUT_DIR, "T08_anova.png"), width=1400, height=700, res=150)
par(mfrow=c(1,2), mar=c(4.5,4.5,2.5,0.5), cex.main=0.88)
boxplot(salario ~ sector, data=datos, col=gray(c(0.3,0.5,0.7,0.85)),
border=gray(0.2), ylab="Salario (EUR/mes)", main="ANOVA: por sector",
cex.axis=0.75)
# ANCOVA
plot(experiencia, salario, pch=ifelse(sexo=="Hombre",16,1),
col=gray(ifelse(sexo=="Hombre",0.3,0.6)), cex=0.5,
xlab="Experiencia (años)", ylab="Salario", main="ANCOVA: sexo + exp.")
m_anc <- lm(salario ~ experiencia + sexo, data=datos)
abline(a=coef(m_anc)[1], b=coef(m_anc)[2], lwd=2, lty=1)
abline(a=coef(m_anc)[1]+coef(m_anc)[3], b=coef(m_anc)[2], lwd=2, lty=2)
legend("topleft", c("Hombres","Mujeres"), pch=c(16,1),
col=gray(c(0.3,0.6)), lty=c(1,2), cex=0.75, bty="n")
dev.off()
cat("\n Gráfico: output/T08_anova.png\n")
pausa()
# --- ANCOVA ---
cat("\n--- ANCOVA: Sector + Experiencia + Sexo ---\n\n")
mod_ancova <- lm(salario ~ sector + experiencia + sexo, data=datos)
ct_anc <- coef(summary(mod_ancova))
cat(sprintf(" %-24s %9s %8s %10s\n", "Variable","Coef.","EE","p-valor"))
cat(sprintf(" %s\n", paste(rep("-",56), collapse="")))
for (i in seq_len(nrow(ct_anc))) {
sig <- ifelse(ct_anc[i,4]<0.001,"***",ifelse(ct_anc[i,4]<0.01,"**",
ifelse(ct_anc[i,4]<0.05,"*"," ")))
cat(sprintf(" %-24s %9.1f %8.1f %10.6f %s\n",
rownames(ct_anc)[i], ct_anc[i,1], ct_anc[i,2], ct_anc[i,4], sig))
}
cat(sprintf("\n R² = %.4f\n", summary(mod_ancova)$r.squared))
cat(" ANCOVA controla por experiencia al comparar sectores y sexos.\n")
cat(" La brecha de género tras controlar por experiencia y sector:\n")
cat(sprintf(" %.0f EUR/mes.\n", coef(mod_ancova)["sexoMujer"]))
pausa()
# =============================================================================
# PARTE II: VD CUALITATIVA — MULTINOMIAL
# =============================================================================
cat("\n\n=== PARTE II: Variable dependiente cualitativa ===\n\n")
cat("--- LOGIT MULTINOMIAL: Elección de Transporte ---\n\n")
load(file.path(DATA_DIR, "T08_CP01_transporte.RData"))
cat("Dataset:", nrow(transporte), "individuos, 3 alternativas\n")
cat(sprintf(" %s\n\n", paste(names(table(transporte$eleccion)),
table(transporte$eleccion), sep=": ", collapse=" | ")))
ml <- multinom(eleccion ~ ingreso + distancia + edad + zona_urbana,
data=transporte, trace=FALSE)
cat(" Coeficientes (base = coche):\n\n")
cf <- coef(ml)
cat(sprintf(" %-12s | %9s %9s %9s %9s %9s\n", "",
"Intercept","ingreso","distancia","edad","urbana"))
cat(sprintf(" %s\n", paste(rep("-",64), collapse="")))
for (alt in rownames(cf)) {
cat(sprintf(" %-12s | %9.4f %9.4f %9.4f %9.4f %9.4f\n",
alt, cf[alt,1], cf[alt,2], cf[alt,3], cf[alt,4], cf[alt,5]))
}
cat("\n INTERPRETACIÓN:\n")
cat(" ingreso negativo en bus y tren: los más ricos prefieren coche.\n")
cat(" distancia positiva: mayor distancia favorece transporte público.\n")
cat(" zona_urbana positiva en bus: la oferta urbana favorece el autobús.\n")
pausa()
# --- EFECTOS MARGINALES ---
cat("\n--- EFECTOS MARGINALES (AME) ---\n\n")
if (requireNamespace("marginaleffects", quietly=TRUE)) {
me <- marginaleffects::avg_slopes(ml)
me_df <- as.data.frame(me)
for (grp in unique(me_df$group)) {
cat(sprintf(" --- %s ---\n", grp))
sub <- me_df[me_df$group == grp,]
for (i in seq_len(nrow(sub))) {
sig <- ifelse(sub$p.value[i]<0.05,"*","")
cat(sprintf(" %-12s AME = %8.4f (p = %.4f) %s\n",
sub$term[i], sub$estimate[i], sub$p.value[i], sig))
}
}
}
pausa()
# --- TABLA DE CLASIFICACIÓN ---
cat("\n--- BONDAD DEL AJUSTE ---\n\n")
pred <- predict(ml)
tab_cl <- table(Observado=transporte$eleccion, Predicho=pred)
cat(" Tabla de clasificación:\n")
print(tab_cl)
cat(sprintf("\n %% correcto: %.1f%%\n", 100*sum(diag(tab_cl))/sum(tab_cl)))
pausa()
# =============================================================================
# PARTE III: VD CUALITATIVA — ORDENADO
# =============================================================================
cat("\n--- PROBIT ORDENADO: Satisfacción Laboral ---\n\n")
load(file.path(DATA_DIR, "T08_CP02_satisfaccion_laboral.RData"))
satisf_lab$satisfaccion <- factor(satisf_lab$satisfaccion, ordered=TRUE)
cat("Dataset:", nrow(satisf_lab), "trabajadores, 5 niveles\n")
print(table(satisf_lab$satisfaccion))
cat("\n")
oprobit <- polr(satisfaccion ~ salario + horas_semanales + autonomia + antiguedad,
data=satisf_lab, method="probit")
s_op <- summary(oprobit)
ct_op <- s_op$coefficients
cat(" Coeficientes del Probit Ordenado:\n\n")
for (i in seq_len(nrow(ct_op))) {
pval <- 2*pnorm(-abs(ct_op[i,"t value"]))
sig <- ifelse(pval<0.001,"***",ifelse(pval<0.01,"**",ifelse(pval<0.05,"*"," ")))
cat(sprintf(" %-16s Coef: %8.4f | EE: %6.4f | t: %6.2f %s\n",
rownames(ct_op)[i], ct_op[i,1], ct_op[i,2], ct_op[i,3], sig))
}
cat(sprintf("\n Umbrales: %s\n",
paste(sprintf("%.3f", oprobit$zeta), collapse=" | ")))
cat("\n INTERPRETACIÓN:\n")
cat(" salario (+): mayor salario → mayor satisfacción.\n")
cat(" horas (-): más horas → menor satisfacción.\n")
cat(" autonomía (+): más autonomía → mayor satisfacción.\n")
cat(" NOTA: El signo de beta solo determina el efecto en las categorías\n")
cat(" extremas (1 y 5). En las intermedias, el efecto puede invertirse.\n")
pausa()
# =============================================================================
# RESUMEN Y GUÍA DE COMANDOS
# =============================================================================
cat("\n================================================================\n")
cat(" RESUMEN\n")
cat("================================================================\n\n")
cat(" PARTE I: Variables cualitativas como regresores\n")
cat(" - Las dummies permiten medir diferencias entre grupos.\n")
cat(" - ANOVA = solo dummies. ANCOVA = dummies + continuas.\n")
cat(" - R crea dummies automáticamente con factor().\n\n")
cat(" PARTE II: VD cualitativa multinomial\n")
cat(" - Logit Multinomial: multinom() del paquete nnet.\n")
cat(" - Los coeficientes son log-odds relativos a la base.\n")
cat(" - Los AME (avg_slopes) dan el efecto sobre probabilidades.\n\n")
cat(" PARTE III: VD cualitativa ordenada\n")
cat(" - Probit/Logit Ordenado: polr() del paquete MASS.\n")
cat(" - Variable latente + umbrales → probabilidades por categoría.\n")
cat("\n\n--- GUÍA DE COMANDOS R ---\n\n")
cat(" factor(x) Convertir a factor (crea dummies)\n")
cat(" relevel(x, ref='base') Cambiar categoría de referencia\n")
cat(" lm(y ~ factor_var) ANOVA (solo dummies)\n")
cat(" lm(y ~ factor + continua) ANCOVA\n")
cat(" lm(y ~ factor * continua) ANCOVA con interacción\n")
cat(" anova(modelo) Tabla ANOVA (F-test por variable)\n")
cat(" multinom(y ~ x, trace=F) Logit Multinomial (nnet)\n")
cat(" polr(y ~ x, method=...) Probit/Logit Ordenado (MASS)\n")
cat(" avg_slopes(modelo) Efectos marginales (marginaleffects)\n\n")
cat("================================================================\n")
cat(" Fin del script T08\n")
cat("================================================================\n")