set.seed(123)
n <- 2500 # más de 2000 observaciones
datos <- data.frame(
ingreso_mensual = rnorm(n, mean = 1500, sd = 400), # numérica continua
edad = sample(18:80, n, replace = TRUE), # numérica discreta
horas_trabajo = rpois(n, lambda = 8), # numérica discreta
gasto_alimentos = rnorm(n, mean = 300, sd = 50), # numérica continua
gasto_transporte = rnorm(n, mean = 120, sd = 30), # numérica continua
sexo = factor(sample(c("Hombre", "Mujer"), n, replace = TRUE)), # categórica nominal
estado_civil = factor(sample(c("Soltero", "Casado", "Divorciado", "Viudo"),
n, replace = TRUE)), # categórica nominal
nivel_educativo = factor(sample(c("Primaria", "Secundaria", "Superior"),
n, replace = TRUE),
ordered = TRUE), # categórica ordinal
region = factor(sample(c("Costa", "Sierra", "Selva"), n, replace = TRUE)), # nominal
acceso_internet = factor(sample(c("Sí", "No"), n, replace = TRUE)), # nominal
estrato = factor(sample(1:5, n, replace = TRUE), ordered = TRUE), # ordinal
num_hijos = rpois(n, lambda = 2), # numérica discreta
nivel_satisfaccion = factor(sample(1:10, n, replace = TRUE),
ordered = TRUE), # ordinal
ahorro_mensual = rnorm(n, mean = 200, sd = 100), # numérica continua
consumo_energia = rnorm(n, mean = 50, sd = 12), # numérica continua
tipo_vivienda = factor(sample(c("Propia", "Alquilada", "Familiar"),
n, replace = TRUE)) # nominal extra
)
head(datos)
## ingreso_mensual edad horas_trabajo gasto_alimentos gasto_transporte sexo
## 1 1275.810 56 8 270.2122 153.11752 Hombre
## 2 1407.929 31 8 243.7237 105.71713 Hombre
## 3 2123.483 39 7 290.6137 150.27149 Hombre
## 4 1528.203 41 3 248.0979 127.57367 Mujer
## 5 1551.715 39 9 367.1230 142.23150 Hombre
## 6 2186.026 74 8 306.1756 66.12078 Mujer
## estado_civil nivel_educativo region acceso_internet estrato num_hijos
## 1 Viudo Primaria Selva Sí 2 2
## 2 Casado Primaria Sierra Sí 2 3
## 3 Casado Secundaria Selva No 1 0
## 4 Soltero Superior Sierra Sí 2 3
## 5 Soltero Secundaria Costa Sí 2 3
## 6 Casado Superior Selva Sí 2 3
## nivel_satisfaccion ahorro_mensual consumo_energia tipo_vivienda
## 1 4 106.46317 46.37502 Alquilada
## 2 6 229.24309 27.05772 Alquilada
## 3 8 325.56069 47.47791 Propia
## 4 5 15.49504 57.11148 Propia
## 5 8 23.37423 61.85887 Alquilada
## 6 10 256.31744 52.03530 Familiar
análisis por grupos que reciba un dataframe, una
variable numérica y una variable categórica, y retorne un dataframe
con
todas las medidas estadísticas calculadas para cada grupo. Esta
función
utilizará bucles o funciones apply para iterar sobre grupos
# Función de análisis por grupos
library(moments)
## Warning: package 'moments' was built under R version 4.5.2
analisis_por_grupos <- function(df, var_num, var_cat) {
x <- df[[var_num]]
g <- df[[var_cat]]
# Filtrar NA
completo <- complete.cases(x, g)
x <- x[completo]
g <- g[completo]
grupos <- unique(g)
resultados <- lapply(grupos, function(gr) {
datos_g <- x[g == gr]
data.frame(
Grupo = as.character(gr),
Media = mean(datos_g),
Mediana = median(datos_g),
Desviacion = sd(datos_g),
Varianza = var(datos_g),
Coef_Variacion = sd(datos_g) / mean(datos_g),
Minimo = min(datos_g),
Maximo = max(datos_g),
Rango = max(datos_g) - min(datos_g),
IQR = IQR(datos_g),
Q1 = quantile(datos_g, 0.25),
Q3 = quantile(datos_g, 0.75),
Asimetria = skewness(datos_g),
Curtosis = kurtosis(datos_g)
)
})
final <- do.call(rbind, resultados)
rownames(final) <- NULL
return(final)
}
# ejemplo
resultado <- analisis_por_grupos(datos, "ingreso_mensual", "estado_civil")
resultado
## Grupo Media Mediana Desviacion Varianza Coef_Variacion Minimo
## 1 Viudo 1506.124 1496.350 406.9835 165635.6 0.2702191 280.8556
## 2 Casado 1510.585 1513.820 403.2035 162573.0 0.2669187 421.8683
## 3 Soltero 1503.643 1492.226 380.5357 144807.4 0.2530758 458.3847
## 4 Divorciado 1497.030 1499.846 391.7100 153436.7 0.2616580 360.5813
## Maximo Rango IQR Q1 Q3 Asimetria Curtosis
## 1 2796.416 2515.560 516.7646 1246.445 1763.210 0.09186004 3.117407
## 2 2816.207 2394.339 556.7529 1237.403 1794.156 -0.01520666 2.795052
## 3 2856.148 2397.764 532.4158 1241.518 1773.934 0.16520289 2.946306
## 4 2618.956 2258.375 513.0577 1239.038 1752.096 -0.12417518 2.950427
detección de outliers que identifique valores atípicos
mediante el método del rango intercuartílico y retorne los índices
de las
observaciones atípicas con sus valores correspondientes.
detectar_outliers <- function(x) {
# Eliminar NA pero conservar indices
x_na <- is.na(x)
x_clean <- x[!x_na]
# Calcular cuartiles
Q1 <- quantile(x_clean, 0.25)
Q3 <- quantile(x_clean, 0.75)
IQR_value <- Q3 - Q1
# Límites para detectar outliers
lim_inf <- Q1 - 1.5 * IQR_value
lim_sup <- Q3 + 1.5 * IQR_value
# Identificar outliers
indices_outliers <- which(x < lim_inf | x > lim_sup)
valores_outliers <- x[indices_outliers]
# Retorno en lista
return(list(
limite_inferior = lim_inf,
limite_superior = lim_sup,
indices = indices_outliers,
valores = valores_outliers
))
}
# ejemplo
outliers_ingreso <- detectar_outliers(datos$ingreso_mensual)
outliers_ingreso
## $limite_inferior
## 25%
## 451.5254
##
## $limite_superior
## 75%
## 2555.348
##
## $indices
## [1] 164 416 456 591 747 842 1011 1012 1126 1324 1435 1622 1703 1851 1950
## [16] 2367 2383 2432
##
## $valores
## [1] 2796.4160 442.7404 435.6309 376.0901 2576.6856 2573.9436 2618.9565
## [8] 2632.8904 2773.6178 2856.1483 421.8683 2816.2070 280.8556 448.2698
## [15] 2626.4337 2708.8417 360.5813 2594.0837
tabla de frecuencias que genere tablas completas con
frecuencias absolutas, relativas y acumuladas, funcionando para
variables
categóricas y numéricas discretizadas automáticamente.
tabla_frecuencias <- function(x, bins = 5) {
# Eliminar NAs
x <- x[!is.na(x)]
# Si es numérica, discretizamos automáticamente
if (is.numeric(x)) {
x_cat <- cut(x, breaks = bins, include.lowest = TRUE)
} else {
x_cat <- as.factor(x)
}
# Frecuencias absolutas
frec_abs <- table(x_cat)
# Frecuencias relativas
frec_rel <- prop.table(frec_abs)
# Acumuladas
frec_abs_acum <- cumsum(frec_abs)
frec_rel_acum <- cumsum(frec_rel)
# Crear dataframe final
tabla <- data.frame(
Categoria = names(frec_abs),
Frecuencia_Absoluta = as.numeric(frec_abs),
Frecuencia_Relativa = round(as.numeric(frec_rel), 4),
Frecuencia_Absoluta_Acumulada = as.numeric(frec_abs_acum),
Frecuencia_Relativa_Acumulada = round(as.numeric(frec_rel_acum), 4)
)
rownames(tabla) <- NULL
return(tabla)
}
# ejemplo
tabla_ingreso <- tabla_frecuencias(datos$sexo, bins = 7)
tabla_ingreso
## Categoria Frecuencia_Absoluta Frecuencia_Relativa
## 1 Hombre 1223 0.4892
## 2 Mujer 1277 0.5108
## Frecuencia_Absoluta_Acumulada Frecuencia_Relativa_Acumulada
## 1 1223 0.4892
## 2 2500 1.0000
3era seccion
# 1. Importación del dataset y verificación de carga
datos <- datos # aquí normalmente usarías read.csv(), readRDS(), etc.
# Verificación de carga exitosa
if (exists("datos")) {
cat("✔ El dataset se cargó correctamente.\n")
cat("Dimensiones:", dim(datos), "\n")
} else {
stop("✘ Error: el dataset no se cargó.")
}
## ✔ El dataset se cargó correctamente.
## Dimensiones: 2500 16
# 2. Identificación de tipos de variables
# Tipos de variables
str(datos)
## 'data.frame': 2500 obs. of 16 variables:
## $ ingreso_mensual : num 1276 1408 2123 1528 1552 ...
## $ edad : int 56 31 39 41 39 74 68 23 42 41 ...
## $ horas_trabajo : int 8 8 7 3 9 8 5 7 9 9 ...
## $ gasto_alimentos : num 270 244 291 248 367 ...
## $ gasto_transporte : num 153 106 150 128 142 ...
## $ sexo : Factor w/ 2 levels "Hombre","Mujer": 1 1 1 2 1 2 2 1 2 2 ...
## $ estado_civil : Factor w/ 4 levels "Casado","Divorciado",..: 4 1 1 3 3 1 4 3 1 2 ...
## $ nivel_educativo : Ord.factor w/ 3 levels "Primaria"<"Secundaria"<..: 1 1 2 3 2 3 3 1 3 3 ...
## $ region : Factor w/ 3 levels "Costa","Selva",..: 2 3 2 3 1 2 3 1 3 2 ...
## $ acceso_internet : Factor w/ 2 levels "No","Sí": 2 2 1 2 2 2 1 2 1 1 ...
## $ estrato : Ord.factor w/ 5 levels "1"<"2"<"3"<"4"<..: 2 2 1 2 2 2 3 2 1 3 ...
## $ num_hijos : int 2 3 0 3 3 3 1 2 2 0 ...
## $ nivel_satisfaccion: Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 4 6 8 5 8 10 10 9 2 2 ...
## $ ahorro_mensual : num 106.5 229.2 325.6 15.5 23.4 ...
## $ consumo_energia : num 46.4 27.1 47.5 57.1 61.9 ...
## $ tipo_vivienda : Factor w/ 3 levels "Alquilada","Familiar",..: 1 1 3 3 1 2 2 3 1 3 ...
# Tabla resumen de tipos
sapply(datos, class)
## $ingreso_mensual
## [1] "numeric"
##
## $edad
## [1] "integer"
##
## $horas_trabajo
## [1] "integer"
##
## $gasto_alimentos
## [1] "numeric"
##
## $gasto_transporte
## [1] "numeric"
##
## $sexo
## [1] "factor"
##
## $estado_civil
## [1] "factor"
##
## $nivel_educativo
## [1] "ordered" "factor"
##
## $region
## [1] "factor"
##
## $acceso_internet
## [1] "factor"
##
## $estrato
## [1] "ordered" "factor"
##
## $num_hijos
## [1] "integer"
##
## $nivel_satisfaccion
## [1] "ordered" "factor"
##
## $ahorro_mensual
## [1] "numeric"
##
## $consumo_energia
## [1] "numeric"
##
## $tipo_vivienda
## [1] "factor"
# 3.1 Detección de valores faltantes
# Conteo de NA por variable
naResumen <- data.frame(
Variable = names(datos),
NA_Conteo = sapply(datos, function(x) sum(is.na(x))),
NA_Porcentaje = round(sapply(datos, function(x) mean(is.na(x)) * 100), 3)
)
naResumen
## Variable NA_Conteo NA_Porcentaje
## ingreso_mensual ingreso_mensual 0 0
## edad edad 0 0
## horas_trabajo horas_trabajo 0 0
## gasto_alimentos gasto_alimentos 0 0
## gasto_transporte gasto_transporte 0 0
## sexo sexo 0 0
## estado_civil estado_civil 0 0
## nivel_educativo nivel_educativo 0 0
## region region 0 0
## acceso_internet acceso_internet 0 0
## estrato estrato 0 0
## num_hijos num_hijos 0 0
## nivel_satisfaccion nivel_satisfaccion 0 0
## ahorro_mensual ahorro_mensual 0 0
## consumo_energia consumo_energia 0 0
## tipo_vivienda tipo_vivienda 0 0
#📌 Decisión de tratamiento (documentada):
#Variables numéricas:
#Se reemplazarán NA con la mediana, para no distorsionar con valores extremos.
#Se marcará un indicador binario (_na_flag) para preservar la información faltante original.
#Variables categóricas:
#Se reemplazarán NA con la categoría "Desconocido".
#También se crea un marcador (_na_flag).
seccion 6
#1. Histogramas con Curvas de Densidad Ingreso mensual
library(ggplot2)
ggplot(datos, aes(x = ingreso_mensual)) +
geom_histogram(aes(y = ..density..), bins = 35, fill = "skyblue", alpha = 0.7) +
geom_density(color = "red", size = 1.2) +
labs(
title = "Distribución del Ingreso Mensual",
x = "Ingreso mensual (S/.)",
y = "Densidad"
) +
theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

