El presente informe describe la base de datos de evaluaciones de fuerza isocinética en jugadores de fútbol argentino con reconstrucción del ligamento cruzado anterior (RLCA).
El análisis se planteó con un enfoque descriptivo, orientado a:
No se realizaron comparaciones inferenciales ni pruebas de hipótesis en esta etapa, dado que el objetivo principal del trabajo es descriptivo.
# ============================================================
# 0) PAQUETES
# ============================================================
paquetes <- c(
"readxl",
"dplyr",
"tidyr",
"stringr",
"stringi",
"lubridate",
"readr",
"janitor",
"ggplot2",
"naniar",
"visdat",
"skimr",
"purrr",
"forcats",
"flextable",
"officer"
)
instalar <- paquetes[!paquetes %in% rownames(installed.packages())]
if(length(instalar) > 0){
install.packages(instalar)
}
invisible(lapply(paquetes, library, character.only = TRUE))
La base se carga desde un archivo Excel. Todas las columnas se importan inicialmente como texto para evitar problemas derivados de:
# ============================================================
# 1) CARGA DE LA BASE
# ============================================================
ruta <- "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia_RLCA_Analizar.xlsx"
# Leemos primero solo encabezados para conocer la cantidad de columnas
n_columnas <- readxl::read_excel(
path = ruta,
sheet = "Datos Isocinesia",
n_max = 0
) %>%
ncol()
# Cargamos TODO como texto
datos_raw <- readxl::read_excel(
path = ruta,
sheet = "Datos Isocinesia",
col_types = rep("text", n_columnas)
) %>%
janitor::clean_names()
names(datos_raw)
[1] "nombre"
[2] "id"
[3] "nacido"
[4] "altura"
[5] "peso"
[6] "sexo"
[7] "sesion"
[8] "edad_al_momento_de_evaluar"
[9] "implicado"
[10] "terapeuta"
[11] "referente"
[12] "articulacion"
[13] "nivel_de_competencia"
[14] "momento_de_evaluacion_pop_en_meses"
[15] "categorias_momento_de_evaluacion_menor_a_6m_6_8m_mayor_a_8m"
[16] "diagnostico"
[17] "injerto"
[18] "lemaire_refuerzo_lateral"
[19] "relesion"
[20] "velocidad"
[21] "pico_del_par_extension_no_implicado"
[22] "pico_del_par_extension_implicado"
[23] "pico_del_par_flexion_no_implicado"
[24] "pico_del_par_flexion_implicado"
[25] "n_rep_trabajo_max_extension_no_implicado"
[26] "n_rep_trabajo_max_extension_implicado"
[27] "n_rep_trabajo_max_flexion_no_implicado"
[28] "n_rep_trabajo_max_flexion_implicado"
[29] "trabj_peso_corporal_extension_no_implicado"
[30] "trabj_peso_corporal_extension_implicado"
[31] "trabj_peso_corporal_flexion_no_implicado"
[32] "trabj_peso_corporal_flexion_implicado"
[33] "trabajo_total_extension_no_implicado"
[34] "trabajo_total_extension_implicado"
[35] "trabajo_total_flexion_no_implicado"
[36] "trabajo_total_flexion_implicado"
[37] "rango_de_mov_extension_no_implicado"
[38] "rango_de_mov_extension_implicado"
[39] "rango_de_mov_flexion_no_implicado"
[40] "rango_de_mov_flexion_implicado"
[41] "razon_agon_antagon_extension_no_implicado"
[42] "razon_agon_antagon_extension_implicado"
[43] "razon_agon_antagon_flexion_no_implicado"
[44] "razon_agon_antagon_flexion_implicado"
[45] "simetria_pico_del_par_extension_percent"
[46] "simetria_pico_del_par_flexion_percent"
[47] "simetria_trabj_peso_corporal_extension_percent"
[48] "simetria_trabj_peso_corporal_flexion_percent"
[49] "simetria_trabajo_total_extension_percent"
[50] "simetria_trabajo_total_flexion_percent"
Se acortan los nombres de dos variables para facilitar su uso posterior.
# ============================================================
# 2) RENOMBRAR COLUMNAS CLAVE
# ============================================================
names(datos_raw)[names(datos_raw) == "momento_de_evaluacion_pop_en_meses"] <-
"momento_pop_meses"
names(datos_raw)[str_detect(
names(datos_raw),
"^categorias_momento_de_evaluacion"
)] <- "categoria_momento_pop_original"
Se definen funciones para:
# ============================================================
# 3) FUNCIONES AUXILIARES DE LIMPIEZA
# ============================================================
limpiar_faltantes <- function(x){
x <- as.character(x)
x <- stringr::str_squish(x)
x[x == ""] <- NA_character_
x_normalizado <- x %>%
stringr::str_to_lower() %>%
stringi::stri_trans_general("Latin-ASCII")
x[x_normalizado %in% c(
"dato faltante",
"datos faltantes",
"sin dato",
"sin datos",
"na",
"n/a",
"nd",
"s/d",
"null"
)] <- NA_character_
return(x)
}
normalizar_texto <- function(x){
x <- limpiar_faltantes(x)
x %>%
stringr::str_squish() %>%
stringr::str_to_lower() %>%
stringi::stri_trans_general("Latin-ASCII")
}
parse_fecha_mixta <- function(x){
x <- limpiar_faltantes(x)
x_num <- suppressWarnings(as.numeric(x))
salida <- rep(as.Date(NA), length(x))
idx_excel <- !is.na(x_num) & x_num > 20000 & x_num < 80000
salida[idx_excel] <- as.Date(
x_num[idx_excel],
origin = "1899-12-30"
)
idx_texto <- !idx_excel & !is.na(x)
salida[idx_texto] <- suppressWarnings(
lubridate::mdy(x[idx_texto])
)
return(salida)
}
parse_num <- function(x){
readr::parse_number(
limpiar_faltantes(x),
locale = readr::locale(
decimal_mark = ".",
grouping_mark = ","
)
)
}
normalizar_si_no <- function(x){
x_norm <- normalizar_texto(x)
salida <- dplyr::case_when(
is.na(x_norm) ~ NA_character_,
x_norm %in% c("si", "s", "yes", "y", "1") ~ "Sí",
x_norm %in% c("no", "n", "0") ~ "No",
TRUE ~ NA_character_
)
factor(salida, levels = c("No", "Sí"))
}
normalizar_implicado <- function(x){
x_norm <- normalizar_texto(x)
salida <- dplyr::case_when(
is.na(x_norm) ~ NA_character_,
x_norm == "derecho" ~ "Derecho",
x_norm == "izquierdo" ~ "Izquierdo",
TRUE ~ NA_character_
)
factor(salida, levels = c("Derecho", "Izquierdo"))
}
normalizar_injerto <- function(x){
x_norm <- normalizar_texto(x)
salida <- dplyr::case_when(
is.na(x_norm) ~ NA_character_,
x_norm == "cuadricipital" ~ "Cuadricipital",
x_norm == "hth" ~ "HTH",
x_norm == "stri" ~ "STRI",
TRUE ~ NA_character_
)
factor(
salida,
levels = c("HTH", "STRI", "Cuadricipital")
)
}
normalizar_categoria_momento <- function(x){
x_norm <- normalizar_texto(x)
salida <- dplyr::case_when(
is.na(x_norm) ~ NA_character_,
str_detect(x_norm, "menor") & str_detect(x_norm, "6") ~ "<6 meses",
str_detect(x_norm, "6") & str_detect(x_norm, "8") ~ "6-8 meses",
str_detect(x_norm, "mayor") & str_detect(x_norm, "8") ~ ">8 meses",
TRUE ~ NA_character_
)
factor(
salida,
levels = c("<6 meses", "6-8 meses", ">8 meses")
)
}
redondear_meses <- function(x){
floor(x + 0.5)
}
calcular_simetria <- function(valor_implicado, valor_no_implicado){
dplyr::if_else(
is.na(valor_implicado) |
is.na(valor_no_implicado) |
valor_no_implicado == 0,
NA_real_,
(valor_implicado / valor_no_implicado) * 100
)
}
Se organizaron las columnas que corresponden a:
# ============================================================
# 4) IDENTIFICAR GRUPOS DE VARIABLES NUMÉRICAS
# ============================================================
vars_metricas_continuas <- names(datos_raw)[str_detect(
names(datos_raw),
"^(pico_del_par|trabj_peso_corporal|trabajo_total|rango_de_mov|razon_agon_antagon)"
)]
vars_repeticiones <- names(datos_raw)[str_detect(
names(datos_raw),
"rep_trabajo_max"
)]
vars_numericas <- unique(c(
"altura",
"peso",
"momento_pop_meses",
vars_metricas_continuas,
vars_repeticiones
))
En esta etapa se:
Las ventanas temporales fueron:
# ============================================================
# 5) CONSTRUIR BASE LIMPIA
# ============================================================
datos_iso <- datos_raw %>%
mutate(across(everything(), limpiar_faltantes)) %>%
select(
-any_of(c(
"nombre",
"sexo",
"terapeuta",
"referente",
"articulacion",
"diagnostico",
"edad_al_momento_de_evaluar"
))
) %>%
mutate(
across(
all_of(intersect(vars_numericas, names(.))),
parse_num
)
) %>%
mutate(
id = as.character(id),
nacido = parse_fecha_mixta(nacido),
sesion = parse_fecha_mixta(sesion),
edad_evaluacion = if_else(
is.na(nacido) | is.na(sesion),
NA_integer_,
as.integer(
floor(
lubridate::time_length(
lubridate::interval(nacido, sesion),
unit = "years"
)
)
)
),
momento_pop_meses_redondeado = redondear_meses(momento_pop_meses),
implicado = normalizar_implicado(implicado),
nivel_de_competencia = factor(nivel_de_competencia),
categoria_momento_pop_original = normalizar_categoria_momento(
categoria_momento_pop_original
),
injerto = normalizar_injerto(injerto),
lemaire_refuerzo_lateral = normalizar_si_no(lemaire_refuerzo_lateral),
relesion = normalizar_si_no(relesion),
velocidad = factor(velocidad),
categoria_momento_pop_derivada = factor(
case_when(
is.na(momento_pop_meses) ~ NA_character_,
momento_pop_meses < 6 ~ "<6 meses",
momento_pop_meses >= 6 & momento_pop_meses < 8 ~ "6 a <8 meses",
momento_pop_meses >= 8 ~ "≥8 meses"
),
levels = c("<6 meses", "6 a <8 meses", "≥8 meses")
)
)
Se calcularon índices de simetría de acuerdo con la fórmula:
\[ \text{Simetría} = \frac{\text{valor implicado}}{\text{valor no implicado}} \times 100 \]
Se generaron simetrías para:
# ============================================================
# 5.1) CREAR VARIABLES DE SIMETRÍA
# ============================================================
datos_iso <- datos_iso %>%
mutate(
simetria_pico_par_extension = calcular_simetria(
valor_implicado = pico_del_par_extension_implicado,
valor_no_implicado = pico_del_par_extension_no_implicado
),
simetria_pico_par_flexion = calcular_simetria(
valor_implicado = pico_del_par_flexion_implicado,
valor_no_implicado = pico_del_par_flexion_no_implicado
),
simetria_trabj_peso_corporal_extension = calcular_simetria(
valor_implicado = trabj_peso_corporal_extension_implicado,
valor_no_implicado = trabj_peso_corporal_extension_no_implicado
),
simetria_trabj_peso_corporal_flexion = calcular_simetria(
valor_implicado = trabj_peso_corporal_flexion_implicado,
valor_no_implicado = trabj_peso_corporal_flexion_no_implicado
),
simetria_trabajo_total_extension = calcular_simetria(
valor_implicado = trabajo_total_extension_implicado,
valor_no_implicado = trabajo_total_extension_no_implicado
),
simetria_trabajo_total_flexion = calcular_simetria(
valor_implicado = trabajo_total_flexion_implicado,
valor_no_implicado = trabajo_total_flexion_no_implicado
)
)
vars_simetrias <- c(
"simetria_pico_par_extension",
"simetria_pico_par_flexion",
"simetria_trabj_peso_corporal_extension",
"simetria_trabj_peso_corporal_flexion",
"simetria_trabajo_total_extension",
"simetria_trabajo_total_flexion"
)
# ============================================================
# 6) VERIFICACIÓN INICIAL DE LA BASE LIMPIA
# ============================================================
glimpse(datos_iso)
Rows: 252
Columns: 52
$ id <chr> "872", "872", "872", "948", "948", "948", "1024",…
$ nacido <date> 1997-09-20, 1997-09-20, 1997-09-20, 1998-01-19, …
$ altura <dbl> 180, 180, 180, 182, 182, 182, 182, 182, 182, 184,…
$ peso <dbl> 76, 76, 76, 76, 76, 76, 76, 76, 76, 80, 80, 80, 8…
$ sesion <date> 2024-12-11, 2024-12-11, 2024-12-11, 2024-10-24, …
$ implicado <fct> Derecho, Derecho, Derecho, Derecho, Derecho, Dere…
$ nivel_de_competencia <fct> Primera división, Primera división, Primera divis…
$ momento_pop_meses <dbl> 7.0, 7.0, 7.0, 7.5, 7.5, 7.5, 6.5, 6.5, 6.5, 6.0,…
$ categoria_momento_pop_original <fct> 6-8 meses, 6-8 meses, 6-8 meses, 6-8 meses, 6-8 m…
$ injerto <fct> NA, NA, NA, NA, NA, NA, STRI, STRI, STRI, NA, NA,…
$ lemaire_refuerzo_lateral <fct> No, No, No, No, No, No, No, No, No, No, No, No, N…
$ relesion <fct> Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, S…
$ velocidad <fct> 60°/seg, 180°/seg, 300°/seg, 60°/seg, 180°/seg, 3…
$ pico_del_par_extension_no_implicado <dbl> 240.4, 175.1, 124.5, 202.3, 153.4, 108.7, 194.4, …
$ pico_del_par_extension_implicado <dbl> 238.8, 169.3, 118.4, 187.4, 145.0, 108.8, 202.5, …
$ pico_del_par_flexion_no_implicado <dbl> 163.6, 115.9, 106.3, 161.8, 122.8, 100.6, 127.5, …
$ pico_del_par_flexion_implicado <dbl> 162.9, 116.6, 111.1, 149.5, 119.8, 109.4, 137.9, …
$ n_rep_trabajo_max_extension_no_implicado <dbl> 4, 2, 4, 3, 4, 4, 5, 4, 3, 4, 6, 6, 3, 3, 2, 4, 6…
$ n_rep_trabajo_max_extension_implicado <dbl> 4, 2, 3, 3, 4, 3, 5, 3, 3, 5, 2, 2, 4, 9, 4, 1, 1…
$ n_rep_trabajo_max_flexion_no_implicado <dbl> 3, 2, 3, 2, 3, 3, 2, 2, 2, 5, 2, 2, 1, 2, 1, 5, 4…
$ n_rep_trabajo_max_flexion_implicado <dbl> 4, 1, 1, 2, 5, 3, 4, 4, 3, 2, 3, 6, 2, 4, 2, 1, 2…
$ trabj_peso_corporal_extension_no_implicado <dbl> 265.9, 224.7, 160.9, 274.7, 225.7, 182.0, 221.9, …
$ trabj_peso_corporal_extension_implicado <dbl> 298.2, 246.5, 180.2, 262.0, 221.6, 179.4, 260.3, …
$ trabj_peso_corporal_flexion_no_implicado <dbl> 250.3, 204.4, 159.2, 284.2, 214.3, 167.0, 154.3, …
$ trabj_peso_corporal_flexion_implicado <dbl> 232.7, 199.5, 156.6, 255.6, 212.1, 163.1, 193.0, …
$ trabajo_total_extension_no_implicado <dbl> 908.8, 1526.1, 1551.3, 991.9, 1561.2, 1707.7, 778…
$ trabajo_total_extension_implicado <dbl> 1085.7, 1666.3, 1727.1, 963.9, 1556.9, 1752.2, 81…
$ trabajo_total_flexion_no_implicado <dbl> 855.3, 1410.0, 1553.7, 1020.6, 1506.3, 1581.8, 56…
$ trabajo_total_flexion_implicado <dbl> 853.4, 1370.5, 1471.2, 928.2, 1475.9, 1599.8, 664…
$ rango_de_mov_extension_no_implicado <dbl> 88.7, 88.8, 88.0, 88.0, 88.9, 88.6, 69.7, 89.3, 8…
$ rango_de_mov_extension_implicado <dbl> 86.4, 89.4, 88.8, 85.8, 89.3, 89.3, 83.1, 88.7, 8…
$ rango_de_mov_flexion_no_implicado <dbl> 88.7, 88.8, 88.0, 88.0, 88.9, 88.6, 69.7, 89.3, 8…
$ rango_de_mov_flexion_implicado <dbl> 86.4, 89.4, 88.8, 85.8, 89.3, 89.3, 83.1, 88.7, 8…
$ razon_agon_antagon_extension_no_implicado <dbl> 68.1, 66.2, 85.4, 80.0, 80.0, 92.6, 65.6, 62.7, 7…
$ razon_agon_antagon_extension_implicado <dbl> 68.2, 68.9, 93.8, 79.8, 82.6, 100.6, 68.1, 68.2, …
$ razon_agon_antagon_flexion_no_implicado <dbl> -12.1, -9.7, -12.0, 4.6, 1.8, 1.4, -17.3, 3.0, -0…
$ razon_agon_antagon_flexion_implicado <dbl> -19.5, -9.2, -11.3, 2.8, 0.3, -2.6, -5.1, 4.4, 1.…
$ simetria_pico_del_par_extension_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_pico_del_par_flexion_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabj_peso_corporal_extension_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabj_peso_corporal_flexion_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabajo_total_extension_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabajo_total_flexion_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ edad_evaluacion <int> 27, 27, 27, 26, 26, 26, 32, 32, 32, 22, 22, 22, 2…
$ momento_pop_meses_redondeado <dbl> 7, 7, 7, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 10, …
$ categoria_momento_pop_derivada <fct> 6 a <8 meses, 6 a <8 meses, 6 a <8 meses, 6 a <8 …
$ simetria_pico_par_extension <dbl> 99.33444, 96.68761, 95.10040, 92.63470, 94.52412,…
$ simetria_pico_par_flexion <dbl> 99.57213, 100.60397, 104.51552, 92.39802, 97.5570…
$ simetria_trabj_peso_corporal_extension <dbl> 112.14742, 109.70182, 111.99503, 95.37677, 98.183…
$ simetria_trabj_peso_corporal_flexion <dbl> 92.96844, 97.60274, 98.36683, 89.93666, 98.97340,…
$ simetria_trabajo_total_extension <dbl> 119.46523, 109.18682, 111.33243, 97.17713, 99.724…
$ simetria_trabajo_total_flexion <dbl> 99.77786, 97.19858, 94.69009, 90.94650, 97.98181,…
summary(datos_iso)
id nacido altura peso sesion
Length:252 Min. :1988-10-27 Min. :168.0 Min. : 61.0 Min. :2024-01-11
Class :character 1st Qu.:1998-02-19 1st Qu.:175.0 1st Qu.: 73.0 1st Qu.:2025-01-21
Mode :character Median :2001-01-03 Median :178.0 Median : 76.0 Median :2025-04-09
Mean :2000-06-22 Mean :178.6 Mean : 77.3 Mean :2025-03-27
3rd Qu.:2003-05-29 3rd Qu.:182.5 3rd Qu.: 80.5 3rd Qu.:2025-07-02
Max. :2008-03-22 Max. :194.0 Max. :110.0 Max. :2026-03-18
NA's :9
implicado nivel_de_competencia momento_pop_meses categoria_momento_pop_original
Derecho :150 Amateur : 42 Min. : 3.000 <6 meses :75
Izquierdo:102 Primera división:117 1st Qu.: 5.375 6-8 meses:78
Segunda división: 75 Median : 7.000 >8 meses :99
Tercera División: 18 Mean : 7.718
3rd Qu.: 9.000
Max. :26.000
injerto lemaire_refuerzo_lateral relesion velocidad pico_del_par_extension_no_implicado
HTH : 72 No:231 No:216 180°/seg:84 Min. : 91.3
STRI : 36 Sí: 21 Sí: 36 300°/seg:84 1st Qu.:137.4
Cuadricipital: 3 60°/seg :84 Median :168.2
NA's :141 Mean :181.4
3rd Qu.:222.1
Max. :336.5
pico_del_par_extension_implicado pico_del_par_flexion_no_implicado pico_del_par_flexion_implicado
Min. : 59.3 Min. : 45.50 Min. : 54.8
1st Qu.:117.5 1st Qu.: 93.47 1st Qu.: 91.7
Median :143.7 Median :111.95 Median :109.6
Mean :153.0 Mean :113.79 Mean :111.0
3rd Qu.:177.5 3rd Qu.:129.78 3rd Qu.:127.2
Max. :322.2 Max. :186.00 Max. :192.3
n_rep_trabajo_max_extension_no_implicado n_rep_trabajo_max_extension_implicado
Min. :1.000 Min. : 1.00
1st Qu.:2.000 1st Qu.: 3.00
Median :3.000 Median : 4.00
Mean :3.425 Mean : 4.02
3rd Qu.:4.000 3rd Qu.: 5.00
Max. :8.000 Max. :13.00
n_rep_trabajo_max_flexion_no_implicado n_rep_trabajo_max_flexion_implicado
Min. : 1.000 Min. : 1.000
1st Qu.: 2.000 1st Qu.: 2.000
Median : 3.000 Median : 3.000
Mean : 3.083 Mean : 3.242
3rd Qu.: 4.000 3rd Qu.: 4.000
Max. :14.000 Max. :15.000
trabj_peso_corporal_extension_no_implicado trabj_peso_corporal_extension_implicado
Min. : 61.5 Min. : 50.2
1st Qu.:193.6 1st Qu.:168.0
Median :232.7 Median :196.1
Mean :237.7 Mean :204.0
3rd Qu.:274.0 3rd Qu.:238.0
Max. :417.7 Max. :374.1
trabj_peso_corporal_flexion_no_implicado trabj_peso_corporal_flexion_implicado
Min. : 27.5 Min. : 45.6
1st Qu.:141.7 1st Qu.:129.8
Median :165.8 Median :157.9
Mean :169.0 Mean :161.6
3rd Qu.:198.1 3rd Qu.:193.1
Max. :300.9 Max. :334.7
trabajo_total_extension_no_implicado trabajo_total_extension_implicado
Min. : 713.9 Min. : 439.5
1st Qu.:1156.0 1st Qu.: 983.7
Median :1551.8 Median :1360.2
Mean :1502.4 Mean :1298.9
3rd Qu.:1817.8 3rd Qu.:1611.5
Max. :2397.6 Max. :2293.0
trabajo_total_flexion_no_implicado trabajo_total_flexion_implicado rango_de_mov_extension_no_implicado
Min. : 275.2 Min. : 315.4 Min. :63.80
1st Qu.: 769.3 1st Qu.: 733.2 1st Qu.:87.78
Median :1079.6 Median :1019.3 Median :88.70
Mean :1069.5 Mean :1027.0 Mean :86.82
3rd Qu.:1337.9 3rd Qu.:1310.6 3rd Qu.:89.22
Max. :1901.8 Max. :1844.9 Max. :91.40
rango_de_mov_extension_implicado rango_de_mov_flexion_no_implicado rango_de_mov_flexion_implicado
Min. :54.40 Min. :63.80 Min. :54.40
1st Qu.:86.67 1st Qu.:87.78 1st Qu.:86.67
Median :88.30 Median :88.70 Median :88.30
Mean :86.27 Mean :86.82 Mean :86.27
3rd Qu.:89.00 3rd Qu.:89.22 3rd Qu.:89.00
Max. :90.90 Max. :91.40 Max. :90.90
razon_agon_antagon_extension_no_implicado razon_agon_antagon_extension_implicado
Min. : 29.40 Min. : 39.50
1st Qu.: 56.25 1st Qu.: 64.78
Median : 63.90 Median : 73.20
Mean : 64.87 Mean : 75.23
3rd Qu.: 74.12 3rd Qu.: 86.00
Max. :103.60 Max. :129.00
razon_agon_antagon_flexion_no_implicado razon_agon_antagon_flexion_implicado
Min. :-19.700 Min. :-41.500
1st Qu.: 1.625 1st Qu.: 3.175
Median : 10.100 Median : 9.800
Mean : 10.796 Mean : 11.258
3rd Qu.: 20.300 3rd Qu.: 20.375
Max. : 43.600 Max. : 48.900
NA's :96 NA's :96
simetria_pico_del_par_extension_percent simetria_pico_del_par_flexion_percent
Length:252 Length:252
Class :character Class :character
Mode :character Mode :character
simetria_trabj_peso_corporal_extension_percent simetria_trabj_peso_corporal_flexion_percent
Length:252 Length:252
Class :character Class :character
Mode :character Mode :character
simetria_trabajo_total_extension_percent simetria_trabajo_total_flexion_percent edad_evaluacion
Length:252 Length:252 Min. :17.00
Class :character Class :character 1st Qu.:21.00
Mode :character Mode :character Median :24.00
Mean :24.28
3rd Qu.:26.00
Max. :36.00
NA's :9
momento_pop_meses_redondeado categoria_momento_pop_derivada simetria_pico_par_extension
Min. : 3.000 <6 meses :75 Min. : 44.16
1st Qu.: 5.750 6 a <8 meses:78 1st Qu.: 76.72
Median : 7.000 ≥8 meses :99 Median : 86.16
Mean : 7.881 Mean : 85.46
3rd Qu.: 9.000 3rd Qu.: 94.28
Max. :26.000 Max. :123.22
simetria_pico_par_flexion simetria_trabj_peso_corporal_extension simetria_trabj_peso_corporal_flexion
Min. : 64.86 Min. : 54.09 Min. : 51.45
1st Qu.: 88.20 1st Qu.: 77.54 1st Qu.: 84.84
Median : 98.15 Median : 86.53 Median : 96.01
Mean : 98.65 Mean : 86.70 Mean : 98.12
3rd Qu.:107.90 3rd Qu.: 96.12 3rd Qu.:106.76
Max. :161.47 Max. :125.34 Max. :287.64
simetria_trabajo_total_extension simetria_trabajo_total_flexion
Min. : 51.13 Min. : 54.90
1st Qu.: 77.33 1st Qu.: 85.84
Median : 85.71 Median : 95.99
Mean : 86.43 Mean : 98.43
3rd Qu.: 95.54 3rd Qu.:106.25
Max. :144.09 Max. :341.42
skimr::skim(datos_iso)
── Data Summary ────────────────────────
Values
Name datos_iso
Number of rows 252
Number of columns 52
_______________________
Column type frequency:
character 7
Date 2
factor 8
numeric 35
________________________
Group variables None
Se revisó la cantidad de datos faltantes por variable y se realizaron controles de consistencia para identificar:
# ============================================================
# 7) TABLA GENERAL DE FALTANTES
# ============================================================
tabla_faltantes <- datos_iso %>%
summarise(
across(
everything(),
~ sum(is.na(.))
)
) %>%
pivot_longer(
cols = everything(),
names_to = "variable",
values_to = "n_faltantes"
) %>%
mutate(
porcentaje_faltantes = round(100 * n_faltantes / nrow(datos_iso), 1)
) %>%
arrange(desc(n_faltantes))
tabla_faltantes
naniar::vis_miss(datos_iso)
visdat::vis_dat(datos_iso)
# ============================================================
# 8) RESUMEN DE VARIABLES CATEGÓRICAS
# ============================================================
vars_categoricas <- c(
"implicado",
"nivel_de_competencia",
"categoria_momento_pop_original",
"categoria_momento_pop_derivada",
"injerto",
"lemaire_refuerzo_lateral",
"relesion",
"velocidad"
)
resumen_categoricas <- purrr::map_dfr(
vars_categoricas,
function(v){
datos_iso %>%
group_by(valor = .data[[v]]) %>%
summarise(n = n(), .groups = "drop") %>%
mutate(
variable = v,
porcentaje = round(100 * n / nrow(datos_iso), 1)
) %>%
select(variable, valor, n, porcentaje)
}
)
resumen_categoricas
# ============================================================
# 9) VALORES NO ESPERADOS ANTES DE LA RECODIFICACIÓN
# ============================================================
valores_implicado_no_esperados <- datos_raw %>%
transmute(
implicado_original = implicado,
implicado_normalizado = normalizar_texto(implicado)
) %>%
filter(
!is.na(implicado_normalizado),
!implicado_normalizado %in% c("derecho", "izquierdo")
) %>%
distinct()
valores_implicado_no_esperados
valores_injerto_no_esperados <- datos_raw %>%
transmute(
injerto_original = injerto,
injerto_normalizado = normalizar_texto(injerto)
) %>%
filter(
!is.na(injerto_normalizado),
!injerto_normalizado %in% c("hth", "stri", "cuadricipital")
) %>%
distinct()
valores_injerto_no_esperados
valores_lemaire_no_esperados <- datos_raw %>%
transmute(
lemaire_original = lemaire_refuerzo_lateral,
lemaire_normalizado = normalizar_texto(lemaire_refuerzo_lateral)
) %>%
filter(
!is.na(lemaire_normalizado),
!lemaire_normalizado %in% c("si", "s", "yes", "y", "1", "no", "n", "0")
) %>%
distinct()
valores_lemaire_no_esperados
valores_relesion_no_esperados <- datos_raw %>%
transmute(
relesion_original = relesion,
relesion_normalizado = normalizar_texto(relesion)
) %>%
filter(
!is.na(relesion_normalizado),
!relesion_normalizado %in% c("si", "s", "yes", "y", "1", "no", "n", "0")
) %>%
distinct()
valores_relesion_no_esperados
La base original contiene una fila por:
Para caracterizar la muestra y construir el flujo de selección, se generó una base con una fila por evaluación:
\[ \text{evaluación} = \text{ID del jugador} \times \text{fecha de sesión} \]
# ============================================================
# 10) BASE A NIVEL DE EVALUACIÓN
# ============================================================
evaluaciones_iso <- datos_iso %>%
distinct(
id,
sesion,
.keep_all = TRUE
)
evaluaciones_iso
Para el análisis descriptivo final se aplicaron los siguientes criterios:
La exclusión por relesión se aplicó a nivel del jugador, ya que la relesión representa una característica clínica del caso y no de una fila aislada.
# ============================================================
# 11) FLUJO DE SELECCIÓN DE LA COHORTE ANALÍTICA
# ============================================================
# Jugadores con al menos una relesión registrada
ids_relesion_si <- datos_iso %>%
group_by(id) %>%
summarise(
relesion_si = any(relesion == "Sí", na.rm = TRUE),
.groups = "drop"
) %>%
filter(relesion_si) %>%
pull(id)
# Etapa 1: base inicial a nivel de evaluación
evaluaciones_iniciales <- evaluaciones_iso
# Etapa 2: excluir jugadores con relesión
evaluaciones_sin_relesion <- evaluaciones_iniciales %>%
filter(!id %in% ids_relesion_si)
# Etapa 3: excluir evaluaciones sin momento POP
evaluaciones_con_pop <- evaluaciones_sin_relesion %>%
filter(!is.na(momento_pop_meses))
# Etapa 4: excluir evaluaciones >18 meses
evaluaciones_hasta_18m <- evaluaciones_con_pop %>%
filter(momento_pop_meses <= 18)
# Etapa 5: crear grupo competitivo y excluir no clasificables
evaluaciones_analisis <- evaluaciones_hasta_18m %>%
mutate(
grupo_competencia = case_when(
nivel_de_competencia == "Primera división" ~ "Primera",
nivel_de_competencia == "Segunda división" ~ "Segunda",
nivel_de_competencia %in% c(
"Tercera División",
"Amateur"
) ~ "Tercera + Amateur",
TRUE ~ NA_character_
),
grupo_competencia = factor(
grupo_competencia,
levels = c(
"Primera",
"Segunda",
"Tercera + Amateur"
)
)
) %>%
filter(!is.na(grupo_competencia))
# Función auxiliar para resumir cada etapa
resumir_etapa_flujo <- function(data, etapa){
tibble::tibble(
etapa = etapa,
n_jugadores = n_distinct(data$id),
n_evaluaciones = nrow(data)
)
}
flujo_exclusiones <- bind_rows(
resumir_etapa_flujo(
evaluaciones_iniciales,
"Base inicial"
),
resumir_etapa_flujo(
evaluaciones_sin_relesion,
"Luego de excluir jugadores con relesión"
),
resumir_etapa_flujo(
evaluaciones_con_pop,
"Luego de excluir evaluaciones sin momento POP"
),
resumir_etapa_flujo(
evaluaciones_hasta_18m,
"Luego de excluir evaluaciones >18 meses"
),
resumir_etapa_flujo(
evaluaciones_analisis,
"Cohorte analítica final"
)
) %>%
mutate(
jugadores_excluidos_desde_etapa_previa = lag(n_jugadores) - n_jugadores,
evaluaciones_excluidas_desde_etapa_previa = lag(n_evaluaciones) - n_evaluaciones
)
flujo_exclusiones
# Detalle de exclusiones por motivo
detalle_exclusiones <- bind_rows(
evaluaciones_iniciales %>%
filter(id %in% ids_relesion_si) %>%
summarise(
motivo = "Jugadores con relesión",
n_jugadores_excluidos = n_distinct(id),
n_evaluaciones_excluidas = n()
),
evaluaciones_sin_relesion %>%
filter(is.na(momento_pop_meses)) %>%
summarise(
motivo = "Evaluaciones sin momento POP disponible",
n_jugadores_excluidos = n_distinct(id),
n_evaluaciones_excluidas = n()
),
evaluaciones_con_pop %>%
filter(momento_pop_meses > 18) %>%
summarise(
motivo = "Evaluaciones realizadas a >18 meses",
n_jugadores_excluidos = n_distinct(id),
n_evaluaciones_excluidas = n()
),
evaluaciones_hasta_18m %>%
mutate(
grupo_competencia = case_when(
nivel_de_competencia == "Primera división" ~ "Primera",
nivel_de_competencia == "Segunda división" ~ "Segunda",
nivel_de_competencia %in% c(
"Tercera División",
"Amateur"
) ~ "Tercera + Amateur",
TRUE ~ NA_character_
)
) %>%
filter(is.na(grupo_competencia)) %>%
summarise(
motivo = "Evaluaciones sin grupo competitivo clasificable",
n_jugadores_excluidos = n_distinct(id),
n_evaluaciones_excluidas = n()
)
)
detalle_exclusiones
Se partió de 58 jugadores y 84 evaluaciones a nivel de sesión.
Durante la depuración:
La cohorte analítica final quedó conformada por 46 jugadores y 70 evaluaciones.
Esta información puede utilizarse directamente para construir el diagrama de flujo de selección de la muestra.
Una vez definida la cohorte de evaluaciones elegibles, se recuperan las tres filas correspondientes a las velocidades isocinéticas.
# ============================================================
# 12) BASE ANALÍTICA FINAL CON LAS TRES VELOCIDADES
# ============================================================
datos_analisis <- datos_iso %>%
inner_join(
evaluaciones_analisis %>%
select(
id,
sesion,
grupo_competencia
),
by = c("id", "sesion")
)
resumen_muestra_analitica <- tibble::tibble(
n_jugadores = n_distinct(evaluaciones_analisis$id),
n_evaluaciones = nrow(evaluaciones_analisis),
n_filas_isocineticas = nrow(datos_analisis)
)
resumen_muestra_analitica
La cohorte analítica incluyó:
# ============================================================
# 13) JUGADORES Y EVALUACIONES POR GRUPO COMPETITIVO
# ============================================================
conteo_muestra_por_grupo <- evaluaciones_analisis %>%
group_by(grupo_competencia) %>%
summarise(
n_jugadores = n_distinct(id),
n_evaluaciones = n(),
.groups = "drop"
)
conteo_muestra_por_grupo
texto_grupos <- conteo_muestra_por_grupo %>%
mutate(
texto = paste0(
grupo_competencia,
": ",
n_jugadores,
" jugadores y ",
n_evaluaciones,
" evaluaciones"
)
) %>%
pull(texto) %>%
paste(collapse = "; ")
texto_grupos
[1] "Primera: 18 jugadores y 29 evaluaciones; Segunda: 13 jugadores y 22 evaluaciones; Tercera + Amateur: 15 jugadores y 19 evaluaciones"
La distribución por nivel competitivo fue la siguiente:
Primera: 18 jugadores y 29 evaluaciones; Segunda: 13 jugadores y 22 evaluaciones; Tercera + Amateur: 15 jugadores y 19 evaluaciones.
# ============================================================
# 14) JUGADORES Y EVALUACIONES POR GRUPO Y TIEMPO
# ============================================================
conteo_muestra_por_grupo_y_tiempo <- evaluaciones_analisis %>%
group_by(
grupo_competencia,
categoria_momento_pop_derivada
) %>%
summarise(
n_jugadores = n_distinct(id),
n_evaluaciones = n(),
.groups = "drop"
) %>%
arrange(
grupo_competencia,
categoria_momento_pop_derivada
)
conteo_muestra_por_grupo_y_tiempo
# ============================================================
# 15) FUNCIONES DE FORMATO
# ============================================================
formatear_numero <- function(x, digitos = 2){
ifelse(
is.na(x),
"NA",
formatC(
x,
format = "f",
digits = digitos,
decimal.mark = ","
)
)
}
formatear_media_de <- function(media, de, digitos = 2){
paste0(
formatear_numero(media, digitos),
" ± ",
formatear_numero(de, digitos)
)
}
formatear_mediana_iqr <- function(mediana, p25, p75, digitos = 2){
paste0(
formatear_numero(mediana, digitos),
" [",
formatear_numero(p25, digitos),
"; ",
formatear_numero(p75, digitos),
"]"
)
}
formatear_n_porcentaje <- function(n, porcentaje, digitos = 1){
paste0(
n,
" (",
formatear_numero(porcentaje, digitos),
"%)"
)
}
La Tabla 1 se construyó a nivel de evaluación única. Las variables numéricas se resumen de forma amplia para permitir decidir posteriormente si la versión final del manuscrito reportará:
En esta etapa se presentan ambas formas de resumen.
# ============================================================
# 16) VARIABLES PARA TABLA 1
# ============================================================
vars_tabla1_numericas <- c(
"edad_evaluacion",
"altura",
"peso",
"momento_pop_meses"
)
etiquetas_tabla1_numericas <- c(
"Edad al momento de la evaluación, años",
"Altura, cm",
"Peso, kg",
"Momento postquirúrgico, meses"
)
vars_tabla1_categoricas <- c(
"implicado",
"categoria_momento_pop_derivada",
"injerto",
"lemaire_refuerzo_lateral"
)
etiquetas_tabla1_categoricas <- c(
"Miembro implicado",
"Momento de evaluación",
"Tipo de injerto",
"Refuerzo lateral tipo Lemaire"
)
# ============================================================
# 17) DISPONIBILIDAD DE DATOS DE TABLA 1
# ============================================================
n_disponibles_tabla1_total <- bind_rows(
purrr::map2_dfr(
vars_tabla1_numericas,
etiquetas_tabla1_numericas,
function(v, etiqueta){
evaluaciones_analisis %>%
filter(!is.na(.data[[v]])) %>%
summarise(
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id)
)
}
),
purrr::map2_dfr(
vars_tabla1_categoricas,
etiquetas_tabla1_categoricas,
function(v, etiqueta){
evaluaciones_analisis %>%
filter(!is.na(.data[[v]])) %>%
summarise(
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id)
)
}
)
)
n_disponibles_tabla1_total
n_disponibles_tabla1_por_grupo <- bind_rows(
purrr::map2_dfr(
vars_tabla1_numericas,
etiquetas_tabla1_numericas,
function(v, etiqueta){
evaluaciones_analisis %>%
filter(!is.na(.data[[v]])) %>%
group_by(grupo_competencia) %>%
summarise(
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id),
.groups = "drop"
)
}
),
purrr::map2_dfr(
vars_tabla1_categoricas,
etiquetas_tabla1_categoricas,
function(v, etiqueta){
evaluaciones_analisis %>%
filter(!is.na(.data[[v]])) %>%
group_by(grupo_competencia) %>%
summarise(
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id),
.groups = "drop"
)
}
)
) %>%
arrange(
variable,
grupo_competencia
)
n_disponibles_tabla1_por_grupo
La tabla de disponibilidad permite identificar sobre cuántas
evaluaciones y jugadores se calculó cada variable.
Por ejemplo, si una variable tiene 70 evaluaciones disponibles y 46
jugadores disponibles, significa que esa información estuvo registrada
en toda la cohorte final. Si presenta un número menor, existen datos
faltantes para esa variable.
# ============================================================
# 18) TABLA 1 - VARIABLES NUMÉRICAS
# ============================================================
tabla1_numericas_resumen <- purrr::map2_dfr(
vars_tabla1_numericas,
etiquetas_tabla1_numericas,
function(v, etiqueta){
tmp <- evaluaciones_analisis %>%
transmute(
id,
grupo_competencia,
valor = .data[[v]]
) %>%
filter(
!is.na(valor),
!is.na(grupo_competencia)
)
resumen_por_grupo <- tmp %>%
group_by(grupo_competencia) %>%
summarise(
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id),
media = mean(valor, na.rm = TRUE),
de = sd(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
iqr = IQR(valor, na.rm = TRUE),
minimo = min(valor, na.rm = TRUE),
maximo = max(valor, na.rm = TRUE),
.groups = "drop"
)
resumen_total <- tmp %>%
summarise(
grupo_competencia = "Total",
variable = etiqueta,
n_evaluaciones_disponibles = n(),
n_jugadores_disponibles = n_distinct(id),
media = mean(valor, na.rm = TRUE),
de = sd(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
iqr = IQR(valor, na.rm = TRUE),
minimo = min(valor, na.rm = TRUE),
maximo = max(valor, na.rm = TRUE)
)
bind_rows(
resumen_por_grupo %>%
mutate(grupo_competencia = as.character(grupo_competencia)),
resumen_total
)
}
) %>%
relocate(
variable,
grupo_competencia,
n_evaluaciones_disponibles,
n_jugadores_disponibles
)
tabla1_numericas_resumen
tabla1_numericas_formateada <- tabla1_numericas_resumen %>%
mutate(
media_de = formatear_media_de(
media,
de,
digitos = 2
),
mediana_p25_p75 = formatear_mediana_iqr(
mediana,
p25,
p75,
digitos = 2
)
) %>%
select(
variable,
grupo_competencia,
n_evaluaciones_disponibles,
n_jugadores_disponibles,
media_de,
mediana_p25_p75,
iqr,
minimo,
maximo
)
tabla1_numericas_formateada
# ============================================================
# 19) TABLA 1 - VARIABLES CATEGÓRICAS
# ============================================================
tabla1_categoricas_resumen <- purrr::map2_dfr(
vars_tabla1_categoricas,
etiquetas_tabla1_categoricas,
function(v, etiqueta){
tmp <- evaluaciones_analisis %>%
transmute(
id,
grupo_competencia,
categoria = as.character(.data[[v]])
) %>%
filter(
!is.na(categoria),
!is.na(grupo_competencia)
)
denominadores_grupo <- tmp %>%
group_by(grupo_competencia) %>%
summarise(
n_evaluaciones_disponibles_variable = n(),
n_jugadores_disponibles_variable = n_distinct(id),
.groups = "drop"
)
resumen_por_grupo <- tmp %>%
group_by(
grupo_competencia,
categoria
) %>%
summarise(
variable = etiqueta,
n_evaluaciones_categoria = n(),
n_jugadores_categoria = n_distinct(id),
.groups = "drop"
) %>%
left_join(
denominadores_grupo,
by = "grupo_competencia"
) %>%
mutate(
porcentaje_evaluaciones =
100 * n_evaluaciones_categoria /
n_evaluaciones_disponibles_variable
)
denominador_total <- tmp %>%
summarise(
grupo_competencia = "Total",
n_evaluaciones_disponibles_variable = n(),
n_jugadores_disponibles_variable = n_distinct(id)
)
resumen_total <- tmp %>%
group_by(categoria) %>%
summarise(
grupo_competencia = "Total",
variable = etiqueta,
n_evaluaciones_categoria = n(),
n_jugadores_categoria = n_distinct(id),
.groups = "drop"
) %>%
bind_cols(
denominador_total %>%
select(
n_evaluaciones_disponibles_variable,
n_jugadores_disponibles_variable
)
) %>%
mutate(
porcentaje_evaluaciones =
100 * n_evaluaciones_categoria /
n_evaluaciones_disponibles_variable
)
bind_rows(
resumen_por_grupo %>%
mutate(grupo_competencia = as.character(grupo_competencia)),
resumen_total
)
}
) %>%
relocate(
variable,
categoria,
grupo_competencia,
n_evaluaciones_categoria,
porcentaje_evaluaciones,
n_jugadores_categoria,
n_evaluaciones_disponibles_variable,
n_jugadores_disponibles_variable
)
tabla1_categoricas_resumen
tabla1_categoricas_formateada <- tabla1_categoricas_resumen %>%
mutate(
n_porcentaje_evaluaciones = formatear_n_porcentaje(
n_evaluaciones_categoria,
porcentaje_evaluaciones,
digitos = 1
)
) %>%
select(
variable,
categoria,
grupo_competencia,
n_porcentaje_evaluaciones,
n_jugadores_categoria,
n_evaluaciones_disponibles_variable,
n_jugadores_disponibles_variable
)
tabla1_categoricas_formateada
La evaluación de distribución se realizó únicamente para las
variables numéricas de la Tabla 1.
Este paso permite decidir, de cara a la versión final del manuscrito, si
cada variable conviene reportarla como:
La decisión no debe basarse en un único indicador, sino en la combinación de:
# ============================================================
# 20) ANÁLISIS DE DISTRIBUCIÓN - TABLA 1
# ============================================================
asimetria_pearson <- function(x){
x <- x[!is.na(x)]
if(length(x) < 3 || sd(x) == 0){
return(NA_real_)
}
3 * (mean(x) - median(x)) / sd(x)
}
contar_outliers_iqr <- function(x){
x <- x[!is.na(x)]
if(length(x) < 4){
return(NA_integer_)
}
q1 <- quantile(x, 0.25, na.rm = TRUE)
q3 <- quantile(x, 0.75, na.rm = TRUE)
iqr <- q3 - q1
limite_inferior <- q1 - 1.5 * iqr
limite_superior <- q3 + 1.5 * iqr
sum(
x < limite_inferior |
x > limite_superior,
na.rm = TRUE
)
}
shapiro_p_seguro <- function(x){
x <- x[!is.na(x)]
if(
length(x) < 3 ||
length(x) > 5000 ||
length(unique(x)) < 3
){
return(NA_real_)
}
tryCatch(
shapiro.test(x)$p.value,
error = function(e) NA_real_
)
}
diagnostico_distribucion_tabla1_total <- purrr::map2_dfr(
vars_tabla1_numericas,
etiquetas_tabla1_numericas,
function(v, etiqueta){
x <- evaluaciones_analisis %>%
pull(.data[[v]]) %>%
na.omit()
tibble::tibble(
variable = etiqueta,
n_evaluaciones = length(x),
media = mean(x, na.rm = TRUE),
de = sd(x, na.rm = TRUE),
mediana = median(x, na.rm = TRUE),
p25 = quantile(x, 0.25, na.rm = TRUE),
p75 = quantile(x, 0.75, na.rm = TRUE),
iqr = IQR(x, na.rm = TRUE),
minimo = min(x, na.rm = TRUE),
maximo = max(x, na.rm = TRUE),
asimetria_pearson = asimetria_pearson(x),
n_outliers_iqr = contar_outliers_iqr(x),
shapiro_p = shapiro_p_seguro(x)
)
}
) %>%
mutate(
shapiro_p_formateado = case_when(
is.na(shapiro_p) ~ NA_character_,
shapiro_p < 0.001 ~ "<0,001",
TRUE ~ formatC(
shapiro_p,
format = "f",
digits = 3,
decimal.mark = ","
)
),
interpretacion_shapiro = case_when(
is.na(shapiro_p) ~ "No evaluable",
shapiro_p < 0.05 ~ "Evidencia contra normalidad",
TRUE ~ "Sin evidencia clara contra normalidad"
),
interpretacion_asimetria = case_when(
is.na(asimetria_pearson) ~ "No evaluable",
abs(asimetria_pearson) < 0.5 ~ "Asimetría baja",
abs(asimetria_pearson) >= 0.5 &
abs(asimetria_pearson) < 1 ~ "Asimetría moderada",
abs(asimetria_pearson) >= 1 ~ "Asimetría marcada"
)
)
diagnostico_distribucion_tabla1_total
diagnostico_distribucion_tabla1_por_grupo <- evaluaciones_analisis %>%
select(
id,
grupo_competencia,
all_of(vars_tabla1_numericas)
) %>%
pivot_longer(
cols = all_of(vars_tabla1_numericas),
names_to = "variable_original",
values_to = "valor"
) %>%
filter(!is.na(valor)) %>%
mutate(
variable = case_when(
variable_original == "edad_evaluacion" ~
"Edad al momento de la evaluación, años",
variable_original == "altura" ~
"Altura, cm",
variable_original == "peso" ~
"Peso, kg",
variable_original == "momento_pop_meses" ~
"Momento postquirúrgico, meses",
TRUE ~ variable_original
)
) %>%
group_by(
variable,
grupo_competencia
) %>%
summarise(
n_evaluaciones = n(),
n_jugadores = n_distinct(id),
media = mean(valor, na.rm = TRUE),
de = sd(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
iqr = IQR(valor, na.rm = TRUE),
minimo = min(valor, na.rm = TRUE),
maximo = max(valor, na.rm = TRUE),
asimetria_pearson = asimetria_pearson(valor),
n_outliers_iqr = contar_outliers_iqr(valor),
shapiro_p = shapiro_p_seguro(valor),
.groups = "drop"
) %>%
mutate(
shapiro_p_formateado = case_when(
is.na(shapiro_p) ~ NA_character_,
shapiro_p < 0.001 ~ "<0,001",
TRUE ~ formatC(
shapiro_p,
format = "f",
digits = 3,
decimal.mark = ","
)
),
interpretacion_shapiro = case_when(
is.na(shapiro_p) ~ "No evaluable",
shapiro_p < 0.05 ~ "Evidencia contra normalidad",
TRUE ~ "Sin evidencia clara contra normalidad"
),
interpretacion_asimetria = case_when(
is.na(asimetria_pearson) ~ "No evaluable",
abs(asimetria_pearson) < 0.5 ~ "Asimetría baja",
abs(asimetria_pearson) >= 0.5 &
abs(asimetria_pearson) < 1 ~ "Asimetría moderada",
abs(asimetria_pearson) >= 1 ~ "Asimetría marcada"
)
)
diagnostico_distribucion_tabla1_por_grupo
datos_tabla1_largo <- evaluaciones_analisis %>%
select(
grupo_competencia,
all_of(vars_tabla1_numericas)
) %>%
pivot_longer(
cols = all_of(vars_tabla1_numericas),
names_to = "variable_original",
values_to = "valor"
) %>%
filter(!is.na(valor)) %>%
mutate(
variable = case_when(
variable_original == "edad_evaluacion" ~
"Edad al momento de la evaluación, años",
variable_original == "altura" ~
"Altura, cm",
variable_original == "peso" ~
"Peso, kg",
variable_original == "momento_pop_meses" ~
"Momento postquirúrgico, meses",
TRUE ~ variable_original
)
)
graficos_histogramas_tabla1 <- datos_tabla1_largo %>%
split(.$variable) %>%
purrr::map(
~ ggplot(
.x,
aes(x = valor)
) +
geom_histogram(
bins = 10,
color = "black",
fill = "grey80"
) +
facet_wrap(
~ grupo_competencia,
scales = "free_y"
) +
labs(
title = unique(.x$variable),
x = "Valor",
y = "Frecuencia"
) +
theme_minimal()
)
graficos_qq_tabla1 <- datos_tabla1_largo %>%
split(.$variable) %>%
purrr::map(
~ ggplot(
.x,
aes(sample = valor)
) +
stat_qq() +
stat_qq_line() +
facet_wrap(
~ grupo_competencia,
scales = "free"
) +
labs(
title = paste0(
"Q-Q plot: ",
unique(.x$variable)
),
x = "Cuantiles teóricos",
y = "Cuantiles observados"
) +
theme_minimal()
)
graficos_histogramas_tabla1[["Edad al momento de la evaluación, años"]]
graficos_histogramas_tabla1[["Altura, cm"]]
graficos_histogramas_tabla1[["Peso, kg"]]
graficos_histogramas_tabla1[["Momento postquirúrgico, meses"]]
graficos_qq_tabla1[["Edad al momento de la evaluación, años"]]
graficos_qq_tabla1[["Altura, cm"]]
graficos_qq_tabla1[["Peso, kg"]]
graficos_qq_tabla1[["Momento postquirúrgico, meses"]]
La Tabla 2 resume las métricas de fuerza y desempeño isocinético según:
Se informan simultáneamente:
Esta estrategia evita tener que definir, para cada una de las múltiples variables de fuerza, un único estadístico de tendencia central.
# ============================================================
# 21) TABLA 2 - VARIABLES DE FUERZA ISOCINÉTICA
# ============================================================
vars_fuerza <- unique(c(
vars_metricas_continuas,
vars_repeticiones,
vars_simetrias
))
vars_fuerza <- vars_fuerza[
vars_fuerza %in% names(datos_analisis)
]
vars_fuerza_no_numericas <- vars_fuerza[
!vapply(
datos_analisis[vars_fuerza],
is.numeric,
logical(1)
)
]
if(length(vars_fuerza_no_numericas) > 0){
stop(
paste0(
"Hay variables de fuerza no numéricas: ",
paste(vars_fuerza_no_numericas, collapse = ", ")
)
)
}
datos_fuerza_largo <- datos_analisis %>%
select(
id,
sesion,
velocidad,
grupo_competencia,
all_of(vars_fuerza)
) %>%
pivot_longer(
cols = all_of(vars_fuerza),
names_to = "variable",
values_to = "valor"
) %>%
filter(
!is.na(valor),
!is.na(grupo_competencia),
!is.na(velocidad)
)
tabla2_fuerza_resumen_numerico <- datos_fuerza_largo %>%
group_by(
variable,
velocidad,
grupo_competencia
) %>%
summarise(
n_evaluaciones_disponibles = n_distinct(
paste(id, sesion)
),
n_jugadores_disponibles = n_distinct(id),
media = mean(valor, na.rm = TRUE),
de = sd(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
iqr = IQR(valor, na.rm = TRUE),
minimo = min(valor, na.rm = TRUE),
maximo = max(valor, na.rm = TRUE),
.groups = "drop"
)
tabla2_fuerza_formateada <- tabla2_fuerza_resumen_numerico %>%
mutate(
media_de = formatear_media_de(
media,
de,
digitos = 2
),
mediana_p25_p75 = formatear_mediana_iqr(
mediana,
p25,
p75,
digitos = 2
)
) %>%
select(
variable,
velocidad,
grupo_competencia,
n_evaluaciones_disponibles,
n_jugadores_disponibles,
media_de,
mediana_p25_p75,
iqr,
minimo,
maximo
)
tabla2_fuerza_formateada
Se genera un archivo Word con:
Se agrega un epígrafe explicativo sobre la cantidad de evaluaciones calculada.
archivo_word_tablas <- file.path(
getwd(),
"Tablas_1_y_2_descriptivas_isocinesia_RLCA.docx"
)
doc <- read_docx()
doc <- body_add_par(
doc,
"Tablas descriptivas del análisis de fuerza isocinética en futbolistas con RLCA",
style = "heading 1"
)
doc <- body_add_par(
doc,
paste0(
"Cohorte analítica final: ",
resumen_muestra_analitica$n_jugadores,
" jugadores, ",
resumen_muestra_analitica$n_evaluaciones,
" evaluaciones clínicas únicas y ",
resumen_muestra_analitica$n_filas_isocineticas,
" registros isocinéticos por velocidad."
),
style = "Normal"
)
doc <- body_add_par(
doc,
"Tabla 1. Características generales de la cohorte analítica",
style = "heading 1"
)
doc <- body_add_par(
doc,
"Panel A. Variables numéricas.",
style = "heading 2"
)
doc <- body_add_flextable(
doc,
value = ft_tabla1_num
)
doc <- body_add_par(
doc,
"* Evaluaciones disponibles corresponde a combinaciones únicas de jugador y fecha de sesión (ID × sesión). Un mismo jugador pudo aportar más de una evaluación si fue medido en distintos momentos postoperatorios.",
style = "Normal"
)
doc <- body_add_par(
doc,
"Panel B. Variables categóricas.",
style = "heading 2"
)
doc <- body_add_flextable(
doc,
value = ft_tabla1_cat
)
doc <- body_add_par(
doc,
"* Los porcentajes se calcularon sobre las evaluaciones con dato disponible para cada variable, excluyendo los faltantes.",
style = "Normal"
)
doc <- body_add_par(
doc,
"Tabla 2. Descripción de las variables de fuerza isocinética según velocidad y nivel competitivo",
style = "heading 1"
)
doc <- body_add_flextable(
doc,
value = ft_tabla2
)
doc <- body_add_par(
doc,
"* Evaluaciones disponibles corresponde al número de evaluaciones clínicas únicas utilizadas para calcular cada variable dentro de cada velocidad y grupo competitivo. Un mismo jugador pudo aportar más de una evaluación en diferentes momentos postoperatorios.",
style = "Normal"
)
print(
doc,
target = archivo_word_tablas
)
archivo_word_tablas
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/Tablas_1_y_2_descriptivas_isocinesia_RLCA.docx"
Para representar variables continuas se propone utilizar puntos con barras verticales P25–P75, en lugar de barras macizas. Esta elección permite mostrar simultáneamente:
Se diseñan tres figuras:
Cada figura se estratifica por:
# ============================================================
# 23) FUNCIÓN AUXILIAR PARA FIGURAS DESCRIPTIVAS
# ============================================================
resumir_para_figura <- function(data, variable_valor){
data %>%
filter(!is.na(.data[[variable_valor]])) %>%
group_by(
velocidad,
grupo_competencia,
categoria_momento_pop_derivada
) %>%
summarise(
mediana = median(.data[[variable_valor]], na.rm = TRUE),
p25 = quantile(.data[[variable_valor]], 0.25, na.rm = TRUE),
p75 = quantile(.data[[variable_valor]], 0.75, na.rm = TRUE),
n_evaluaciones = n_distinct(paste(id, sesion)),
n_jugadores = n_distinct(id),
.groups = "drop"
)
}
# ============================================================
# 24) FIGURA 1 - PICO DE PAR EN EXTENSIÓN
# ============================================================
datos_figura_extension <- datos_analisis %>%
select(
id,
sesion,
velocidad,
grupo_competencia,
categoria_momento_pop_derivada,
pico_del_par_extension_implicado,
pico_del_par_extension_no_implicado
) %>%
pivot_longer(
cols = c(
pico_del_par_extension_implicado,
pico_del_par_extension_no_implicado
),
names_to = "miembro",
values_to = "valor"
) %>%
mutate(
miembro = case_when(
miembro == "pico_del_par_extension_implicado" ~ "Implicado",
miembro == "pico_del_par_extension_no_implicado" ~ "No implicado",
TRUE ~ miembro
),
miembro = factor(
miembro,
levels = c("No implicado", "Implicado")
),
velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
) %>%
filter(!is.na(valor)) %>%
group_by(
miembro,
velocidad_etiqueta,
grupo_competencia,
categoria_momento_pop_derivada
) %>%
summarise(
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
.groups = "drop"
)
figura_extension <- ggplot(
datos_figura_extension,
aes(
x = categoria_momento_pop_derivada,
y = mediana,
color = grupo_competencia
)
) +
geom_errorbar(
aes(
ymin = p25,
ymax = p75
),
width = 0.18,
position = position_dodge(width = 0.55),
linewidth = 0.7
) +
geom_point(
position = position_dodge(width = 0.55),
size = 2.7
) +
facet_grid(
miembro ~ velocidad_etiqueta
) +
labs(
title = "Pico de par en extensión según grupo competitivo y momento postoperatorio",
subtitle = "Puntos: mediana. Barras verticales: P25–P75.",
x = "Momento postoperatorio",
y = "Pico de par en extensión",
color = "Grupo competitivo"
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
strip.text = element_text(face = "bold")
)
figura_extension
# ============================================================
# 25) FIGURA 2 - PICO DE PAR EN FLEXIÓN
# ============================================================
datos_figura_flexion <- datos_analisis %>%
select(
id,
sesion,
velocidad,
grupo_competencia,
categoria_momento_pop_derivada,
pico_del_par_flexion_implicado,
pico_del_par_flexion_no_implicado
) %>%
pivot_longer(
cols = c(
pico_del_par_flexion_implicado,
pico_del_par_flexion_no_implicado
),
names_to = "miembro",
values_to = "valor"
) %>%
mutate(
miembro = case_when(
miembro == "pico_del_par_flexion_implicado" ~ "Implicado",
miembro == "pico_del_par_flexion_no_implicado" ~ "No implicado",
TRUE ~ miembro
),
miembro = factor(
miembro,
levels = c("No implicado", "Implicado")
),
velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
) %>%
filter(!is.na(valor)) %>%
group_by(
miembro,
velocidad_etiqueta,
grupo_competencia,
categoria_momento_pop_derivada
) %>%
summarise(
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
.groups = "drop"
)
figura_flexion <- ggplot(
datos_figura_flexion,
aes(
x = categoria_momento_pop_derivada,
y = mediana,
color = grupo_competencia
)
) +
geom_errorbar(
aes(
ymin = p25,
ymax = p75
),
width = 0.18,
position = position_dodge(width = 0.55),
linewidth = 0.7
) +
geom_point(
position = position_dodge(width = 0.55),
size = 2.7
) +
facet_grid(
miembro ~ velocidad_etiqueta
) +
labs(
title = "Pico de par en flexión según grupo competitivo y momento postoperatorio",
subtitle = "Puntos: mediana. Barras verticales: P25–P75.",
x = "Momento postoperatorio",
y = "Pico de par en flexión",
color = "Grupo competitivo"
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
strip.text = element_text(face = "bold")
)
figura_flexion
# ============================================================
# 26) FIGURA 3 - SIMETRÍA DEL PICO DE PAR
# ============================================================
datos_figura_simetria <- datos_analisis %>%
select(
id,
sesion,
velocidad,
grupo_competencia,
categoria_momento_pop_derivada,
simetria_pico_par_extension,
simetria_pico_par_flexion
) %>%
pivot_longer(
cols = c(
simetria_pico_par_extension,
simetria_pico_par_flexion
),
names_to = "movimiento",
values_to = "valor"
) %>%
mutate(
movimiento = case_when(
movimiento == "simetria_pico_par_extension" ~ "Extensión",
movimiento == "simetria_pico_par_flexion" ~ "Flexión",
TRUE ~ movimiento
),
movimiento = factor(
movimiento,
levels = c("Extensión", "Flexión")
),
velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
) %>%
filter(!is.na(valor)) %>%
group_by(
movimiento,
velocidad_etiqueta,
grupo_competencia,
categoria_momento_pop_derivada
) %>%
summarise(
mediana = median(valor, na.rm = TRUE),
p25 = quantile(valor, 0.25, na.rm = TRUE),
p75 = quantile(valor, 0.75, na.rm = TRUE),
.groups = "drop"
)
figura_simetria <- ggplot(
datos_figura_simetria,
aes(
x = categoria_momento_pop_derivada,
y = mediana,
color = grupo_competencia
)
) +
geom_hline(
yintercept = 100,
linetype = "dashed",
color = "grey50"
) +
geom_errorbar(
aes(
ymin = p25,
ymax = p75
),
width = 0.18,
position = position_dodge(width = 0.55),
linewidth = 0.7
) +
geom_point(
position = position_dodge(width = 0.55),
size = 2.7
) +
facet_grid(
movimiento ~ velocidad_etiqueta
) +
labs(
title = "Simetría del pico de par según grupo competitivo y momento postoperatorio",
subtitle = "Puntos: mediana. Barras verticales: P25–P75. Línea discontinua: 100% de simetría.",
x = "Momento postoperatorio",
y = "Simetría del pico de par (%)",
color = "Grupo competitivo"
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
strip.text = element_text(face = "bold")
)
figura_simetria
# ============================================================
# 27) GUARDAR FIGURAS
# ============================================================
carpeta_figuras <- file.path(
getwd(),
"figuras_isocinesia_RLCA"
)
dir.create(
carpeta_figuras,
showWarnings = FALSE
)
ggsave(
filename = file.path(
carpeta_figuras,
"Figura_1_pico_par_extension.png"
),
plot = figura_extension,
width = 13,
height = 7.5,
dpi = 300
)
ggsave(
filename = file.path(
carpeta_figuras,
"Figura_2_pico_par_flexion.png"
),
plot = figura_flexion,
width = 13,
height = 7.5,
dpi = 300
)
ggsave(
filename = file.path(
carpeta_figuras,
"Figura_3_simetria_pico_par.png"
),
plot = figura_simetria,
width = 13,
height = 7.5,
dpi = 300
)
carpeta_figuras
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/figuras_isocinesia_RLCA"
El análisis permitió construir una cohorte analítica depurada de 46 jugadores y 70 evaluaciones clínicas únicas, excluyendo jugadores con relesión, evaluaciones sin momento postoperatorio y evaluaciones realizadas más allá de los 18 meses posteriores a la cirugía.
La Tabla 1 resume las características generales de la cohorte según nivel competitivo. Las variables numéricas se presentan en formato amplio, incluyendo media, desvío estándar, mediana, P25–P75, IQR, mínimo y máximo, con el objetivo de definir posteriormente el formato de reporte más adecuado para la versión final del manuscrito. Las variables categóricas se presentan mediante frecuencias absolutas y relativas calculadas sobre los datos disponibles.
La Tabla 2 describe los resultados de fuerza isocinética según grupo competitivo y velocidad de evaluación. Para todas las variables de fuerza se informan medidas de tendencia central y dispersión tanto paramétricas como robustas, evitando imponer una única forma de resumen sobre un conjunto amplio y heterogéneo de métricas.
Las figuras descriptivas propuestas permiten visualizar el comportamiento del pico de par en extensión, del pico de par en flexión y de las simetrías correspondientes según nivel competitivo y momento postoperatorio. Se eligió una representación basada en medianas y rangos intercuartílicos para ofrecer una lectura más estable frente a posibles valores extremos.
# ============================================================
# 28) FIGURAS FINALES TIPO BOXPLOT + PUNTOS
# ============================================================
# 4 figuras principales:
# 1) Pico del par en extensión - implicado
# 2) Pico del par en flexión - implicado
# 3) Simetría del pico del par en extensión
# 4) Simetría del pico del par en flexión
#
# Cada figura se divide en 3 subpaneles:
# a) 60°/s
# b) 180°/s
# c) 300°/s
#
# Eje X: momento postoperatorio
# Eje Y: variable numérica
# Color/relleno: grupo competitivo
# Puntos: evaluaciones individuales
# ============================================================
# ============================================================
# 28.1) Crear carpeta para guardar las figuras finales
# ============================================================
carpeta_figuras_boxplot <- file.path(
getwd(),
"figuras_isocinesia_RLCA",
"boxplots_finales"
)
dir.create(
carpeta_figuras_boxplot,
recursive = TRUE,
showWarnings = FALSE
)
# ============================================================
# 28.2) Función para preparar datos
# ============================================================
preparar_datos_boxplot <- function(
data,
variable_valor
){
data %>%
select(
id,
sesion,
velocidad,
grupo_competencia,
categoria_momento_pop_derivada,
all_of(variable_valor)
) %>%
rename(
valor = all_of(variable_valor)
) %>%
filter(
!is.na(valor),
!is.na(velocidad),
!is.na(grupo_competencia),
!is.na(categoria_momento_pop_derivada)
) %>%
mutate(
velocidad_num = readr::parse_number(as.character(velocidad)),
velocidad_etiqueta = case_when(
velocidad_num == 60 ~ "a) 60°/s",
velocidad_num == 180 ~ "b) 180°/s",
velocidad_num == 300 ~ "c) 300°/s",
TRUE ~ NA_character_
),
velocidad_etiqueta = factor(
velocidad_etiqueta,
levels = c("a) 60°/s", "b) 180°/s", "c) 300°/s")
),
categoria_momento_pop_derivada = factor(
categoria_momento_pop_derivada,
levels = c("<6 meses", "6 a <8 meses", "≥8 meses")
),
grupo_competencia = factor(
grupo_competencia,
levels = c("Primera", "Segunda", "Tercera + Amateur")
)
) %>%
filter(!is.na(velocidad_etiqueta))
}
# ============================================================
# 28.3) Función para tabla de n por celda
# ============================================================
crear_tabla_n_figura <- function(
data_plot,
nombre_figura
){
data_plot %>%
group_by(
velocidad_etiqueta,
categoria_momento_pop_derivada,
grupo_competencia
) %>%
summarise(
n_evaluaciones = n_distinct(paste(id, sesion)),
n_jugadores = n_distinct(id),
.groups = "drop"
) %>%
mutate(
figura = nombre_figura,
bajo_n = case_when(
n_evaluaciones < 5 ~ "Sí",
TRUE ~ "No"
)
) %>%
relocate(
figura,
velocidad_etiqueta,
categoria_momento_pop_derivada,
grupo_competencia
)
}
# ============================================================
# 28.4) Función para posición de etiquetas n
# ============================================================
crear_posicion_n <- function(data_plot){
rango_por_velocidad <- data_plot %>%
group_by(velocidad_etiqueta) %>%
summarise(
y_min = min(valor, na.rm = TRUE),
y_max = max(valor, na.rm = TRUE),
rango = y_max - y_min,
.groups = "drop"
) %>%
mutate(
y_n = y_max + 0.08 * rango
)
data_plot %>%
group_by(
velocidad_etiqueta,
categoria_momento_pop_derivada,
grupo_competencia
) %>%
summarise(
n_evaluaciones = n_distinct(paste(id, sesion)),
n_jugadores = n_distinct(id),
.groups = "drop"
) %>%
left_join(
rango_por_velocidad,
by = "velocidad_etiqueta"
) %>%
mutate(
etiqueta_n = paste0("n=", n_evaluaciones)
)
}
# ============================================================
# 28.5) Función para crear boxplot final
# ============================================================
crear_boxplot_final <- function(
data_plot,
titulo,
subtitulo,
etiqueta_y,
agregar_linea_100 = FALSE,
mostrar_n = FALSE
){
datos_n <- crear_posicion_n(data_plot)
grafico <- ggplot(
data_plot,
aes(
x = categoria_momento_pop_derivada,
y = valor,
fill = grupo_competencia,
color = grupo_competencia
)
)
if(agregar_linea_100){
grafico <- grafico +
geom_hline(
yintercept = 100,
linetype = "dashed",
color = "grey35",
linewidth = 0.5
)
}
grafico <- grafico +
geom_boxplot(
width = 0.58,
alpha = 0.30,
outlier.shape = NA,
linewidth = 0.50,
position = position_dodge(width = 0.75)
) +
geom_point(
position = position_jitterdodge(
jitter.width = 0.10,
jitter.height = 0,
dodge.width = 0.75,
seed = 123
),
size = 1.7,
alpha = 0.65,
stroke = 0.20
)
if(mostrar_n){
grafico <- grafico +
geom_text(
data = datos_n,
aes(
x = categoria_momento_pop_derivada,
y = y_n,
label = etiqueta_n,
color = grupo_competencia,
group = grupo_competencia
),
position = position_dodge(width = 0.75),
inherit.aes = FALSE,
size = 3.0,
show.legend = FALSE
)
}
grafico +
facet_wrap(
~ velocidad_etiqueta,
nrow = 1
) +
scale_y_continuous(
expand = expansion(mult = c(0.04, 0.15))
) +
labs(
title = titulo,
subtitle = subtitulo,
x = "Momento postoperatorio",
y = etiqueta_y,
fill = "Grupo competitivo",
color = "Grupo competitivo",
caption = "Cada punto representa una evaluación clínica única. Un mismo jugador pudo aportar más de una evaluación si fue medido en distintos momentos postoperatorios. Las celdas con bajo número de evaluaciones deben interpretarse con cautela."
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
strip.text = element_text(face = "bold"),
axis.text.x = element_text(angle = 20, hjust = 1),
plot.title = element_text(face = "bold"),
plot.caption = element_text(
hjust = 0,
size = 8,
color = "grey30"
)
)
}
# ============================================================
# 28.6) FIGURA 1
# Pico del par en extensión - implicado
# ============================================================
datos_figura_1 <- preparar_datos_boxplot(
data = datos_analisis,
variable_valor = "pico_del_par_extension_implicado"
)
figura_1_boxplot <- crear_boxplot_final(
data_plot = datos_figura_1,
titulo = "Figura 1. Pico del par en extensión del miembro implicado",
subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo.",
etiqueta_y = "Pico del par en extensión (N·m)",
agregar_linea_100 = FALSE,
mostrar_n = FALSE
)
figura_1_boxplot
# ============================================================
# 28.7) FIGURA 2
# Pico del par en flexión - implicado
# ============================================================
datos_figura_2 <- preparar_datos_boxplot(
data = datos_analisis,
variable_valor = "pico_del_par_flexion_implicado"
)
figura_2_boxplot <- crear_boxplot_final(
data_plot = datos_figura_2,
titulo = "Figura 2. Pico del par en flexión del miembro implicado",
subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo.",
etiqueta_y = "Pico del par en flexión (N·m)",
agregar_linea_100 = FALSE,
mostrar_n = FALSE
)
figura_2_boxplot
# ============================================================
# 28.8) FIGURA 3
# Simetría del pico del par en extensión
# ============================================================
datos_figura_3 <- preparar_datos_boxplot(
data = datos_analisis,
variable_valor = "simetria_pico_par_extension"
)
figura_3_boxplot <- crear_boxplot_final(
data_plot = datos_figura_3,
titulo = "Figura 3. Simetría del pico del par en extensión",
subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo. La línea discontinua indica 100% de simetría.",
etiqueta_y = "Simetría del pico del par en extensión (%)",
agregar_linea_100 = TRUE,
mostrar_n = FALSE
)
figura_3_boxplot
# ============================================================
# 28.9) FIGURA 4
# Simetría del pico del par en flexión
# ============================================================
datos_figura_4 <- preparar_datos_boxplot(
data = datos_analisis,
variable_valor = "simetria_pico_par_flexion"
)
figura_4_boxplot <- crear_boxplot_final(
data_plot = datos_figura_4,
titulo = "Figura 4. Simetría del pico del par en flexión",
subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo. La línea discontinua indica 100% de simetría.",
etiqueta_y = "Simetría del pico del par en flexión (%)",
agregar_linea_100 = TRUE,
mostrar_n = FALSE
)
figura_4_boxplot
# ============================================================
# 28.10) Tabla de n por figura y estrato
# ============================================================
tabla_n_figuras <- bind_rows(
crear_tabla_n_figura(
datos_figura_1,
"Figura 1. Pico del par en extensión del miembro implicado"
),
crear_tabla_n_figura(
datos_figura_2,
"Figura 2. Pico del par en flexión del miembro implicado"
),
crear_tabla_n_figura(
datos_figura_3,
"Figura 3. Simetría del pico del par en extensión"
),
crear_tabla_n_figura(
datos_figura_4,
"Figura 4. Simetría del pico del par en flexión"
)
) %>%
arrange(
figura,
velocidad_etiqueta,
categoria_momento_pop_derivada,
grupo_competencia
)
tabla_n_figuras
# Resumen de celdas con bajo n
tabla_n_figuras %>%
count(
figura,
bajo_n,
name = "n_celdas"
)
# ============================================================
# 28.11) Guardar figuras y tabla de n
# ============================================================
ggsave(
filename = file.path(
carpeta_figuras_boxplot,
"Figura_1_pico_par_extension_implicado_boxplot.png"
),
plot = figura_1_boxplot,
width = 15,
height = 8,
dpi = 300
)
ggsave(
filename = file.path(
carpeta_figuras_boxplot,
"Figura_2_pico_par_flexion_implicado_boxplot.png"
),
plot = figura_2_boxplot,
width = 15,
height = 8,
dpi = 300
)
ggsave(
filename = file.path(
carpeta_figuras_boxplot,
"Figura_3_simetria_pico_par_extension_boxplot.png"
),
plot = figura_3_boxplot,
width = 15,
height = 8,
dpi = 300
)
ggsave(
filename = file.path(
carpeta_figuras_boxplot,
"Figura_4_simetria_pico_par_flexion_boxplot.png"
),
plot = figura_4_boxplot,
width = 15,
height = 8,
dpi = 300
)
# Guardar tabla de n por estrato
if(!requireNamespace("writexl", quietly = TRUE)){
install.packages("writexl")
}
writexl::write_xlsx(
tabla_n_figuras,
path = file.path(
carpeta_figuras_boxplot,
"Tabla_n_por_figura_y_estrato.xlsx"
)
)
carpeta_figuras_boxplot
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/figuras_isocinesia_RLCA/boxplots_finales"