# ============================================================================= # 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")