El conjunto de datos trata con información asociada a los jugadores bateadores de Major League Baseball (MLB) con el propósito de construir y evaluar modelos de aprendizaje automático orientados a la predicción del porcentaje de bateo.
El dataset integra variables demográficas, antropométricas, deportivas y biomecánicas que pueden influir en el desempeño ofensivo de un jugador profesional de béisbol. La información simula registros históricos correspondientes a múltiples jugadores y temporadas, permitiendo aplicar técnicas de de regresión como arboles de regresión, bosques aleatorios y regresión lineal múltiple.
Las variables independientes:
Variable dependiente: * pct_bateo: Porcentaje de bateo del jugador
Construir, evaluar y comparar modelos de de regresión árboles de regresión, bosques aleatorios y regresión lineal múltiple con datos históricos relacionados con el porcentaje de bateo de jugadores de beisbol.
Los modelos serán aceptados si tienen valores de r square y r square ajustado por encima del 50%
Los datos originales no se escalan ni se estandarizan, de tal forma que los modelos utilizan los datos originales
Las particiones serán 70% para datos de entrenamiento y 30% para datos de validación.
Al final el caso de estudio deberá indicar cual es el mejor modelo predictivo con estos datos.
El caso de estudio se puede encontrar en el en la dirección url rpubs.com https://rpubs.com/rpizarrog/1436170
Los datos se pueden encontrar en el espacio del autor github.com https://raw.githubusercontent.com/rpizarrog/Libro-Aprendizaje-Automatico.-Casos-de-Estudio-con-R-y-Python/refs/heads/main/datos/dataset_mlb_porcentaje_de_bateo.csv
Las funciones se pueden encontrar y reutilizar desde el espacio github.com en https://raw.githubusercontent.com/rpizarrog/Libro-Aprendizaje-Automatico.-Casos-de-Estudio-con-R-y-Python/refs/heads/main/R%20MarkDown/funciones/funciones%20para%20modelos%20de%20regresion%20ARBOLES%20%20BOSQUES%20ALEATORIOS%20LINEAL%20MULTIPLE%20de%20porcentaje%20bateo%20jugadores%20MLB.R
# install.packages("readr")
# install.packages("tidyverse")
# install.packages("psych")
# install.packages("dplyr")
# install.packages("ggplot2")
# install.packages("caret")
# install.packages("broom")
# install.packages("lmtest")
# install.packages("car")
# install.packages("stats")
# install.packages("flextable")
# install.packages("officer")
# install.packages("patchwork")
# install.packages("performance")
# install.packages("see")
# install.packages("car")
# install.packages("nortest")
# install.packages("lmtest")
# install,packages("e1071")
# install.packages("rpart")
# install.packages("randomForest")
library(readr) # cargar datos datos
library(tidyverse) # Para manipular
library (psych) # Para descriobir datos
library(dplyr) # Manipulación de datos
library(ggplot2) # gráficos
library(caret) # partición de datos
library(broom) # tidy modelos
library(lmtest) # Durbin-Watson
library(car) # VIF y diagnóstico, entre otras
library(stats) # lm, shapiro.test
library(patchwork) # Graficos organizados en columnas renglones
# Tablas compatibles con Word
library(flextable)
library(officer)
library(performance) # Para evaluar postulados de modelos
library(see) # Para evaluar postulados de modelos dependencia de performance
library(car) # Para verificar postulados de los modelos
library(nortest) # Para pruebas de normalidad Anderson-Darling
library(lmtest) # Para pruebas de homocedasticidad Breusch–Pagan y prueba de White y otras pruebas
library(glmnet) # Para modelos Lasso y Ridge
# library(e1071) # Para modelos SVR varios kernels
library(rpart) #arboles de regresión
library(randomForest) # randomForest
library(rpart.plot) # Visualiazar arboles de regresión
# url <- "../funciones/funciones para modelos de regresion ARBOLES BOSQUES ALEATORIOS LINEAL MULTIPLE de porcentaje bateo jugadores MLB.R" # local
url <- "https://raw.githubusercontent.com/rpizarrog/Libro-Aprendizaje-Automatico.-Casos-de-Estudio-con-R-y-Python/refs/heads/main/R%20MarkDown/funciones/funciones%20para%20modelos%20de%20regresion%20ARBOLES%20%20BOSQUES%20ALEATORIOS%20LINEAL%20MULTIPLE%20de%20porcentaje%20bateo%20jugadores%20MLB.R" # WEB
source(url)
# url <- "../datos/dataset_mlb_porcentaje_de_bateo.csv" # local
url <- "https://raw.githubusercontent.com/rpizarrog/Libro-Aprendizaje-Automatico.-Casos-de-Estudio-con-R-y-Python/refs/heads/main/datos/dataset_mlb_porcentaje_de_bateo.csv" # WEB
datos <- f_cargar_datos(url)
Se presentan los primeros y últimos registros con las primeras y últimas cuatro variables.
f_visualizar_head_tail_reducido_word(datos)
edad | experiencia_mlb | estatura_cm | peso_kg | ... | vision_dinamica | agilidad | tiempo_reaccion | pct_bateo |
|---|---|---|---|---|---|---|---|---|
39 | 21 | 176.8 | 76.4 | ... | 77.3 | 72.7 | 0.253 | 0.297 |
39 | 18 | 178.1 | 95.5 | ... | 81.3 | 75.7 | 0.25 | 0.283 |
28 | 6 | 170.5 | 97.3 | ... | 77.1 | 74.9 | 0.275 | 0.303 |
25 | 6 | 174.2 | 88.5 | ... | 81.7 | 72.1 | 0.286 | 0.289 |
28 | 8 | 183 | 89 | ... | 74.8 | 71 | 0.231 | 0.269 |
39 | 19 | 191.4 | 110 | ... | 80.2 | 73.9 | 0.258 | 0.285 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
31 | 10 | 168.5 | 80.4 | ... | 83 | 71.8 | 0.262 | 0.293 |
32 | 11 | 183.2 | 80.7 | ... | 83.7 | 73.4 | 0.279 | 0.31 |
33 | 13 | 181.4 | 83.6 | ... | 81.2 | 81.7 | 0.251 | 0.312 |
27 | 4 | 179 | 84.3 | ... | 82.4 | 71.4 | 0.286 | 0.287 |
34 | 15 | 190 | 78.3 | ... | 80.6 | 70 | 0.268 | 0.303 |
36 | 17 | 180.3 | 87.5 | ... | 76.4 | 72.2 | 0.282 | 0.294 |
f_describir_datos(datos)
## $describe
## vars n mean sd median trimmed mad min max
## edad 1 4500 29.49 5.81 29.00 29.48 7.41 20.00 39.00
## experiencia_mlb 2 4500 9.06 5.92 9.00 8.97 7.41 0.00 21.00
## estatura_cm 3 4500 184.95 6.99 184.90 184.95 6.82 162.00 211.90
## peso_kg 4 4500 88.22 10.04 88.20 88.23 10.08 53.60 129.50
## velocidad_swing 5 4500 111.93 6.95 111.90 111.88 7.12 86.50 140.30
## fuerza 6 4500 81.96 5.92 81.90 81.96 5.93 62.10 102.80
## precision_contacto 7 4500 80.13 5.04 80.10 80.13 5.04 61.40 97.90
## vision_dinamica 8 4500 79.06 3.98 79.00 79.05 4.00 64.30 92.90
## agilidad 9 4500 74.89 6.02 75.00 74.90 5.78 53.50 98.20
## tiempo_reaccion 10 4500 0.27 0.02 0.27 0.27 0.01 0.21 0.32
## pct_bateo 11 4500 0.28 0.02 0.28 0.28 0.02 0.22 0.35
## range skew kurtosis se
## edad 19.00 0.02 -1.21 0.09
## experiencia_mlb 21.00 0.09 -1.13 0.09
## estatura_cm 49.90 0.02 0.11 0.10
## peso_kg 75.90 0.01 0.10 0.15
## velocidad_swing 53.80 0.07 0.00 0.10
## fuerza 40.70 -0.01 -0.03 0.09
## precision_contacto 36.50 -0.02 -0.08 0.08
## vision_dinamica 28.60 0.02 -0.01 0.06
## agilidad 44.70 0.00 0.14 0.09
## tiempo_reaccion 0.11 0.05 -0.01 0.00
## pct_bateo 0.12 0.00 0.01 0.00
##
## $structure
## [1] "'data.frame':\t4500 obs. of 11 variables:\n $ edad : num 39 39 28 25 28 39 38 23 27 37 ...\n $ experiencia_mlb : num 21 18 6 6 8 19 16 4 5 14 ...\n $ estatura_cm : num 177 178 170 174 183 ...\n $ peso_kg : num 76.4 95.5 97.3 88.5 89 110 77.6 88.4 79.3 76.7 ...\n $ velocidad_swing : num 105 114 125 113 106 ...\n $ fuerza : num 86.8 95.7 89.1 88.3 87.6 76.7 88.9 83.6 99.5 81.2 ...\n $ precision_contacto: num 87 74.4 80.9 81.9 81.7 83.1 76.4 82 72.6 88.2 ...\n $ vision_dinamica : num 77.3 81.3 77.1 81.7 74.8 80.2 72.8 79.1 77.9 72.5 ...\n $ agilidad : num 72.7 75.7 74.9 72.1 71 73.9 73.4 82.9 73.3 74.2 ...\n $ tiempo_reaccion : num 0.253 0.25 0.275 0.286 0.231 0.258 0.256 0.286 0.268 0.268 ...\n $ pct_bateo : num 0.297 0.283 0.303 0.289 0.269 0.285 0.278 0.281 0.272 0.296 ..."
A partir de los datos originales, crear datos de entrenamiento y validación: 70% datos de entrenamiento y 30% datos de validación; se ejecuta la función f_particionar_datos().
particiones <- f_particionar_datos(datos)
datos_entrenamiento <- particiones$datos_entrenamiento
datos_validacion <- particiones$datos_validacion
f_visualizar_head_tail_reducido_word(datos_entrenamiento)
edad | experiencia_mlb | estatura_cm | peso_kg | ... | vision_dinamica | agilidad | tiempo_reaccion | pct_bateo |
|---|---|---|---|---|---|---|---|---|
36 | 14 | 176.4 | 95.3 | ... | 77.4 | 74.1 | 0.249 | 0.28 |
38 | 17 | 180.5 | 66.4 | ... | 72.5 | 75.4 | 0.276 | 0.269 |
34 | 16 | 180 | 76.4 | ... | 84.4 | 77.7 | 0.269 | 0.275 |
27 | 6 | 188.8 | 100.3 | ... | 75.1 | 76 | 0.297 | 0.265 |
24 | 6 | 200.9 | 84.4 | ... | 78.4 | 67.7 | 0.275 | 0.283 |
25 | 4 | 176.8 | 84.3 | ... | 80.3 | 73.9 | 0.271 | 0.274 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
26 | 8 | 193.1 | 88 | ... | 78.6 | 67.5 | 0.287 | 0.277 |
25 | 4 | 171 | 95.5 | ... | 81.7 | 73.9 | 0.29 | 0.271 |
39 | 19 | 186.1 | 84.5 | ... | 78.4 | 75.9 | 0.248 | 0.28 |
33 | 10 | 187.2 | 92.7 | ... | 78.3 | 80.4 | 0.296 | 0.276 |
20 | 0 | 183.4 | 94.9 | ... | 84.9 | 73.2 | 0.259 | 0.276 |
38 | 15 | 170.1 | 85.6 | ... | 74.3 | 75.3 | 0.27 | 0.268 |
f_visualizar_head_tail_reducido_word(datos_validacion)
edad | experiencia_mlb | estatura_cm | peso_kg | ... | vision_dinamica | agilidad | tiempo_reaccion | pct_bateo |
|---|---|---|---|---|---|---|---|---|
39 | 21 | 176.8 | 76.4 | ... | 77.3 | 72.7 | 0.253 | 0.297 |
26 | 6 | 177.5 | 91.2 | ... | 75.5 | 74.3 | 0.256 | 0.29 |
28 | 9 | 190.6 | 81.6 | ... | 81.2 | 81.8 | 0.28 | 0.305 |
36 | 13 | 180.7 | 81 | ... | 82.8 | 70.2 | 0.267 | 0.267 |
35 | 17 | 179.5 | 89.9 | ... | 79.3 | 76.8 | 0.264 | 0.282 |
35 | 15 | 181.1 | 72.6 | ... | 81.4 | 73.3 | 0.272 | 0.288 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
30 | 12 | 183.8 | 73.8 | ... | 84.6 | 70.3 | 0.269 | 0.256 |
35 | 17 | 188.5 | 79.6 | ... | 78.9 | 72.4 | 0.252 | 0.287 |
29 | 10 | 182.4 | 109.6 | ... | 80.3 | 66.4 | 0.283 | 0.241 |
21 | 0 | 191 | 86.5 | ... | 84.3 | 80 | 0.278 | 0.284 |
33 | 13 | 186.2 | 92 | ... | 76.5 | 82.4 | 0.261 | 0.294 |
32 | 11 | 183.2 | 80.7 | ... | 83.7 | 73.4 | 0.279 | 0.31 |
Se crea el modelo árboles de regresión con el conjunto de datos de entrenamiento con la función f_crear_modelo_AR() que utiliza en su encapsulamiento, el paquete rpart y la función del mismo nombre rpart(); recibe como argumentos, los datos de entrenamiento, la variable dependiente y se construye modelo. Se observan en modo consola, las reglas de decisión del modelo.
modelo_AR <- f_crear_modelo_AR(datos_entrenamiento, "pct_bateo")
modelo_AR
## n= 3150
##
## node), split, n, deviance, yval
## * denotes terminal node
##
## 1) root 3150 1.09619700 0.2828730
## 2) precision_contacto< 81.75 1978 0.51485220 0.2755723
## 4) velocidad_swing< 111.95 997 0.20572360 0.2678435
## 8) precision_contacto< 76.05 326 0.05381535 0.2574785 *
## 9) precision_contacto>=76.05 671 0.09986922 0.2728793
## 18) vision_dinamica< 78.75 310 0.03932665 0.2673129 *
## 19) vision_dinamica>=78.75 361 0.04268909 0.2776593 *
## 5) velocidad_swing>=111.95 981 0.18904800 0.2834271
## 10) precision_contacto< 76.25 355 0.05943551 0.2748648
## 20) precision_contacto< 71.55 70 0.01074979 0.2632143 *
## 21) precision_contacto>=71.55 285 0.03685065 0.2777263 *
## 11) precision_contacto>=76.25 626 0.08882695 0.2882827
## 22) vision_dinamica< 79.25 315 0.03730452 0.2834730 *
## 23) vision_dinamica>=79.25 311 0.03685459 0.2931543 *
## 3) precision_contacto>=81.75 1172 0.29798360 0.2951945
## 6) velocidad_swing< 111.65 557 0.10349720 0.2863411
## 12) precision_contacto< 85.95 381 0.05790196 0.2823438 *
## 13) precision_contacto>=85.95 176 0.02632899 0.2949943 *
## 7) velocidad_swing>=111.65 615 0.11128510 0.3032130
## 14) precision_contacto< 86.75 447 0.06139604 0.2993870
## 28) velocidad_swing< 118.85 312 0.03265646 0.2960417 *
## 29) velocidad_swing>=118.85 135 0.01717810 0.3071185 *
## 15) precision_contacto>=86.75 168 0.02593607 0.3133929 *
Se extraen las variables del modelo con modelo_AR$variable.importance; los resultados de importancia de variables muestran que precision_contacto constituye el predictor más relevante del modelo, seguido por velocidad_swing.
Las características relacionadas con la capacidad técnica y biomecánica del bateador representan los principales atributos asociados al porcentaje de bateo. En contraste, variables demográficas como edad y experiencia presentan una contribución predictiva considerablemente menor dentro del árbol de regresión construido.
variables_importantes <- data.frame(
variables = names(modelo_AR$variable.importance),
importancia = as.numeric(modelo_AR$variable.importance)
)
variables_importantes
f_visualizar_AR(modelo_AR)
modelo_RF <- f_crear_modelo_RF(datos_entrenamiento, "pct_bateo", arboles=100)
modelo_RF
##
## Call:
## randomForest(formula = formula_modelo, data = datos, ntree = arboles, importance = TRUE)
## Type of random forest: regression
## Number of trees: 100
## No. of variables tried at each split: 3
##
## Mean of squared residuals: 6.087093e-05
## % Var explained: 82.51
Las cuestiones técnicas de un jugador son las de mayor relevancia para obtener mejores porcentajes de bateo en un jugador de beisbol; precision_contacto \(\approx 92.08\), velocidad_swing \(\approx 75.35\) y vision_dinamica \(\approx 42.180\)
# VARIABLES IMPORTANTES
# RANDOM FOREST
variables_importantes <- data.frame(
variables = rownames(importance(modelo_RF)),
importancia = importance(modelo_RF)[,1]
) %>%
arrange(desc(importancia))
variables_importantes
Dada la naturaleza de que los modelos Random Forest están compuestos por múltiples árboles generados aleatoriamente, la visualización completa del bosque resulta compleja. Por ello, de acuerdo a inteligencia artificial generativa, en la práctica suele recurrirse a la interpretación mediante importancia de variables más que a la representación gráfica de todos los árboles individuales.
Se construye el modelo de regresión lineal múltiple mediante la función f_construir_RLM(). La salida presentada en consola a partir del resumen estadístico del modelo indica que las variables independientes velocidad_swing, fuerza, precision_contacto, vision_dinamica, agilidad y tiempo_reaccion presentan significancia estadística al 99.9%. Estos resultados son consistentes con los obtenidos en los modelos de árboles de regresión y bosques aleatorios, donde las variables asociadas a características técnicas y biomecánicas del jugador de béisbol muestran los mayores niveles de importancia predictiva que impactan en el el porcentaje de bateo.
modelo_RLM <- f_construir_modelo_RLM(datos_entrenamiento, "pct_bateo")
##
## ============================
## Modelo de Regresión Lineal Múltiple
## ============================
## Variable dependiente: pct_bateo
## Número de observaciones: 3150
## Número de variables independientes: 10
##
## Call:
## lm(formula = formula_modelo, data = datos_modelo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.0188446 -0.0039979 -0.0000069 0.0040504 0.0194139
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.454e-01 5.386e-03 -45.573 <2e-16 ***
## edad 3.320e-06 6.592e-05 0.050 0.960
## experiencia_mlb -2.127e-05 6.474e-05 -0.329 0.742
## estatura_cm 2.007e-05 1.529e-05 1.312 0.190
## peso_kg -1.400e-05 1.066e-05 -1.314 0.189
## velocidad_swing 1.380e-03 1.524e-05 90.544 <2e-16 ***
## fuerza 7.065e-04 1.781e-05 39.679 <2e-16 ***
## precision_contacto 2.433e-03 2.120e-05 114.764 <2e-16 ***
## vision_dinamica 1.428e-03 2.679e-05 53.320 <2e-16 ***
## agilidad 6.924e-04 1.783e-05 38.839 <2e-16 ***
## tiempo_reaccion -1.710e-01 6.993e-03 -24.458 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.005972 on 3139 degrees of freedom
## Multiple R-squared: 0.8979, Adjusted R-squared: 0.8976
## F-statistic: 2760 on 10 and 3139 DF, p-value: < 2.2e-16
modelo_RLM
##
## Call:
## lm(formula = formula_modelo, data = datos_modelo)
##
## Coefficients:
## (Intercept) edad experiencia_mlb estatura_cm
## -2.454e-01 3.320e-06 -2.128e-05 2.007e-05
## peso_kg velocidad_swing fuerza precision_contacto
## -1.400e-05 1.380e-03 7.065e-04 2.433e-03
## vision_dinamica agilidad tiempo_reaccion
## 1.428e-03 6.924e-04 -1.710e-01
Se ejecuta la función f_validar_postulados_modelos() que valida los modelos de regresión construidos para este caso de estudio. Los resultados en modo consola indican que el modelo de regresión lineal múltiple es válido dado que cumple con los postulados de linealidad, homocedasticidad, normalidad e independencia de residuos, no así con el postulado de multicolinealidad, lo que sugiere tal vez construir modelos regularizados Ridge o Lasso o hasta modelos de soporte vectorial SVR para disminuir con ello el impacto de la multicolinealidad en las variables.
Por otra parte los postulados de regresión no aplican para los modelos de árboles de regresión ni para los modelos bosques aleatorios. CITA: Breiman, James, Hastie.
# LISTA DE MODELOS
modelos <- list(modelo_RLM, modelo_AR, modelo_RF)
datos_modelos <- list(datos_entrenamiento, datos_entrenamiento, datos_entrenamiento)
nombres_modelos <- c("Regresión Lineal Múltiple", "Árbol de Regresión","Random Forest")
# VALIDAR MODELOS
resultado_postulados <- f_validar_postulados_modelos(
modelos = modelos,
datos_list = datos_modelos,
variable_dependiente = "pct_bateo",
nombres = nombres_modelos)
##
## ============================
## Validación de Postulados
## ============================
## Modelo Tipo VIF_Max Linealidad Homocedasticidad
## 1 Regresión Lineal Múltiple lm 12.9523 Cumple Cumple
## 2 Árbol de Regresión arbol_regresion NA NA NA
## 3 Random Forest random_forest NA NA NA
## Normalidad Independencia
## 1 Cumple Cumple
## 2 NA NA
## 3 NA NA
resultado_postulados
Se ejecuta la función f_evaluacion_modelos() que evalúa y compara la calidad predictiva de los modelos, la salida en modo consola.
Aunque la regresión lineal múltiple presentó el mayor nivel de capacidad explicativa, los modelos basados en árboles ofrecen ventajas importantes en términos de interpretabilidad de reglas y manejo de relaciones no lineales.
resultado_evaluacion <- f_evaluacion(
modelos = list(modelo_RLM, modelo_AR, modelo_RF),
datos_validacion_list = list(datos_validacion, datos_validacion, datos_validacion),
variable_dependiente = "pct_bateo",
nombres = c("Lineal Múltiple", "Arboles de regresión", "Bosques aleatorios")
)
##
## ============================
## EVALUACIÓN DE MODELOS
## ============================
## Modelo Tipo Parametros R_square R_square_ajustado
## 1 Lineal Múltiple lm 10 0.8886 0.8878
## 2 Arboles de regresión arbol_regresion 12 0.5601 0.5561
## 3 Bosques aleatorios random_forest 100 0.8219 0.8076
## MSE RMSE MAE
## 1 0e+00 0.0061 0.0048
## 2 1e-04 0.0121 0.0095
## 3 1e-04 0.0077 0.0061
El caso cumple con el objetivo planteado al inicio, se crearon tres modelos de regresión basados en algoritmos árboles de regresión, bosques aleatorios y regresión lineal múltiple.
El contexto de los datos es de tipo deportivo específicamente predecir el porcentaje de bateo a partir de un historial de datos asociado a variables de tipo técnico, biomécánico y atributos relacionados con la condición física de un jugador de beisbol.
Se utilizaron datos originales sin estandarizar ni escalar haciendo las particiones del 70% para datos de entrenamiento y 30% para datos de validación.
El modelo de regresión lineal múltiple, cumple con los supuestos de linealidad, homocedasticidad, normalidad e independencia de residuos; al respecto los modelos de árboles de regresión y bosques aleatorios no se aplica los supuestos de de regresión. Con respecto al supuesto de multicolinealidad, se identifica que existen variables correlacionadas en el conjunto de datos, lo que sustentaría aplicar modelos tales como Lasso o Ridge e incluso modelos de soporte para regresión SVR con estos mismos datos.
Los modelos de árboles de regresión y bosques aleatorios detectan que las variable de tipo técnico y biomecánico tales como precision_contacto, velocidad_swing y vision_dinamica son características importantes para predecir el porcentaje de bateo de un jugador de beisbol.
Por otra parte, de los modelos construidos con estos datos, el modelo que tiene mayor calidad predictiva es el modelo de regresión lineal múltiple con un valor de r square y r square ajustado por encima del 88% con respecto al 82% del modelo bosques aleatorios.
Por otra parte el caso de estudio permite observar que se pueden aplicar distintos modelos de regresión para comparar y enriquecer interpretaciones y con ello llegar a conclusiones más robustas en el contexto de la predicción de los datos.
Los modelos cumplen con la expectativa de que la calidad predictiva específicamente el en crietori de r square y r square ajustado estuvieran por encima del 50%.