title: “Determinación de Sexo mediante Análisis de Clavícula Derecha” subtitle: “Análisis Discriminante y Regresión Logística” author: “Ennie Monserrat Abrego Camacho” date: “2025-11-20” output: html_document: toc: true toc_float: true toc_depth: 3 theme: cerulean highlight: tango code_folding: show —
knitr::opts_chunk$set( echo = TRUE, warning = FALSE, message = FALSE, fig.width = 10, fig.height = 6 )
# 1. INTRODUCCIÓN
En este archivo se analiza el dimorfismo sexual en la clavícula derecha mediante variables métricas para determinar el sexo en restos óseos. Haciendo uso de técnicas de análisis discriminante y regresión logística con las variables:
- **LMCD**: Longitud Máxima de Clavícula Derecha
- **C12CD**: Circunferencia en Punto Medio de Clavícula Derecha
# 2. CARGA Y PREPARACIÓN DE DATOS
``` r
# Cargar librerías
library(pacman)
p_load(haven, dplyr, ggplot2, MASS, car, effsize, knitr, kableExtra, gridExtra)
# Cargar datos
setwd("C:/Users/OSCAR/Desktop/Estadistica forense EMAC")
Hombro <- read_sav("Datos hombro.sav")
# Convertir sexoN a factor
Hombro$sexoN <- factor(Hombro$sexoN,
levels = c(1, 2),
labels = c("Hombre", "Mujer"))
# Crear variable numérica para regresión logística
Hombro$sexoN_num <- ifelse(Hombro$sexoN == "Hombre", 1, 0)
# Verificar datos
cat("Total de casos:", nrow(Hombro), "\n")
## Total de casos: 80
cat("Distribución por sexo:\n")
## Distribución por sexo:
table(Hombro$sexoN)
##
## Hombre Mujer
## 50 30
# LMCD
res_lmcd <- Hombro %>%
group_by(sexoN) %>%
summarise(
n = sum(!is.na(LMCD)),
Media = round(mean(LMCD, na.rm = TRUE), 2),
DE = round(sd(LMCD, na.rm = TRUE), 2),
Min = round(min(LMCD, na.rm = TRUE), 2),
Max = round(max(LMCD, na.rm = TRUE), 2)
)
kable(res_lmcd, caption = "Tabla 1. Estadísticas descriptivas de LMCD por sexo") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| sexoN | n | Media | DE | Min | Max |
|---|---|---|---|---|---|
| Hombre | 35 | 151.03 | 10.17 | 132.50 | 175.50 |
| Mujer | 25 | 131.30 | 7.56 | 116.48 | 144.69 |
# C12CD
res_c12cd <- Hombro %>%
group_by(sexoN) %>%
summarise(
n = sum(!is.na(C12CD)),
Media = round(mean(C12CD, na.rm = TRUE), 2),
DE = round(sd(C12CD, na.rm = TRUE), 2),
Min = round(min(C12CD, na.rm = TRUE), 2),
Max = round(max(C12CD, na.rm = TRUE), 2)
)
kable(res_c12cd, caption = "Tabla 2. Estadísticas descriptivas de C12CD por sexo") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| sexoN | n | Media | DE | Min | Max |
|---|---|---|---|---|---|
| Hombre | 46 | 35.94 | 2.33 | 31 | 42 |
| Mujer | 29 | 30.29 | 2.83 | 26 | 36 |
# Calcular Cohen's d
cohend_result <- cohen.d(Hombro$LMCD, Hombro$sexoN, na.rm = TRUE)
print(cohend_result)
##
## Cohen's d
##
## d estimate: 2.148866 (large)
## 95 percent confidence interval:
## lower upper
## 1.493929 2.803803
Interpretación: El tamaño del efecto es muy grande (d = 2.149), se encuentra por encima del valor para ser clasificado como grande, lo que nos indica diferencias sustanciales entre sexos.
# Gráfico de densidad LMCD
p1 <- ggplot(Hombro, aes(x = LMCD, fill = sexoN)) +
geom_density(alpha = 0.6) +
scale_fill_manual(values = c("lightblue", "pink")) +
labs(
title = "Gráfica 1. Distribución de LMCD por Sexo",
x = "Longitud Máxima de Clavícula Derecha (mm)",
y = "Densidad",
fill = "Sexo"
) +
theme_minimal() +
theme(legend.position = "bottom")
# Boxplot LMCD
p2 <- ggplot(Hombro, aes(x = sexoN, y = LMCD, fill = sexoN)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("lightblue", "pink")) +
labs(
title = "Gráfica 2. Boxplot de LMCD por Sexo",
x = "Sexo",
y = "LMCD (mm)"
) +
theme_minimal() +
theme(legend.position = "none")
# Boxplot C12CD
p3 <- ggplot(Hombro, aes(x = sexoN, y = C12CD, fill = sexoN)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("lightblue", "pink")) +
labs(
title = "Gráfica 3. Boxplot de C12CD por Sexo",
x = "Sexo",
y = "C12CD (mm)"
) +
theme_minimal() +
theme(legend.position = "none")
# Dispersión bivariada
p4 <- ggplot(Hombro, aes(x = LMCD, y = C12CD, color = sexoN)) +
geom_point(size = 3, alpha = 0.6) +
scale_color_manual(values = c("blue", "red")) +
labs(
title = "Gráfica 4. Relación LMCD vs C12CD por Sexo",
x = "LMCD (mm)",
y = "C12CD (mm)",
color = "Sexo"
) +
theme_minimal() +
theme(legend.position = "bottom")
# Mostrar gráficas
library(gridExtra)
grid.arrange(p1, p2, p3, p4, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 5 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 20 rows containing missing values or values outside the scale range
## (`geom_point()`).
Interpretación general: Las visualizaciones presentadas permiten evaluar el dimorfismo sexual en medidas de la clavícula derecha. La combinación de gráficos univariados (densidad y boxplots) con análisis bivariado (dispersión) proporciona una comprensión integral de las diferencias morfométricas entre sexos, información fundamental para desarrollar métodos estadísticos de estimación del sexo.
# Crear tabla de resultados
normalidad <- data.frame(
Variable = c("LMCD", "LMCD", "C12CD", "C12CD"),
Sexo = c("Hombre", "Mujer", "Hombre", "Mujer"),
p_valor = c(
shapiro.test(Hombro$LMCD[Hombro$sexoN == "Hombre"])$p.value,
shapiro.test(Hombro$LMCD[Hombro$sexoN == "Mujer"])$p.value,
shapiro.test(Hombro$C12CD[Hombro$sexoN == "Hombre"])$p.value,
shapiro.test(Hombro$C12CD[Hombro$sexoN == "Mujer"])$p.value
)
)
normalidad$p_valor <- round(normalidad$p_valor, 4)
normalidad$Normal <- ifelse(normalidad$p_valor > 0.05, "Sí ✓", "No ✗")
kable(normalidad, caption = "Tabla 3. Pruebas de normalidad (Shapiro-Wilk)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Variable | Sexo | p_valor | Normal |
|---|---|---|---|
| LMCD | Hombre | 0.1055 | Sí ✓ |
| LMCD | Mujer | 0.8169 | Sí ✓ |
| C12CD | Hombre | 0.0628 | Sí ✓ |
| C12CD | Mujer | 0.2313 | Sí ✓ |
Conclusión: Todas las variables siguen distribución normal (p > 0.05), justificando el uso de pruebas paramétricas.
# Prueba t para LMCD
t_lmcd <- t.test(LMCD ~ sexoN, data = Hombro, var.equal = TRUE)
# Prueba t para C12CD
t_c12cd <- t.test(C12CD ~ sexoN, data = Hombro, var.equal = TRUE)
# Crear tabla de resultados
resultados_t <- data.frame(
Variable = c("LMCD", "C12CD"),
t = c(t_lmcd$statistic, t_c12cd$statistic),
gl = c(t_lmcd$parameter, t_c12cd$parameter),
p_valor = c(t_lmcd$p.value, t_c12cd$p.value),
Diferencia = c(diff(t_lmcd$estimate), diff(t_c12cd$estimate)),
IC_inferior = c(t_lmcd$conf.int[1], t_c12cd$conf.int[1]),
IC_superior = c(t_lmcd$conf.int[2], t_c12cd$conf.int[2])
)
resultados_t$p_valor <- format(resultados_t$p_valor, scientific = TRUE, digits = 3)
kable(resultados_t,
digits = 3,
caption = "Tabla 4. Resultados de pruebas t de Student") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Variable | t | gl | p_valor | Diferencia | IC_inferior | IC_superior |
|---|---|---|---|---|---|---|
| LMCD | 8.206 | 58 | 2.77e-11 | -19.724 | 14.912 | 24.535 |
| C12CD | 9.410 | 73 | 3.18e-14 | -5.652 | 4.455 | 6.849 |
Interpretación: Ambas variables muestran diferencias altamente significativas entre sexos (p < 0.001).
# Crear dataset completo
Hombro_completo <- Hombro[complete.cases(Hombro[, c("sexoN", "LMCD")]), ]
# Ajustar modelo
lda_1 <- lda(sexoN ~ LMCD, data = Hombro_completo)
print(lda_1)
## Call:
## lda(sexoN ~ LMCD, data = Hombro_completo)
##
## Prior probabilities of groups:
## Hombre Mujer
## 0.5833333 0.4166667
##
## Group means:
## LMCD
## Hombre 151.0263
## Mujer 131.3026
##
## Coefficients of linear discriminants:
## LD1
## LMCD 0.1089485
# Predicciones
pred_lda_1 <- predict(lda_1, Hombro_completo)
# Matriz de confusión
tabla_lda_1 <- table(Real = Hombro_completo$sexoN,
Predicho = pred_lda_1$class)
kable(tabla_lda_1, caption = "Tabla 5. Matriz de confusión - LDA (LMCD)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Hombre | Mujer | |
|---|---|---|
| Hombre | 32 | 3 |
| Mujer | 3 | 22 |
# Precisión
accuracy_lda_1 <- sum(diag(tabla_lda_1)) / sum(tabla_lda_1) * 100
cat("\nPrecisión del modelo:", round(accuracy_lda_1, 2), "%\n")
##
## Precisión del modelo: 90 %
# Caso ejemplo
nuevo_caso <- data.frame(LMCD = 175)
pred_ejemplo <- predict(lda_1, nuevo_caso)
cat("Para LMCD = 175 mm:\n")
## Para LMCD = 175 mm:
cat("Clasificación:", as.character(pred_ejemplo$class), "\n")
## Clasificación: Hombre
cat("Probabilidad Hombre:", round(pred_ejemplo$posterior[1], 4), "\n")
## Probabilidad Hombre: 0.9997
cat("Probabilidad Mujer:", round(pred_ejemplo$posterior[2], 4), "\n")
## Probabilidad Mujer: 3e-04
# Crear dataset completo
Hombro_completo2 <- Hombro[complete.cases(Hombro[, c("sexoN", "LMCD", "C12CD")]), ]
# Ajustar modelo
lda_2 <- lda(sexoN ~ LMCD + C12CD, data = Hombro_completo2)
print(lda_2)
## Call:
## lda(sexoN ~ LMCD + C12CD, data = Hombro_completo2)
##
## Prior probabilities of groups:
## Hombre Mujer
## 0.5833333 0.4166667
##
## Group means:
## LMCD C12CD
## Hombre 151.0263 35.77151
## Mujer 131.3026 30.31206
##
## Coefficients of linear discriminants:
## LD1
## LMCD -0.0727066
## C12CD -0.2559868
# Predicciones
pred_lda_2 <- predict(lda_2, Hombro_completo2)
# Matriz de confusión
tabla_lda_2 <- table(Real = Hombro_completo2$sexoN,
Predicho = pred_lda_2$class)
kable(tabla_lda_2, caption = "Tabla 6. Matriz de confusión - LDA (LMCD + C12CD)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Hombre | Mujer | |
|---|---|---|
| Hombre | 34 | 1 |
| Mujer | 1 | 24 |
# Precisión
accuracy_lda_2 <- sum(diag(tabla_lda_2)) / sum(tabla_lda_2) * 100
cat("\nPrecisión del modelo:", round(accuracy_lda_2, 2), "%\n")
##
## Precisión del modelo: 96.67 %
cat("Mejora respecto a modelo univariado:",
round(accuracy_lda_2 - accuracy_lda_1, 2), "puntos porcentuales\n")
## Mejora respecto a modelo univariado: 6.67 puntos porcentuales
# Casos ejemplo
casos <- data.frame(
LMCD = c(145, 155),
C12CD = c(33, 37)
)
pred_casos <- predict(lda_2, casos)
for(i in 1:nrow(casos)) {
cat("\nCaso", i, "- LMCD =", casos$LMCD[i], "mm, C12CD =", casos$C12CD[i], "mm:\n")
cat("Clasificación:", as.character(pred_casos$class[i]), "\n")
cat("Probabilidad Hombre:", round(pred_casos$posterior[i, 1], 4), "\n")
}
##
## Caso 1 - LMCD = 145 mm, C12CD = 33 mm:
## Clasificación: Hombre
## Probabilidad Hombre: 0.7495
##
## Caso 2 - LMCD = 155 mm, C12CD = 37 mm:
## Clasificación: Hombre
## Probabilidad Hombre: 0.9977
# Ajustar modelo
logit_1 <- glm(sexoN_num ~ LMCD,
data = Hombro_completo,
family = binomial)
summary(logit_1)
##
## Call:
## glm(formula = sexoN_num ~ LMCD, family = binomial, data = Hombro_completo)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -41.72697 10.79320 -3.866 0.000111 ***
## LMCD 0.29945 0.07717 3.880 0.000104 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 81.503 on 59 degrees of freedom
## Residual deviance: 32.430 on 58 degrees of freedom
## AIC: 36.43
##
## Number of Fisher Scoring iterations: 6
# Odds Ratio
or_lmcd <- exp(coef(logit_1)[2])
cat("\nOdds Ratio para LMCD:", round(or_lmcd, 4), "\n")
##
## Odds Ratio para LMCD: 1.3491
cat("Interpretación: Por cada mm adicional en LMCD,\n")
## Interpretación: Por cada mm adicional en LMCD,
cat("el odds de ser Hombre aumenta", round((or_lmcd-1)*100, 2), "%\n")
## el odds de ser Hombre aumenta 34.91 %
# Predicciones
prob_logit_1 <- predict(logit_1, type = "response")
pred_clase_1 <- ifelse(prob_logit_1 > 0.5, 1, 0)
# Matriz de confusión
tabla_logit_1 <- table(Real = Hombro_completo$sexoN_num,
Predicho = pred_clase_1)
kable(tabla_logit_1, caption = "Tabla 7. Matriz de confusión - Logit (LMCD)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| 0 | 1 | |
|---|---|---|
| 0 | 22 | 3 |
| 1 | 3 | 32 |
# Precisión
accuracy_logit_1 <- sum(diag(tabla_logit_1)) / sum(tabla_logit_1) * 100
cat("\nPrecisión del modelo:", round(accuracy_logit_1, 2), "%\n")
##
## Precisión del modelo: 90 %
# Varios valores de LMCD
lmcd_valores <- c(135, 145, 155, 165)
probabilidades <- predict(logit_1,
newdata = data.frame(LMCD = lmcd_valores),
type = "response")
ejemplos_logit1 <- data.frame(
LMCD = lmcd_valores,
Prob_Hombre = round(probabilidades, 4),
Porcentaje = paste0(round(probabilidades * 100, 2), "%"),
Clasificación = ifelse(probabilidades > 0.5, "Hombre", "Mujer")
)
kable(ejemplos_logit1,
caption = "Tabla 8. Ejemplos de predicción con modelo logístico (LMCD)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| LMCD | Prob_Hombre | Porcentaje | Clasificación |
|---|---|---|---|
| 135 | 0.2140 | 21.4% | Mujer |
| 145 | 0.8447 | 84.47% | Hombre |
| 155 | 0.9909 | 99.09% | Hombre |
| 165 | 0.9995 | 99.95% | Hombre |
# Ajustar modelo
logit_2 <- glm(sexoN_num ~ LMCD + C12CD,
data = Hombro_completo2,
family = binomial)
summary(logit_2)
##
## Call:
## glm(formula = sexoN_num ~ LMCD + C12CD, family = binomial, data = Hombro_completo2)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -69.0885 22.9591 -3.009 0.00262 **
## LMCD 0.3067 0.1117 2.747 0.00602 **
## C12CD 0.8022 0.3405 2.356 0.01847 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 81.503 on 59 degrees of freedom
## Residual deviance: 18.731 on 57 degrees of freedom
## AIC: 24.731
##
## Number of Fisher Scoring iterations: 8
# Odds Ratios
or_multivariado <- exp(coef(logit_2)[-1])
cat("\nOdds Ratios:\n")
##
## Odds Ratios:
cat("LMCD:", round(or_multivariado[1], 4),
"- Aumento del", round((or_multivariado[1]-1)*100, 2), "%\n")
## LMCD: 1.359 - Aumento del 35.9 %
cat("C12CD:", round(or_multivariado[2], 4),
"- Aumento del", round((or_multivariado[2]-1)*100, 2), "%\n")
## C12CD: 2.2305 - Aumento del 123.05 %
# Predicciones
prob_logit_2 <- predict(logit_2, type = "response")
pred_clase_2 <- ifelse(prob_logit_2 > 0.5, 1, 0)
# Matriz de confusión
tabla_logit_2 <- table(Real = Hombro_completo2$sexoN_num,
Predicho = pred_clase_2)
kable(tabla_logit_2, caption = "Tabla 9. Matriz de confusión - Logit (LMCD + C12CD)") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| 0 | 1 | |
|---|---|---|
| 0 | 24 | 1 |
| 1 | 1 | 34 |
# Precisión
accuracy_logit_2 <- sum(diag(tabla_logit_2)) / sum(tabla_logit_2) * 100
cat("\nPrecisión del modelo:", round(accuracy_logit_2, 2), "%\n")
##
## Precisión del modelo: 96.67 %
# Varios casos
casos_logit2 <- data.frame(
LMCD = c(135, 145, 155, 165),
C12CD = c(31, 33, 37, 39)
)
prob_casos <- predict(logit_2,
newdata = casos_logit2,
type = "response")
ejemplos_logit2 <- cbind(
casos_logit2,
Prob_Hombre = round(prob_casos, 4),
Porcentaje = paste0(round(prob_casos * 100, 2), "%"),
Clasificación = ifelse(prob_casos > 0.5, "Hombre", "Mujer")
)
kable(ejemplos_logit2,
caption = "Tabla 10. Ejemplos de predicción con modelo logístico bivariado") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| LMCD | C12CD | Prob_Hombre | Porcentaje | Clasificación |
|---|---|---|---|---|
| 135 | 31 | 0.0567 | 5.67% | Mujer |
| 145 | 33 | 0.8654 | 86.54% | Hombre |
| 155 | 37 | 0.9997 | 99.97% | Hombre |
| 165 | 39 | 1.0000 | 100% | Hombre |
# Crear tabla comparativa
comparacion <- data.frame(
Modelo = c("LDA (LMCD)", "LDA (LMCD + C12CD)",
"Logit (LMCD)", "Logit (LMCD + C12CD)"),
Variables = c("1", "2", "1", "2"),
Precisión = c(accuracy_lda_1, accuracy_lda_2,
accuracy_logit_1, accuracy_logit_2),
Errores = c(sum(tabla_lda_1) - sum(diag(tabla_lda_1)),
sum(tabla_lda_2) - sum(diag(tabla_lda_2)),
sum(tabla_logit_1) - sum(diag(tabla_logit_1)),
sum(tabla_logit_2) - sum(diag(tabla_logit_2)))
)
comparacion$Precisión <- paste0(round(comparacion$Precisión, 2), "%")
kable(comparacion,
caption = "Tabla 11. Comparación de modelos") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
row_spec(c(2, 4), bold = TRUE, background = "#E8F4F8")
| Modelo | Variables | Precisión | Errores |
|---|---|---|---|
| LDA (LMCD) | 1 | 90% | 6 |
| LDA (LMCD + C12CD) | 2 | 96.67% | 2 |
| Logit (LMCD) | 1 | 90% | 6 |
| Logit (LMCD + C12CD) | 2 | 96.67% | 2 |
# Gráfico de barras comparativo
ggplot(comparacion, aes(x = Modelo, y = as.numeric(gsub("%", "", Precisión)), fill = Variables)) +
geom_bar(stat = "identity") +
geom_text(aes(label = Precisión), vjust = -0.5) +
scale_fill_manual(values = c("steelblue", "darkgreen")) +
labs(
title = "Gráfica 5. Comparación de Precisión entre Modelos",
x = "Modelo",
y = "Precisión (%)",
fill = "N° Variables"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Los modelos multivariados (que combinan LMCD y C12CD) presenten mayor precisión que los modelos univariados, ya que la incorporación de múltiples dimensiones óseas captura mejor la variabilidad morfológica y el dimorfismo sexual.
Dimorfismo sexual significativo: Ambas variables (LMCD y C12CD) muestran diferencias altamente significativas entre sexos (p < 0.001), con un tamaño del efecto por encima de los valores para ser clasificado grande (d = 2.15).
Alta precisión predictiva: Los modelos bivariados (LMCD + C12CD) alcanzan 96.67% de clasificación correcta, comparable con métodos pélvicos tradicionales.
Complementariedad de variables: La combinación de LMCD y C12CD mejora la precisión en 6.67 puntos porcentuales y reduce los errores en un 66.7% (de 6 a 2 casos).
Equivalencia metodológica: El análisis discriminante y la regresión logística producen resultados prácticamente idénticos en términos de precisión de clasificación.
Aplicabilidad forense: La clavícula derecha es un excelente indicador para determinación de sexo cuando la pelvis o el cráneo no están disponibles o están fragmentados.
Fecha de análisis: 2025-11-20
Software: R version R version 4.5.2 (2025-10-31
ucrt)
Paquetes principales: MASS, haven, dplyr, ggplot2
This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.
When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
summary(cars)
## speed dist
## Min. : 4.0 Min. : 2.00
## 1st Qu.:12.0 1st Qu.: 26.00
## Median :15.0 Median : 36.00
## Mean :15.4 Mean : 42.98
## 3rd Qu.:19.0 3rd Qu.: 56.00
## Max. :25.0 Max. :120.00
You can also embed plots, for example:
Note that the echo = FALSE parameter was added to the
code chunk to prevent printing of the R code that generated the
plot.