Este informe resuelve el ejercicio 6 con la base IOP_1212_01_D.sav. El literal a solicita tablas de frecuencias de la administración de los ingresos del hogar (P29) y de la satisfacción con la vida familiar (P40). El literal b solicita intervalos de clase y tablas para la edad considerada ideal para que una mujer (P1) y un hombre (P2) se casen.
Las edades son opiniones de las personas encuestadas: no corresponden a sus edades personales ni a edades de matrimonio observadas. Se presentan frecuencias sin ponderación, de acuerdo con la tarea. No se realiza inferencia poblacional ni se presupone un diseño muestral que no fue proporcionado.
Objetivo general: Describir las cuatro variables mediante tablas de frecuencias y agrupación de edades en intervalos de clase.
Guarde el .Rmd y el .sav en la misma
carpeta. Instale una sola vez
install.packages(c("haven", "rmarkdown", "knitr")) y pulse
Knit → Knit to HTML en RStudio.
Se leen los códigos y etiquetas originales con
haven::read_sav(user_na = TRUE). Para las edades,
haven::zap_missing() convierte a NA los
valores perdidos definidos en SPSS. Cada variable se analiza con sus
propios casos válidos: no se eliminan filas por faltantes en otras
preguntas.
# IMPORTACIÓN: conservar códigos originales y convertir perdidos SPSS.
if (!requireNamespace("haven", quietly = TRUE)) {
stop('Instale haven en la consola: install.packages("haven")')
}
base_original <- haven::read_sav(archivo, user_na = TRUE)
names(base_original) <- toupper(trimws(names(base_original)))
stopifnot(all(c("P29", "P40", "P1", "P2") %in% names(base_original)))
N <- nrow(base_original)
# TABLAS CATEGÓRICAS: cada variable utiliza su propio denominador válido.
tabla_categoria <- function(variable, codigos_validos, ordinal = FALSE) {
original <- base_original[[variable]]
etiquetas <- attr(original, "labels")
codigos <- sort(unique(c(as.numeric(etiquetas), as.numeric(original))))
codigos <- codigos[!is.na(codigos)]
limpios <- as.numeric(haven::zap_missing(original))
validos <- !is.na(limpios) & limpios %in% codigos_validos
n_validos <- sum(validos)
if (n_validos == 0) stop("No hay respuestas válidas en ", variable)
fi <- vapply(codigos, function(k) sum(as.numeric(original) == k, na.rm = TRUE), numeric(1))
nombres <- vapply(codigos, function(k) {
j <- match(k, as.numeric(etiquetas))
if (is.na(j)) paste("Código", k) else names(etiquetas)[j]
}, character(1))
valido <- codigos %in% codigos_validos
tabla <- data.frame(Codigo = as.character(codigos), Respuesta = nombres,
fi = fi, Porcentaje_total = 100 * fi / N,
Porcentaje_valido = ifelse(valido, 100 * fi / n_validos, NA_real_))
if (ordinal) {
acum <- cumsum(ifelse(valido, fi, 0))
tabla$Porcentaje_valido_acumulado <- ifelse(valido, 100 * acum / n_validos, NA_real_)
}
n_sistema <- sum(is.na(as.numeric(original)))
if (n_sistema > 0) {
extra <- tabla[1, , drop = FALSE]
extra[1, ] <- NA
extra$Codigo <- "NA"
extra$Respuesta <- "Sin dato en el archivo (perdido de sistema)"
extra$fi <- n_sistema
extra$Porcentaje_total <- 100 * n_sistema / N
tabla <- rbind(tabla, extra)
}
total <- tabla[1, , drop = FALSE]
total[1, ] <- NA
total$Codigo <- ""
total$Respuesta <- "TOTAL"
total$fi <- N
total$Porcentaje_total <- 100
total$Porcentaje_valido <- 100
tabla <- rbind(tabla, total)
rownames(tabla) <- NULL
stopifnot(sum(tabla$fi[-nrow(tabla)]) == N,
abs(sum(tabla$Porcentaje_valido[-nrow(tabla)], na.rm = TRUE) - 100) < 1e-8)
list(tabla = tabla, n_validos = n_validos,
no_validos = N - n_validos, n_sistema = n_sistema)
}
P29 <- tabla_categoria("P29", 1:5)
P40 <- tabla_categoria("P40", 1:7, ordinal = TRUE)
# CLASES DE EDAD: límites reales para edades registradas en años enteros.
tabla_edad <- function(variable) {
x <- as.numeric(haven::zap_missing(base_original[[variable]]))
# El diccionario del SAV define 99 como perdido en P1 y P2.
stopifnot(!any(x == 99, na.rm = TRUE))
x <- x[!is.na(x)]
stopifnot(length(x) > 1, all(is.finite(x)), all(x == floor(x)))
n <- length(x)
minimo <- min(x)
maximo <- max(x)
rango <- maximo - minimo
k_teorico <- 1 + log2(n)
k_sturges <- ceiling(k_teorico)
# Con límites reales, la cobertura total es máximo - mínimo + 1.
cobertura <- rango + 1
amplitud <- max(1, ceiling(cobertura / k_sturges))
k_final <- ceiling(cobertura / amplitud)
limites <- minimo - 0.5 + (0:k_final) * amplitud
grupos <- cut(x, breaks = limites, right = FALSE, include.lowest = TRUE)
fi <- as.numeric(table(grupos))
li <- head(limites, -1)
ls <- tail(limites, -1)
tabla <- data.frame(
Clase = seq_len(k_final),
Intervalo_anos = paste(li + .5, ls - .5, sep = "–"),
Limite_real_inferior = li, Limite_real_superior = ls,
Marca_clase = (li + ls)/2, fi = fi, Fi = cumsum(fi),
hi = fi/n, Porcentaje_valido = 100 * fi/n,
Porcentaje_valido_acumulado = 100 * cumsum(fi)/n,
Porcentaje_total = 100 * fi/N
)
stopifnot(!anyNA(grupos), sum(fi) == n,
abs(sum(tabla$hi) - 1) < 1e-10, tail(tabla$Fi, 1) == n)
parametros <- data.frame(Variable = variable, N_total = N, n_validos = n,
Perdidos = N - n, Minimo = minimo, Maximo = maximo,
Rango = rango, Cobertura_real = cobertura,
Sturges_sin_redondear = k_teorico,
Clases_Sturges = k_sturges, Amplitud = amplitud,
Clases_finales = k_final)
list(tabla = tabla, parametros = parametros, x = x)
}
P1 <- tabla_edad("P1")
P2 <- tabla_edad("P2")
parametros_edades <- rbind(P1$parametros, P2$parametros)
control <- data.frame(Variable = c("P29", "P40", "P1", "P2"), Total = N,
Validos = c(P29$n_validos, P40$n_validos, length(P1$x), length(P2$x)),
No_validos = c(P29$no_validos, P40$no_validos, N-length(P1$x), N-length(P2$x)))
# Funciones de presentación: no modifican los valores originales.
redondear <- function(tabla, digitos = 2) {
numericas <- vapply(tabla, is.numeric, logical(1))
tabla[numericas] <- lapply(tabla[numericas], round, digits = digitos)
tabla
}
cat_interpretacion <- function(objeto) {
t <- objeto$tabla
v <- which(!is.na(t$Porcentaje_valido) & t$Respuesta != "TOTAL")
j <- v[which.max(t$fi[v])]
paste0('La respuesta más frecuente es «', t$Respuesta[j], '»: ', t$fi[j],
' respuestas (', round(t$Porcentaje_valido[j], 2),
' % de las respuestas válidas).')
}
edad_interpretacion <- function(objeto) {
t <- objeto$tabla
j <- which.max(t$fi)
paste0('La clase modal es ', t$Intervalo_anos[j], ' años: ', t$fi[j],
' respuestas (', round(t$Porcentaje_valido[j], 2),
' % de las edades válidas). La mediana sin agrupar es ', median(objeto$x), ' años.')
}
| Variable | Total | Validos | No_validos |
|---|---|---|---|
| P29 | 1203 | 677 | 526 |
| P40 | 1203 | 1191 | 12 |
| P1 | 1203 | 1161 | 42 |
| P2 | 1203 | 1162 | 41 |
Nota. La base contiene 1203 registros. En P29 hay 513 perdidos de sistema y 13 respuestas “No contesta”. En P40 hay una respuesta “No sabe” y 11 “No contesta”. En P1 y P2 se excluyen, respectivamente, 42 y 41 códigos 99. El tratamiento se sustenta en las etiquetas y metadatos del archivo.
fi es la frecuencia absoluta. El porcentaje total se calcula como 100 × fi / 1203. El porcentaje válido usa solo las respuestas sustantivas de cada pregunta. El símbolo “—” indica que el porcentaje no aplica a esa fila. Las sumas pueden diferir de 100 % por redondeo.
| Código | Respuesta | fi | % total | % válido |
|---|---|---|---|---|
| 1 | Yo administro todo el dinero y doy a mi pareja su parte | 185 | 15.38 | 27.33 |
| 2 | Mi cónyuge/pareja administra todo el dinero y me da mi parte | 126 | 10.47 | 18.61 |
| 3 | Juntamos todo el dinero y cada uno toma lo que necesita | 264 | 21.95 | 39.00 |
| 4 | Juntamos parte del dinero y mantenemos separado el resto | 66 | 5.49 | 9.75 |
| 5 | Mantenemos nuestro dinero separado | 36 | 2.99 | 5.32 |
| 9 | No contesta | 13 | 1.08 | — |
| NA | Sin dato en el archivo (perdido de sistema) | 513 | 42.64 | — |
| TOTAL | 1203 | 100.00 | 100.00 |
Nota. Fuente: elaboración propia con P29. Denominador válido: 677. No se presentan acumulados porque es una variable nominal: sus categorías no tienen un orden cuantitativo.
Interpretación: La respuesta más frecuente es «Juntamos todo el dinero y cada uno toma lo que necesita»: 264 respuestas (39 % de las respuestas válidas). El porcentaje válido describe a quienes dieron una respuesta sustantiva y no a los 1203 registros completos de la base.
| Código | Respuesta | fi | % total | % válido | % válido acum. |
|---|---|---|---|---|---|
| 1 | Me siento completamente satisfecho/a | 161 | 13.38 | 13.52 | 13.52 |
| 2 | Muy satisfecho/a | 406 | 33.75 | 34.09 | 47.61 |
| 3 | Bastante satisfecho/a | 409 | 34.00 | 34.34 | 81.95 |
| 4 | Ni satisfecho ni insatisfecho/a | 175 | 14.55 | 14.69 | 96.64 |
| 5 | Bastante insatisfecho/a | 25 | 2.08 | 2.10 | 98.74 |
| 6 | Muy insatisfecho/a | 11 | 0.91 | 0.92 | 99.66 |
| 7 | Completamente insatisfecho/a | 4 | 0.33 | 0.34 | 100.00 |
| 8 | No sabe | 1 | 0.08 | — | — |
| 9 | No contesta | 11 | 0.91 | — | — |
| TOTAL | 1203 | 100.00 | 100.00 | — |
Nota. Fuente: elaboración propia con P40. Denominador válido: 1191. Se conserva el orden ordinal de los códigos 1–7, desde completa satisfacción hasta completa insatisfacción. El acumulado avanza en ese sentido.
Interpretación: La respuesta más frecuente es «Bastante satisfecho/a»: 409 respuestas (34.34 % de las respuestas válidas). Las categorías 1–3 reúnen 976 respuestas, equivalentes a 81.95 % de las válidas; en conjunto expresan algún grado de satisfacción.
Se adopta Sturges como punto de partida: \(k^*=1+\log_2(n)\) y \(k_0=\lceil k^*\rceil\). Como las edades están registradas en años enteros, se usan límites reales desde mínimo − 0,5 hasta máximo + 0,5. Así:
\[ R=x_{max}-x_{min},\quad C=R+1,\quad h=\left\lceil\frac{C}{k_0}\right\rceil,\quad k_{final}=\left\lceil\frac{C}{h}\right\rceil. \]
Aquí C es la cobertura entre límites reales, h la amplitud entera y k final el número de clases necesarias para cubrir los datos. Redondear la amplitud puede cambiar el número final de clases respecto de Sturges. Esta decisión evita añadir clases vacías más allá de la cobertura necesaria. Se conservan las clases vacías dentro del rango observado.
| P1: mujer | P2: hombre | |
|---|---|---|
| N_total | 1203.0000 | 1203.0000 |
| n_validos | 1161.0000 | 1162.0000 |
| Perdidos | 42.0000 | 41.0000 |
| Minimo | 15.0000 | 18.0000 |
| Maximo | 60.0000 | 42.0000 |
| Rango | 45.0000 | 24.0000 |
| Cobertura_real | 46.0000 | 25.0000 |
| Sturges_sin_redondear | 11.1812 | 11.1824 |
| Clases_Sturges | 12.0000 | 12.0000 |
| Amplitud | 4.0000 | 3.0000 |
| Clases_finales | 12.0000 | 9.0000 |
Para P1, n = 1161, k₀ = 12, C = 46 y h = ⌈46/12⌉ = 4 años; se obtienen 12 clases. Para P2, n = 1162, k₀ = 12, C = 25 y h = ⌈25/12⌉ = 3 años; se obtienen 9 clases. Se agrupa cada variable de manera independiente.
| Edad (años) | Marca xi | fi | Fi | hi | % válido | % acum. |
|---|---|---|---|---|---|---|
| 15–18 | 16.5 | 13 | 13 | 0.0112 | 1.12 | 1.12 |
| 19–22 | 20.5 | 147 | 160 | 0.1266 | 12.66 | 13.78 |
| 23–26 | 24.5 | 536 | 696 | 0.4617 | 46.17 | 59.95 |
| 27–30 | 28.5 | 401 | 1097 | 0.3454 | 34.54 | 94.49 |
| 31–34 | 32.5 | 21 | 1118 | 0.0181 | 1.81 | 96.30 |
| 35–38 | 36.5 | 38 | 1156 | 0.0327 | 3.27 | 99.57 |
| 39–42 | 40.5 | 4 | 1160 | 0.0034 | 0.34 | 99.91 |
| 43–46 | 44.5 | 0 | 1160 | 0.0000 | 0.00 | 99.91 |
| 47–50 | 48.5 | 0 | 1160 | 0.0000 | 0.00 | 99.91 |
| 51–54 | 52.5 | 0 | 1160 | 0.0000 | 0.00 | 99.91 |
| 55–58 | 56.5 | 0 | 1160 | 0.0000 | 0.00 | 99.91 |
| 59–62 | 60.5 | 1 | 1161 | 0.0009 | 0.09 | 100.00 |
Nota. Fuente: elaboración propia con P1. Total válido: 1161; perdidos: 42. Los intervalos escritos como 15–18 incluyen ambas edades; sus límites reales son [14,5; 18,5). La marca de clase es el punto medio. Fi y el porcentaje acumulado se calculan de menor a mayor edad; hi = fi / n. La suma de fi es 1161 y la de hi es 1.
Interpretación: La clase modal es 23–26 años: 536 respuestas (46.17 % de las edades válidas). La mediana sin agrupar es 25 años. El valor 60 está presente una vez y se conserva, pues no figura como perdido; explica que se incluyan intervalos altos, algunos con frecuencia cero. No se elimina por ser poco frecuente.
| Edad (años) | Marca xi | fi | Fi | hi | % válido | % acum. |
|---|---|---|---|---|---|---|
| 18–20 | 19 | 44 | 44 | 0.0379 | 3.79 | 3.79 |
| 21–23 | 22 | 35 | 79 | 0.0301 | 3.01 | 6.80 |
| 24–26 | 25 | 278 | 357 | 0.2392 | 23.92 | 30.72 |
| 27–29 | 28 | 237 | 594 | 0.2040 | 20.40 | 51.12 |
| 30–32 | 31 | 409 | 1003 | 0.3520 | 35.20 | 86.32 |
| 33–35 | 34 | 134 | 1137 | 0.1153 | 11.53 | 97.85 |
| 36–38 | 37 | 9 | 1146 | 0.0077 | 0.77 | 98.62 |
| 39–41 | 40 | 15 | 1161 | 0.0129 | 1.29 | 99.91 |
| 42–44 | 43 | 1 | 1162 | 0.0009 | 0.09 | 100.00 |
Nota. Fuente: elaboración propia con P2. Total válido: 1162; perdidos: 41. El primer intervalo 18–20 tiene límites reales [17,5; 20,5). La suma de fi es 1162 y la de hi es 1. El último intervalo 42–44 cubre el máximo observado, 42, aunque el límite superior de clase sea mayor.
Interpretación: La clase modal es 30–32 años: 409 respuestas (35.2 % de las edades válidas). La mediana sin agrupar es 29 años.
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## loaded via a namespace (and not attached):
## [1] vctrs_0.7.1 cli_3.6.5 knitr_1.52 rlang_1.1.7
## [5] xfun_0.56 otel_0.2.0 forcats_1.0.1 haven_2.5.5
## [9] jsonlite_2.0.0 glue_1.8.0 htmltools_0.5.9 sass_0.4.10
## [13] hms_1.1.4 rmarkdown_2.32 evaluate_1.0.5 jquerylib_0.1.4
## [17] tibble_3.3.1 tzdb_0.5.0 fastmap_1.2.0 yaml_2.3.12
## [21] lifecycle_1.0.5 compiler_4.5.2 pkgconfig_2.0.3 rstudioapi_0.18.0
## [25] digest_0.6.39 R6_2.6.1 readr_2.2.0 pillar_1.11.1
## [29] magrittr_2.0.4 bslib_0.10.0 tools_4.5.2 cachem_1.1.0