#2. Boxplots Comparativos Entre Grupos Ingreso mensual por sexo
ggplot(datos, aes(x = sexo, y = ingreso_mensual, fill = sexo)) +
geom_boxplot(alpha = 0.8) +
labs(
title = "Comparación del Ingreso Mensual por Sexo",
x = "Sexo",
y = "Ingreso mensual (S/.)"
) +
theme_minimal() +
scale_fill_brewer(palette = "Set2")

#3. Gráficos de Dispersión (Scatterplot) Ingreso vs. Ahorro mensual
ggplot(datos, aes(x = ingreso_mensual, y = ahorro_mensual)) +
geom_point(alpha = 0.35, color = "blue") +
geom_smooth(method = "lm", color = "red") +
labs(
title = "Relación entre Ingreso Mensual y Ahorro",
x = "Ingreso mensual (S/.)",
y = "Ahorro mensual (S/.)"
) +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

#4. Gráficos de Barras para Variables Categóricas Distribución del sexo
ggplot(datos, aes(x = sexo, fill = sexo)) +
geom_bar() +
labs(
title = "Distribución por Sexo",
x = "Sexo",
y = "Frecuencia"
) +
theme_minimal() +
scale_fill_brewer(palette = "Set2")

#5. Visualizaciones que Comunican Hallazgos Relevantes Boxplot del nivel de satisfacción por estrato socioeconómico
ggplot(datos, aes(x = estrato, y = as.numeric(nivel_satisfaccion), fill = estrato)) +
geom_boxplot() +
labs(
title = "Nivel de Satisfacción según Estrato Socioeconómico",
x = "Estrato",
y = "Nivel de satisfacción"
) +
theme_minimal()
