Propósito
Este documento reproduce la caracterización definitiva de la
alfabetización física realizada con el CAPL-2 versión Colombia
en la muestra multicéntrica nacional de escolares de 8 a 12 años.
El flujo reproduce:
- disponibilidad de los puntajes finales;
- descriptivos nacionales y regionales;
- categorías interpretativas oficiales del CAPL-2;
- distribución de categorías a nivel nacional y por región;
- asociación entre región y categoría mediante chi-cuadrado de Pearson
y V de Cramer;
- verificación mediante simulación Monte Carlo para Comportamiento
diario;
- comparación regional de puntajes mediante Kruskal-Wallis y epsilon
cuadrado;
- comparaciones post hoc de Dunn con ajuste de Holm;
- controles de correspondencia con los resultados definitivos de la
tesis.
1. Entorno
reproducible
paquetes <- c(
"readxl",
"dplyr",
"tidyr",
"tibble",
"purrr",
"capl",
"knitr"
)
faltantes <- paquetes[
!vapply(
paquetes,
requireNamespace,
logical(1),
quietly = TRUE
)
]
if (length(faltantes) > 0) {
stop(
paste0(
"Instale antes de continuar los paquetes: ",
paste(faltantes, collapse = ", ")
)
)
}
library(readxl)
library(dplyr)
library(tidyr)
library(tibble)
library(purrr)
library(capl)
library(knitr)
set.seed(20260907)
2. Lectura de la base
analítica
localizar_archivo <- function(nombre_archivo) {
if (file.exists(nombre_archivo)) {
return(normalizePath(nombre_archivo))
}
entrada <- tryCatch(
knitr::current_input(dir = TRUE),
error = function(e) ""
)
if (nzchar(entrada)) {
candidato <- file.path(
dirname(entrada),
basename(nombre_archivo)
)
if (file.exists(candidato)) {
return(normalizePath(candidato))
}
}
archivos <- list.files(
path = getwd(),
recursive = TRUE,
full.names = TRUE
)
coincidencias <- archivos[
tolower(basename(archivos)) ==
tolower(basename(nombre_archivo))
]
if (length(coincidencias) >= 1) {
return(normalizePath(coincidencias[1]))
}
stop(
paste0(
"No se encontró el archivo '",
basename(nombre_archivo),
"'. Guarde el Excel y el Rmd en la misma carpeta ",
"o ajuste params$archivo_excel."
)
)
}
ruta_base <- localizar_archivo(
params$archivo_excel
)
cat(
"Archivo localizado en:\n",
ruta_base,
"\n"
)
## Archivo localizado en:
## C:\Users\Personal\Documents\análisis doctorado\Analisis\Datos_publicos_CAPL2_REVISION\CAPL2_Colombia_BASE_PUBLICA_REPRODUCIBLE.xlsx
hojas <- readxl::excel_sheets(
ruta_base
)
if (!params$hoja_excel %in% hojas) {
stop(
paste0(
"No se encontró la hoja '",
params$hoja_excel,
"'."
)
)
}
base <- readxl::read_excel(
ruta_base,
sheet = params$hoja_excel
)
cat(
"Dimensiones de la base:",
nrow(base),
"filas x",
ncol(base),
"columnas\n"
)
## Dimensiones de la base: 843 filas x 46 columnas
3. Variables requeridas
y auditoría inicial
variables_requeridas <- c(
"age",
"gender",
"region",
"grade",
"pc_score_final",
"db_score_final",
"mc_score_final",
"ku_score_final",
"capl_total_final"
)
faltan_variables <- setdiff(
variables_requeridas,
names(base)
)
if (length(faltan_variables) > 0) {
stop(
paste0(
"Faltan variables necesarias: ",
paste(
faltan_variables,
collapse = ", "
)
)
)
}
if (nrow(base) != 843) {
stop(
paste0(
"Se esperaban 843 escolares y se encontraron ",
nrow(base),
"."
)
)
}
normalizar_sexo_capl <- function(x) {
x2 <- tolower(
iconv(
as.character(x),
from = "",
to = "ASCII//TRANSLIT"
)
)
dplyr::case_when(
x2 %in% c(
"boy",
"male",
"nino",
"masculino",
"m",
"1"
) ~ "boy",
x2 %in% c(
"girl",
"female",
"nina",
"femenino",
"f",
"0"
) ~ "girl",
TRUE ~ NA_character_
)
}
car_nacional <- base %>%
transmute(
edad = as.numeric(age),
sexo_original = as.character(gender),
sexo_capl = normalizar_sexo_capl(gender),
sexo = case_when(
sexo_capl == "boy" ~ "Niños",
sexo_capl == "girl" ~ "Niñas",
TRUE ~ NA_character_
),
region = as.character(region),
grado = as.numeric(grade),
competencia_fisica =
as.numeric(pc_score_final),
comportamiento_diario =
as.numeric(db_score_final),
motivacion_confianza =
as.numeric(mc_score_final),
conocimiento_comprension =
as.numeric(ku_score_final),
capl_total =
as.numeric(capl_total_final)
)
cat("N total =", nrow(car_nacional), "\n")
## N total = 843
cat("\nSexo:\n")
##
## Sexo:
print(
table(
car_nacional$sexo,
useNA = "ifany"
)
)
##
## Niñas Niños
## 425 418
cat("\nEdad:\n")
##
## Edad:
print(
table(
car_nacional$edad,
useNA = "ifany"
)
)
##
## 8 9 10 11 12
## 168 170 171 170 164
cat("\nRegión:\n")
##
## Región:
print(
table(
car_nacional$region,
useNA = "ifany"
)
)
##
## Caribe Centro-Oriente Centro sur-Amazonía
## 140 140 141
## Eje cafetero-Antioquia Llanos-Orinoquía Pacífico
## 141 141 140
4. Disponibilidad de
los puntajes
tabla_disponibilidad_nacional <- tibble(
dominio = c(
"Competencia física",
"Comportamiento diario",
"Motivación y confianza",
"Conocimiento y comprensión",
"CAPL-2 total"
),
n_valido = c(
sum(!is.na(
car_nacional$competencia_fisica
)),
sum(!is.na(
car_nacional$comportamiento_diario
)),
sum(!is.na(
car_nacional$motivacion_confianza
)),
sum(!is.na(
car_nacional$conocimiento_comprension
)),
sum(!is.na(
car_nacional$capl_total
))
)
) %>%
mutate(
n_total = nrow(
car_nacional
),
porcentaje_valido =
100 * n_valido / n_total,
n_sin_puntaje =
n_total - n_valido,
porcentaje_sin_puntaje =
100 * n_sin_puntaje / n_total,
across(
c(
porcentaje_valido,
porcentaje_sin_puntaje
),
~ round(.x, 1)
)
)
knitr::kable(
tabla_disponibilidad_nacional,
caption = "Disponibilidad de puntuaciones del CAPL-2"
)
Disponibilidad de puntuaciones del CAPL-2
| Competencia física |
843 |
843 |
100.0 |
0 |
0.0 |
| Comportamiento diario |
840 |
843 |
99.6 |
3 |
0.4 |
| Motivación y confianza |
842 |
843 |
99.9 |
1 |
0.1 |
| Conocimiento y comprensión |
842 |
843 |
99.9 |
1 |
0.1 |
| CAPL-2 total |
838 |
843 |
99.4 |
5 |
0.6 |
n_esperados <- c(
843,
840,
842,
842,
838
)
control_disponibilidad <- tabla_disponibilidad_nacional %>%
mutate(
n_esperado = n_esperados,
coincide =
n_valido ==
n_esperado
)
knitr::kable(
control_disponibilidad,
caption = "Control de disponibilidad frente a los resultados definitivos"
)
Control de disponibilidad frente a los resultados
definitivos
| Competencia física |
843 |
843 |
100.0 |
0 |
0.0 |
843 |
TRUE |
| Comportamiento diario |
840 |
843 |
99.6 |
3 |
0.4 |
840 |
TRUE |
| Motivación y confianza |
842 |
843 |
99.9 |
1 |
0.1 |
842 |
TRUE |
| Conocimiento y comprensión |
842 |
843 |
99.9 |
1 |
0.1 |
842 |
TRUE |
| CAPL-2 total |
838 |
843 |
99.4 |
5 |
0.6 |
838 |
TRUE |
if (!all(control_disponibilidad$coincide)) {
stop(
"La disponibilidad de los puntajes no coincide con la versión definitiva."
)
}
5. Descriptivos
nacionales y regionales
puntajes_nacionales_largo <- car_nacional %>%
select(
edad,
sexo,
region,
grado,
competencia_fisica,
comportamiento_diario,
motivacion_confianza,
conocimiento_comprension,
capl_total
) %>%
pivot_longer(
cols = c(
competencia_fisica,
comportamiento_diario,
motivacion_confianza,
conocimiento_comprension,
capl_total
),
names_to = "variable",
values_to = "puntaje"
) %>%
mutate(
dominio = case_when(
variable ==
"competencia_fisica" ~
"Competencia física",
variable ==
"comportamiento_diario" ~
"Comportamiento diario",
variable ==
"motivacion_confianza" ~
"Motivación y confianza",
variable ==
"conocimiento_comprension" ~
"Conocimiento y comprensión",
variable ==
"capl_total" ~
"CAPL-2 total"
),
maximo_teorico = case_when(
variable %in% c(
"competencia_fisica",
"comportamiento_diario",
"motivacion_confianza"
) ~ 30,
variable ==
"conocimiento_comprension" ~ 10,
variable ==
"capl_total" ~ 100
)
)
tabla_puntajes_nacional <- puntajes_nacionales_largo %>%
group_by(
dominio,
maximo_teorico
) %>%
summarise(
n = sum(!is.na(puntaje)),
media = mean(
puntaje,
na.rm = TRUE
),
DE = sd(
puntaje,
na.rm = TRUE
),
P25 = quantile(
puntaje,
0.25,
na.rm = TRUE
),
mediana = median(
puntaje,
na.rm = TRUE
),
P75 = quantile(
puntaje,
0.75,
na.rm = TRUE
),
minimo = min(
puntaje,
na.rm = TRUE
),
maximo = max(
puntaje,
na.rm = TRUE
),
.groups = "drop"
) %>%
mutate(
mediana_porcentaje_maximo =
100 *
mediana /
maximo_teorico,
across(
c(
media,
DE,
P25,
mediana,
P75,
minimo,
maximo,
mediana_porcentaje_maximo
),
~ round(.x, 2)
)
)
knitr::kable(
tabla_puntajes_nacional,
caption = "Descriptivos nacionales de los puntajes del CAPL-2"
)
Descriptivos nacionales de los puntajes del CAPL-2
| CAPL-2 total |
100 |
838 |
60.37 |
9.46 |
53.83 |
59.77 |
66.71 |
30.70 |
88.86 |
59.77 |
| Competencia física |
30 |
843 |
14.94 |
4.29 |
12.00 |
14.71 |
17.50 |
5.29 |
29.29 |
49.05 |
| Comportamiento diario |
30 |
840 |
17.19 |
4.60 |
14.00 |
17.00 |
20.00 |
3.00 |
28.00 |
56.67 |
| Conocimiento y comprensión |
10 |
842 |
5.40 |
2.55 |
3.00 |
5.00 |
7.00 |
0.00 |
10.00 |
50.00 |
| Motivación y confianza |
30 |
842 |
22.85 |
4.23 |
19.62 |
23.10 |
26.40 |
9.60 |
30.00 |
77.00 |
tabla_puntajes_region <- puntajes_nacionales_largo %>%
group_by(
dominio,
maximo_teorico,
region
) %>%
summarise(
n = sum(!is.na(puntaje)),
media = mean(
puntaje,
na.rm = TRUE
),
DE = sd(
puntaje,
na.rm = TRUE
),
P25 = quantile(
puntaje,
0.25,
na.rm = TRUE
),
mediana = median(
puntaje,
na.rm = TRUE
),
P75 = quantile(
puntaje,
0.75,
na.rm = TRUE
),
.groups = "drop"
) %>%
mutate(
mediana_porcentaje_maximo =
100 *
mediana /
maximo_teorico,
across(
c(
media,
DE,
P25,
mediana,
P75,
mediana_porcentaje_maximo
),
~ round(.x, 2)
)
)
tabla_medianas_region <- tabla_puntajes_region %>%
select(
dominio,
region,
mediana
) %>%
pivot_wider(
names_from = region,
values_from = mediana
)
knitr::kable(
tabla_medianas_region,
caption = "Puntajes medianos del CAPL-2 según región"
)
Puntajes medianos del CAPL-2 según región
| CAPL-2 total |
56.98 |
57.81 |
62.96 |
56.53 |
66.46 |
60.87 |
| Competencia física |
14.36 |
12.43 |
16.46 |
17.14 |
16.71 |
12.43 |
| Comportamiento diario |
15.00 |
17.00 |
18.00 |
18.00 |
18.00 |
17.00 |
| Conocimiento y comprensión |
4.00 |
4.00 |
4.00 |
4.00 |
8.00 |
7.00 |
| Motivación y confianza |
23.50 |
24.50 |
23.00 |
18.20 |
23.60 |
24.80 |
control_nacional <- tribble(
~dominio, ~mediana_esperada, ~P25_esperado, ~P75_esperado,
"CAPL-2 total", 59.8, 53.8, 66.7,
"Competencia física", 14.7, 12.0, 17.5,
"Comportamiento diario", 17.0, 14.0, 20.0,
"Motivación y confianza", 23.1, 19.6, 26.4,
"Conocimiento y comprensión", 5.0, 3.0, 7.0
)
control_nacional <- control_nacional %>%
left_join(
tabla_puntajes_nacional %>%
select(
dominio,
mediana,
P25,
P75
),
by = "dominio"
) %>%
mutate(
coincide_mediana =
abs(
mediana -
mediana_esperada
) < 0.11,
coincide_P25 =
abs(
P25 -
P25_esperado
) < 0.11,
coincide_P75 =
abs(
P75 -
P75_esperado
) < 0.11
)
knitr::kable(
control_nacional,
caption = "Control de descriptivos nacionales"
)
Control de descriptivos nacionales
| CAPL-2 total |
59.8 |
53.8 |
66.7 |
59.77 |
53.83 |
66.71 |
TRUE |
TRUE |
TRUE |
| Competencia física |
14.7 |
12.0 |
17.5 |
14.71 |
12.00 |
17.50 |
TRUE |
TRUE |
TRUE |
| Comportamiento diario |
17.0 |
14.0 |
20.0 |
17.00 |
14.00 |
20.00 |
TRUE |
TRUE |
TRUE |
| Motivación y confianza |
23.1 |
19.6 |
26.4 |
23.10 |
19.62 |
26.40 |
TRUE |
TRUE |
TRUE |
| Conocimiento y comprensión |
5.0 |
3.0 |
7.0 |
5.00 |
3.00 |
7.00 |
TRUE |
TRUE |
TRUE |
if (
!all(
control_nacional$coincide_mediana &
control_nacional$coincide_P25 &
control_nacional$coincide_P75
)
) {
warning(
"Algún descriptivo nacional no coincide con la versión definitiva."
)
}
6. Categorías
interpretativas oficiales del CAPL-2
Las categorías se obtienen mediante los criterios oficiales del
CAPL-2 considerando la edad, el sexo y el puntaje correspondiente.
car_nacional_categorias <- car_nacional %>%
mutate(
categoria_pc =
capl::get_capl_interpretation(
age = edad,
gender = sexo_capl,
score = competencia_fisica,
protocol = "pc"
),
categoria_db =
capl::get_capl_interpretation(
age = edad,
gender = sexo_capl,
score = comportamiento_diario,
protocol = "db"
),
categoria_mc =
capl::get_capl_interpretation(
age = edad,
gender = sexo_capl,
score = motivacion_confianza,
protocol = "mc"
),
categoria_ku =
capl::get_capl_interpretation(
age = edad,
gender = sexo_capl,
score = conocimiento_comprension,
protocol = "ku"
),
categoria_total =
capl::get_capl_interpretation(
age = edad,
gender = sexo_capl,
score = capl_total,
protocol = "capl"
)
)
auditoria_categorias <- tibble(
dominio = c(
"Competencia física",
"Comportamiento diario",
"Motivación y confianza",
"Conocimiento y comprensión",
"CAPL-2 total"
),
n_puntaje = c(
sum(!is.na(
car_nacional_categorias$competencia_fisica
)),
sum(!is.na(
car_nacional_categorias$comportamiento_diario
)),
sum(!is.na(
car_nacional_categorias$motivacion_confianza
)),
sum(!is.na(
car_nacional_categorias$conocimiento_comprension
)),
sum(!is.na(
car_nacional_categorias$capl_total
))
),
n_categoria = c(
sum(!is.na(
car_nacional_categorias$categoria_pc
)),
sum(!is.na(
car_nacional_categorias$categoria_db
)),
sum(!is.na(
car_nacional_categorias$categoria_mc
)),
sum(!is.na(
car_nacional_categorias$categoria_ku
)),
sum(!is.na(
car_nacional_categorias$categoria_total
))
)
) %>%
mutate(
diferencia =
n_puntaje -
n_categoria
)
knitr::kable(
auditoria_categorias,
caption = "Auditoría de las categorías oficiales"
)
Auditoría de las categorías oficiales
| Competencia física |
843 |
843 |
0 |
| Comportamiento diario |
840 |
840 |
0 |
| Motivación y confianza |
842 |
842 |
0 |
| Conocimiento y comprensión |
842 |
842 |
0 |
| CAPL-2 total |
838 |
838 |
0 |
if (
any(
auditoria_categorias$diferencia != 0
)
) {
stop(
"Existen puntajes válidos sin categoría oficial reproducida."
)
}
categorias_largo <- car_nacional_categorias %>%
select(
edad,
sexo,
region,
categoria_pc,
categoria_db,
categoria_mc,
categoria_ku,
categoria_total
) %>%
pivot_longer(
cols =
starts_with(
"categoria_"
),
names_to = "variable",
values_to = "categoria"
) %>%
mutate(
dominio = case_when(
variable ==
"categoria_pc" ~
"Competencia física",
variable ==
"categoria_db" ~
"Comportamiento diario",
variable ==
"categoria_mc" ~
"Motivación y confianza",
variable ==
"categoria_ku" ~
"Conocimiento y comprensión",
variable ==
"categoria_total" ~
"CAPL-2 total"
),
categoria =
factor(
categoria,
levels = c(
"beginning",
"progressing",
"achieving",
"excelling"
),
labels = c(
"Beginning",
"Progressing",
"Achieving",
"Excelling"
)
)
)
7. Distribución
nacional y regional de las categorías
tabla_categorias_nacional <- categorias_largo %>%
filter(
!is.na(categoria)
) %>%
count(
dominio,
categoria,
name = "n"
) %>%
group_by(
dominio
) %>%
mutate(
total = sum(n),
porcentaje =
100 * n / total
) %>%
ungroup() %>%
mutate(
porcentaje =
round(
porcentaje,
1
),
presentacion =
paste0(
n,
" (",
format(
porcentaje,
nsmall = 1,
decimal.mark = ","
),
"%)"
)
)
tabla_categorias_nacional_ancha <-
tabla_categorias_nacional %>%
select(
dominio,
total,
categoria,
presentacion
) %>%
distinct() %>%
pivot_wider(
names_from = categoria,
values_from = presentacion
) %>%
rename(
n = total
)
knitr::kable(
tabla_categorias_nacional_ancha,
caption = "Distribución nacional según categorías interpretativas oficiales del CAPL-2"
)
Distribución nacional según categorías interpretativas
oficiales del CAPL-2
| CAPL-2 total |
838 |
115 (13,7%) |
545 (65,0%) |
123 (14,7%) |
55 ( 6,6%) |
| Competencia física |
843 |
372 (44,1%) |
366 (43,4%) |
60 ( 7,1%) |
45 ( 5,3%) |
| Comportamiento diario |
840 |
56 ( 6,7%) |
645 (76,8%) |
126 (15,0%) |
13 ( 1,5%) |
| Conocimiento y comprensión |
842 |
386 (45,8%) |
208 (24,7%) |
88 (10,5%) |
160 (19,0%) |
| Motivación y confianza |
842 |
55 ( 6,5%) |
363 (43,1%) |
163 (19,4%) |
261 (31,0%) |
categorias_esperadas <- tribble(
~dominio, ~Beginning, ~Progressing, ~Achieving, ~Excelling,
"CAPL-2 total", 115, 545, 123, 55,
"Competencia física", 372, 366, 60, 45,
"Comportamiento diario", 56, 645, 126, 13,
"Motivación y confianza", 55, 363, 163, 261,
"Conocimiento y comprensión", 386, 208, 88, 160
)
categorias_observadas <- tabla_categorias_nacional %>%
select(
dominio,
categoria,
n
) %>%
pivot_wider(
names_from = categoria,
values_from = n,
values_fill = 0
)
control_categorias <- categorias_esperadas %>%
left_join(
categorias_observadas,
by = "dominio",
suffix = c(
"_esperado",
"_observado"
)
)
for (
cat_actual in c(
"Beginning",
"Progressing",
"Achieving",
"Excelling"
)
) {
control_categorias[[
paste0(
"coincide_",
cat_actual
)
]] <-
control_categorias[[
paste0(
cat_actual,
"_esperado"
)
]] ==
control_categorias[[
paste0(
cat_actual,
"_observado"
)
]]
}
knitr::kable(
control_categorias,
caption = "Control de correspondencia de las categorías nacionales"
)
Control de correspondencia de las categorías
nacionales
| CAPL-2 total |
115 |
545 |
123 |
55 |
115 |
545 |
123 |
55 |
TRUE |
TRUE |
TRUE |
TRUE |
| Competencia física |
372 |
366 |
60 |
45 |
372 |
366 |
60 |
45 |
TRUE |
TRUE |
TRUE |
TRUE |
| Comportamiento diario |
56 |
645 |
126 |
13 |
56 |
645 |
126 |
13 |
TRUE |
TRUE |
TRUE |
TRUE |
| Motivación y confianza |
55 |
363 |
163 |
261 |
55 |
363 |
163 |
261 |
TRUE |
TRUE |
TRUE |
TRUE |
| Conocimiento y comprensión |
386 |
208 |
88 |
160 |
386 |
208 |
88 |
160 |
TRUE |
TRUE |
TRUE |
TRUE |
columnas_control <- grep(
"^coincide_",
names(
control_categorias
),
value = TRUE
)
if (
!all(
unlist(
control_categorias[
columnas_control
]
)
)
) {
stop(
"La distribución de categorías no coincide con los resultados definitivos."
)
}
tabla_categorias_region <- categorias_largo %>%
filter(
!is.na(categoria)
) %>%
count(
dominio,
region,
categoria,
name = "n"
) %>%
group_by(
dominio,
region
) %>%
mutate(
total_region =
sum(n),
porcentaje =
100 *
n /
total_region
) %>%
ungroup() %>%
mutate(
porcentaje =
round(
porcentaje,
1
)
) %>%
arrange(
dominio,
region,
categoria
)
knitr::kable(
head(
tabla_categorias_region,
40
),
caption = "Ejemplo de la distribución de categorías oficiales según región"
)
Ejemplo de la distribución de categorías oficiales según
región
| CAPL-2 total |
Caribe |
Beginning |
28 |
140 |
20.0 |
| CAPL-2 total |
Caribe |
Progressing |
98 |
140 |
70.0 |
| CAPL-2 total |
Caribe |
Achieving |
11 |
140 |
7.9 |
| CAPL-2 total |
Caribe |
Excelling |
3 |
140 |
2.1 |
| CAPL-2 total |
Centro sur-Amazonía |
Beginning |
29 |
139 |
20.9 |
| CAPL-2 total |
Centro sur-Amazonía |
Progressing |
85 |
139 |
61.2 |
| CAPL-2 total |
Centro sur-Amazonía |
Achieving |
20 |
139 |
14.4 |
| CAPL-2 total |
Centro sur-Amazonía |
Excelling |
5 |
139 |
3.6 |
| CAPL-2 total |
Centro-Oriente |
Beginning |
14 |
138 |
10.1 |
| CAPL-2 total |
Centro-Oriente |
Progressing |
84 |
138 |
60.9 |
| CAPL-2 total |
Centro-Oriente |
Achieving |
33 |
138 |
23.9 |
| CAPL-2 total |
Centro-Oriente |
Excelling |
7 |
138 |
5.1 |
| CAPL-2 total |
Eje cafetero-Antioquia |
Beginning |
23 |
141 |
16.3 |
| CAPL-2 total |
Eje cafetero-Antioquia |
Progressing |
112 |
141 |
79.4 |
| CAPL-2 total |
Eje cafetero-Antioquia |
Achieving |
6 |
141 |
4.3 |
| CAPL-2 total |
Llanos-Orinoquía |
Beginning |
11 |
140 |
7.9 |
| CAPL-2 total |
Llanos-Orinoquía |
Progressing |
65 |
140 |
46.4 |
| CAPL-2 total |
Llanos-Orinoquía |
Achieving |
30 |
140 |
21.4 |
| CAPL-2 total |
Llanos-Orinoquía |
Excelling |
34 |
140 |
24.3 |
| CAPL-2 total |
Pacífico |
Beginning |
10 |
140 |
7.1 |
| CAPL-2 total |
Pacífico |
Progressing |
101 |
140 |
72.1 |
| CAPL-2 total |
Pacífico |
Achieving |
23 |
140 |
16.4 |
| CAPL-2 total |
Pacífico |
Excelling |
6 |
140 |
4.3 |
| Competencia física |
Caribe |
Beginning |
65 |
140 |
46.4 |
| Competencia física |
Caribe |
Progressing |
64 |
140 |
45.7 |
| Competencia física |
Caribe |
Achieving |
8 |
140 |
5.7 |
| Competencia física |
Caribe |
Excelling |
3 |
140 |
2.1 |
| Competencia física |
Centro sur-Amazonía |
Beginning |
94 |
141 |
66.7 |
| Competencia física |
Centro sur-Amazonía |
Progressing |
45 |
141 |
31.9 |
| Competencia física |
Centro sur-Amazonía |
Achieving |
2 |
141 |
1.4 |
| Competencia física |
Centro-Oriente |
Beginning |
39 |
140 |
27.9 |
| Competencia física |
Centro-Oriente |
Progressing |
83 |
140 |
59.3 |
| Competencia física |
Centro-Oriente |
Achieving |
12 |
140 |
8.6 |
| Competencia física |
Centro-Oriente |
Excelling |
6 |
140 |
4.3 |
| Competencia física |
Eje cafetero-Antioquia |
Beginning |
29 |
141 |
20.6 |
| Competencia física |
Eje cafetero-Antioquia |
Progressing |
83 |
141 |
58.9 |
| Competencia física |
Eje cafetero-Antioquia |
Achieving |
17 |
141 |
12.1 |
| Competencia física |
Eje cafetero-Antioquia |
Excelling |
12 |
141 |
8.5 |
| Competencia física |
Llanos-Orinoquía |
Beginning |
48 |
141 |
34.0 |
| Competencia física |
Llanos-Orinoquía |
Progressing |
48 |
141 |
34.0 |
8. Asociación entre
región y categoría oficial
analizar_asociacion <- function(datos) {
datos <- datos %>%
filter(
!is.na(region),
!is.na(categoria)
)
tab <- table(
datos$region,
datos$categoria
)
prueba <- suppressWarnings(
chisq.test(
tab,
correct = FALSE
)
)
n_total <- sum(tab)
filas <- nrow(tab)
columnas <- ncol(tab)
v_cramer <- sqrt(
as.numeric(
prueba$statistic
) /
(
n_total *
min(
filas - 1,
columnas - 1
)
)
)
tibble(
n = n_total,
chi2 =
as.numeric(
prueba$statistic
),
gl =
as.numeric(
prueba$parameter
),
p =
prueba$p.value,
v_cramer =
v_cramer,
esperado_minimo =
min(
prueba$expected
),
porcentaje_esperados_menor_5 =
100 *
mean(
prueba$expected < 5
)
)
}
tabla_asociacion_region_categoria <- categorias_largo %>%
filter(
!is.na(categoria)
) %>%
group_by(
dominio
) %>%
group_modify(
~ analizar_asociacion(.x)
) %>%
ungroup() %>%
mutate(
chi2 = round(
chi2,
3
),
v_cramer =
round(
v_cramer,
3
),
esperado_minimo =
round(
esperado_minimo,
3
),
porcentaje_esperados_menor_5 =
round(
porcentaje_esperados_menor_5,
3
)
)
knitr::kable(
tabla_asociacion_region_categoria,
digits = 3,
caption = "Asociación entre región y categorías interpretativas oficiales"
)
Asociación entre región y categorías interpretativas
oficiales
| CAPL-2 total |
838 |
144.74 |
15 |
0 |
0.240 |
9.057 |
0 |
| Competencia física |
843 |
184.96 |
15 |
0 |
0.270 |
7.473 |
0 |
| Comportamiento diario |
840 |
83.46 |
15 |
0 |
0.182 |
2.151 |
25 |
| Conocimiento y comprensión |
842 |
321.75 |
15 |
0 |
0.357 |
14.527 |
0 |
| Motivación y confianza |
842 |
194.61 |
15 |
0 |
0.278 |
9.080 |
0 |
8.1. Monte Carlo para
Comportamiento diario
set.seed(20260907)
datos_db_cat <- categorias_largo %>%
filter(
dominio ==
"Comportamiento diario",
!is.na(region),
!is.na(categoria)
)
tabla_db_cat <- table(
datos_db_cat$region,
datos_db_cat$categoria
)
chi_db_mc <- chisq.test(
tabla_db_cat,
simulate.p.value = TRUE,
B = 100000
)
n_db <- sum(
tabla_db_cat
)
v_cramer_db <- sqrt(
as.numeric(
chi_db_mc$statistic
) /
(
n_db *
min(
nrow(
tabla_db_cat
) - 1,
ncol(
tabla_db_cat
) - 1
)
)
)
resultado_db_mc <- tibble(
dominio =
"Comportamiento diario",
n = n_db,
chi2 =
as.numeric(
chi_db_mc$statistic
),
p_monte_carlo =
chi_db_mc$p.value,
B = 100000,
v_cramer =
v_cramer_db
) %>%
mutate(
chi2 =
round(
chi2,
3
),
v_cramer =
round(
v_cramer,
3
)
)
knitr::kable(
resultado_db_mc,
digits = 4,
caption = "Verificación Monte Carlo para Comportamiento diario"
)
Verificación Monte Carlo para Comportamiento diario
| Comportamiento diario |
840 |
83.46 |
0 |
100000 |
0.182 |
asociacion_esperada <- tribble(
~dominio, ~n_esperado, ~chi2_esperado, ~v_esperado,
"CAPL-2 total", 838, 145.0, 0.240,
"Competencia física", 843, 185.0, 0.270,
"Comportamiento diario", 840, 83.5, 0.182,
"Motivación y confianza", 842, 195.0, 0.278,
"Conocimiento y comprensión", 842, 322.0, 0.357
)
control_asociacion <- asociacion_esperada %>%
left_join(
tabla_asociacion_region_categoria %>%
select(
dominio,
n,
chi2,
v_cramer
),
by = "dominio"
) %>%
mutate(
coincide_n =
n ==
n_esperado,
coincide_chi2 =
abs(
chi2 -
chi2_esperado
) < 0.11,
coincide_v =
abs(
v_cramer -
v_esperado
) < 0.002
)
knitr::kable(
control_asociacion,
caption = "Control de correspondencia de la asociación región × categoría"
)
Control de correspondencia de la asociación región ×
categoría
| CAPL-2 total |
838 |
145.0 |
0.240 |
838 |
144.74 |
0.240 |
TRUE |
FALSE |
TRUE |
| Competencia física |
843 |
185.0 |
0.270 |
843 |
184.96 |
0.270 |
TRUE |
TRUE |
TRUE |
| Comportamiento diario |
840 |
83.5 |
0.182 |
840 |
83.46 |
0.182 |
TRUE |
TRUE |
TRUE |
| Motivación y confianza |
842 |
195.0 |
0.278 |
842 |
194.61 |
0.278 |
TRUE |
FALSE |
TRUE |
| Conocimiento y comprensión |
842 |
322.0 |
0.357 |
842 |
321.75 |
0.357 |
TRUE |
FALSE |
TRUE |
if (
!all(
control_asociacion$coincide_n &
control_asociacion$coincide_chi2 &
control_asociacion$coincide_v
)
) {
warning(
"Algún resultado de asociación no coincide con el redondeo de la versión definitiva."
)
}
9. Comparación
regional mediante Kruskal-Wallis
analizar_kw <- function(datos) {
datos <- datos %>%
filter(
!is.na(puntaje),
!is.na(region)
)
prueba <- kruskal.test(
puntaje ~ region,
data = datos
)
H <- as.numeric(
prueba$statistic
)
k <- length(
unique(
datos$region
)
)
n_total <- nrow(
datos
)
epsilon2 <-
(
H -
k +
1
) /
(
n_total -
k
)
epsilon2 <- max(
0,
epsilon2
)
tibble(
n = n_total,
H = H,
gl =
as.numeric(
prueba$parameter
),
p =
prueba$p.value,
epsilon2 =
epsilon2
)
}
tabla_kw_definitiva <- puntajes_nacionales_largo %>%
group_by(
dominio
) %>%
group_modify(
~ analizar_kw(.x)
) %>%
ungroup() %>%
mutate(
H = round(
H,
3
),
epsilon2 =
round(
epsilon2,
3
),
magnitud = case_when(
epsilon2 <
0.01 ~
"Despreciable",
epsilon2 <
0.06 ~
"Pequeña",
epsilon2 <
0.14 ~
"Moderada",
TRUE ~
"Grande"
)
)
knitr::kable(
tabla_kw_definitiva,
digits = 3,
caption = "Comparación de los puntajes del CAPL-2 entre regiones"
)
Comparación de los puntajes del CAPL-2 entre regiones
| CAPL-2 total |
838 |
97.12 |
5 |
0 |
0.111 |
Moderada |
| Competencia física |
843 |
167.62 |
5 |
0 |
0.194 |
Grande |
| Comportamiento diario |
840 |
41.46 |
5 |
0 |
0.044 |
Pequeña |
| Conocimiento y comprensión |
842 |
294.98 |
5 |
0 |
0.347 |
Grande |
| Motivación y confianza |
842 |
210.35 |
5 |
0 |
0.246 |
Grande |
kw_esperado <- tribble(
~dominio, ~n_esperado, ~H_esperado, ~epsilon_esperado, ~magnitud_esperada,
"CAPL-2 total", 838, 97.1, 0.111, "Moderada",
"Competencia física", 843, 168.0, 0.194, "Grande",
"Comportamiento diario", 840, 41.5, 0.044, "Pequeña",
"Motivación y confianza", 842, 210.0, 0.246, "Grande",
"Conocimiento y comprensión", 842, 295.0, 0.347, "Grande"
)
control_kw <- kw_esperado %>%
left_join(
tabla_kw_definitiva %>%
select(
dominio,
n,
H,
epsilon2,
magnitud
),
by = "dominio"
) %>%
mutate(
coincide_n =
n ==
n_esperado,
coincide_H =
abs(
H -
H_esperado
) < 0.11,
coincide_epsilon =
abs(
epsilon2 -
epsilon_esperado
) < 0.002,
coincide_magnitud =
magnitud ==
magnitud_esperada
)
knitr::kable(
control_kw,
caption = "Control de correspondencia de Kruskal-Wallis"
)
Control de correspondencia de Kruskal-Wallis
| CAPL-2 total |
838 |
97.1 |
0.111 |
Moderada |
838 |
97.12 |
0.111 |
Moderada |
TRUE |
TRUE |
TRUE |
TRUE |
| Competencia física |
843 |
168.0 |
0.194 |
Grande |
843 |
167.62 |
0.194 |
Grande |
TRUE |
FALSE |
TRUE |
TRUE |
| Comportamiento diario |
840 |
41.5 |
0.044 |
Pequeña |
840 |
41.46 |
0.044 |
Pequeña |
TRUE |
TRUE |
TRUE |
TRUE |
| Motivación y confianza |
842 |
210.0 |
0.246 |
Grande |
842 |
210.35 |
0.246 |
Grande |
TRUE |
FALSE |
TRUE |
TRUE |
| Conocimiento y comprensión |
842 |
295.0 |
0.347 |
Grande |
842 |
294.98 |
0.347 |
Grande |
TRUE |
TRUE |
TRUE |
TRUE |
if (
!all(
control_kw$coincide_n &
control_kw$coincide_H &
control_kw$coincide_epsilon &
control_kw$coincide_magnitud
)
) {
warning(
"Algún resultado de Kruskal-Wallis no coincide con la versión definitiva."
)
}
10. Comparaciones post
hoc de Dunn con ajuste de Holm
La implementación reproduce la corrección por empates utilizada en el
análisis definitivo.
dunn_holm <- function(datos) {
datos <- datos %>%
filter(
!is.na(puntaje),
!is.na(region)
)
x <- datos$puntaje
g <- factor(
datos$region
)
N <- length(x)
rangos <- rank(
x,
ties.method =
"average"
)
niveles <- levels(g)
n_grupo <- table(g)
rango_medio <- tapply(
rangos,
g,
mean
)
mediana_grupo <- tapply(
x,
g,
median
)
frecuencias_empates <- table(
x
)
termino_empates <- sum(
frecuencias_empates^3 -
frecuencias_empates
)
varianza_dunn <-
N *
(
N + 1
) /
12 -
termino_empates /
(
12 *
(
N - 1
)
)
pares <- combn(
niveles,
2,
simplify = FALSE
)
resultados <- lapply(
pares,
function(par) {
g1 <- par[1]
g2 <- par[2]
diferencia_rangos <-
rango_medio[g1] -
rango_medio[g2]
error_estandar <- sqrt(
varianza_dunn *
(
1 /
n_grupo[g1] +
1 /
n_grupo[g2]
)
)
z <- as.numeric(
diferencia_rangos /
error_estandar
)
p <- 2 *
pnorm(
-abs(z)
)
if (
rango_medio[g1] >=
rango_medio[g2]
) {
grupo_mayor <- g1
grupo_menor <- g2
} else {
grupo_mayor <- g2
grupo_menor <- g1
}
tibble(
grupo_1 = g1,
grupo_2 = g2,
grupo_mayor =
grupo_mayor,
grupo_menor =
grupo_menor,
mediana_1 =
as.numeric(
mediana_grupo[g1]
),
mediana_2 =
as.numeric(
mediana_grupo[g2]
),
z = z,
p_sin_ajuste = p
)
}
) %>%
bind_rows()
resultados %>%
mutate(
p_ajustada = p.adjust(
p_sin_ajuste,
method = "holm"
),
abs_z = abs(z),
significativo =
p_ajustada < 0.05,
contraste = paste(
grupo_mayor,
">",
grupo_menor
)
)
}
tabla_dunn_completa <- puntajes_nacionales_largo %>%
group_by(
dominio
) %>%
group_modify(
~ dunn_holm(.x)
) %>%
ungroup() %>%
mutate(
z = round(
z,
3
),
abs_z = round(
abs_z,
3
),
mediana_1 = round(
mediana_1,
2
),
mediana_2 = round(
mediana_2,
2
)
)
tabla_dunn_significativas <- tabla_dunn_completa %>%
filter(
significativo
) %>%
arrange(
dominio,
p_ajustada,
desc(abs_z)
)
auditoria_dunn <- tabla_dunn_completa %>%
group_by(
dominio
) %>%
summarise(
comparaciones_totales =
n(),
comparaciones_significativas =
sum(
significativo
),
.groups = "drop"
)
knitr::kable(
auditoria_dunn,
caption = "Número de comparaciones post hoc significativas por resultado"
)
Número de comparaciones post hoc significativas por
resultado
| CAPL-2 total |
15 |
9 |
| Competencia física |
15 |
11 |
| Comportamiento diario |
15 |
5 |
| Conocimiento y comprensión |
15 |
10 |
| Motivación y confianza |
15 |
5 |
tabla_dunn_principales <- tabla_dunn_significativas %>%
group_by(
dominio
) %>%
slice_max(
order_by = abs_z,
n = 3,
with_ties = FALSE
) %>%
ungroup() %>%
arrange(
dominio,
desc(abs_z)
) %>%
select(
dominio,
contraste,
mediana_1,
mediana_2,
abs_z,
p_ajustada
)
knitr::kable(
tabla_dunn_principales,
digits = 3,
caption = "Principales comparaciones post hoc entre regiones"
)
Principales comparaciones post hoc entre regiones
| CAPL-2 total |
Llanos-Orinoquía > Eje cafetero-Antioquia |
56.53 |
66.46 |
8.128 |
0.000 |
| CAPL-2 total |
Llanos-Orinoquía > Caribe |
56.98 |
66.46 |
7.299 |
0.000 |
| CAPL-2 total |
Llanos-Orinoquía > Centro sur-Amazonía |
57.81 |
66.46 |
6.015 |
0.000 |
| Competencia física |
Eje cafetero-Antioquia > Pacífico |
17.14 |
12.43 |
9.341 |
0.000 |
| Competencia física |
Eje cafetero-Antioquia > Centro sur-Amazonía |
12.43 |
17.14 |
9.002 |
0.000 |
| Competencia física |
Centro-Oriente > Pacífico |
16.46 |
12.43 |
7.887 |
0.000 |
| Comportamiento diario |
Llanos-Orinoquía > Caribe |
15.00 |
18.00 |
5.778 |
0.000 |
| Comportamiento diario |
Centro-Oriente > Caribe |
15.00 |
18.00 |
4.668 |
0.000 |
| Comportamiento diario |
Eje cafetero-Antioquia > Caribe |
15.00 |
18.00 |
4.039 |
0.001 |
| Conocimiento y comprensión |
Llanos-Orinoquía > Eje cafetero-Antioquia |
4.00 |
8.00 |
13.222 |
0.000 |
| Conocimiento y comprensión |
Pacífico > Eje cafetero-Antioquia |
4.00 |
7.00 |
11.891 |
0.000 |
| Conocimiento y comprensión |
Llanos-Orinoquía > Caribe |
4.00 |
8.00 |
11.337 |
0.000 |
| Motivación y confianza |
Centro sur-Amazonía > Eje cafetero-Antioquia |
24.50 |
18.20 |
12.463 |
0.000 |
| Motivación y confianza |
Pacífico > Eje cafetero-Antioquia |
18.20 |
24.80 |
11.981 |
0.000 |
| Motivación y confianza |
Caribe > Eje cafetero-Antioquia |
23.50 |
18.20 |
10.232 |
0.000 |
dunn_esperado <- tribble(
~dominio, ~significativas_esperadas,
"CAPL-2 total", 9,
"Competencia física", 11,
"Comportamiento diario", 5,
"Conocimiento y comprensión", 10,
"Motivación y confianza", 5
)
control_dunn <- dunn_esperado %>%
left_join(
auditoria_dunn %>%
select(
dominio,
comparaciones_totales,
comparaciones_significativas
),
by = "dominio"
) %>%
mutate(
total_correcto =
comparaciones_totales ==
15,
significativas_correctas =
comparaciones_significativas ==
significativas_esperadas
)
knitr::kable(
control_dunn,
caption = "Control de correspondencia de las comparaciones de Dunn"
)
Control de correspondencia de las comparaciones de
Dunn
| CAPL-2 total |
9 |
15 |
9 |
TRUE |
TRUE |
| Competencia física |
11 |
15 |
11 |
TRUE |
TRUE |
| Comportamiento diario |
5 |
15 |
5 |
TRUE |
TRUE |
| Conocimiento y comprensión |
10 |
15 |
10 |
TRUE |
TRUE |
| Motivación y confianza |
5 |
15 |
5 |
TRUE |
TRUE |
if (
!all(
control_dunn$total_correcto &
control_dunn$significativas_correctas
)
) {
warning(
"El número de comparaciones significativas no coincide con la versión definitiva."
)
}
11. Exportación de
resultados reproducibles
write.csv(
tabla_disponibilidad_nacional,
"CAPL2_CARACTERIZACION_DISPONIBILIDAD.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_puntajes_nacional,
"CAPL2_CARACTERIZACION_PUNTAJES_NACIONAL.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_puntajes_region,
"CAPL2_CARACTERIZACION_PUNTAJES_REGION.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_categorias_nacional,
"CAPL2_CARACTERIZACION_CATEGORIAS_NACIONAL.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_categorias_region,
"CAPL2_CARACTERIZACION_CATEGORIAS_REGION.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_asociacion_region_categoria,
"CAPL2_CARACTERIZACION_ASOCIACION_REGION_CATEGORIAS.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
resultado_db_mc,
"CAPL2_CARACTERIZACION_MONTE_CARLO_DB.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_kw_definitiva,
"CAPL2_CARACTERIZACION_KRUSKAL_WALLIS.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_dunn_completa,
"CAPL2_CARACTERIZACION_DUNN_COMPLETO.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
write.csv(
tabla_dunn_principales,
"CAPL2_CARACTERIZACION_DUNN_PRINCIPALES.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
12. Control final
control_final <- tibble(
componente = c(
"N total",
"Disponibilidad",
"Categorías nacionales",
"Asociación región × categoría",
"Kruskal-Wallis",
"Dunn"
),
estado = c(
nrow(car_nacional) == 843,
all(
control_disponibilidad$coincide
),
all(
unlist(
control_categorias[
columnas_control
]
)
),
all(
control_asociacion$coincide_n &
control_asociacion$coincide_chi2 &
control_asociacion$coincide_v
),
all(
control_kw$coincide_n &
control_kw$coincide_H &
control_kw$coincide_epsilon &
control_kw$coincide_magnitud
),
all(
control_dunn$total_correcto &
control_dunn$significativas_correctas
)
)
)
knitr::kable(
control_final,
caption = "Control final de correspondencia con los resultados definitivos"
)
Control final de correspondencia con los resultados
definitivos
| N total |
TRUE |
| Disponibilidad |
TRUE |
| Categorías nacionales |
TRUE |
| Asociación región × categoría |
FALSE |
| Kruskal-Wallis |
FALSE |
| Dunn |
TRUE |
if (!all(control_final$estado)) {
warning(
"Existe al menos una discrepancia con los resultados definitivos. Revise antes de publicar."
)
}