# Check if the dplyr package is installed; if not, install it
if(!require(dplyr)) install.packages("dplyr")
# Load the dplyr package
library(dplyr)
# Check if the statar package is installed; if not, install it
if(!require(caret)) install.packages("caret")
# Load the statar package
library(caret)
Caso de Estudio: Predicción de Retornos Anuales en Fondos de Renta Variable
FZ2022 Algoritmos y Análisis de Datos
1 Escenario:
Eres un gestor de fondos de renta variable con el objetivo de identificar qué empresas mejorarán su margen operativo (EBIT / Ventas) en el siguiente trimestre. Para ello, dispones de datos trimestrales financieros de varias empresas y deberás transformarlos en crecimientos porcentuales trimestrales para construir un modelo predictivo que ayude a anticipar el desempeño futuro de estas empresas.
2 Objetivo del Caso:
Desarrollar un modelo de Machine Learning para predecir si el margen operativo de una empresa crecerá porcentualmente o no, en términos trimestrales. Para esto, se requiere:
- A partir de los datos trimestrales, calcular el margen operativo para cada empresa.
- Crear la variable dependiente margen_operativo_p, que será igual a 1 si el crecimiento trimestral del margen operativo de la empresa es positivo y 0 en caso contrario.
- Calcular las variables independientes del modelo predictivo a partir de razones financieras.
- Construir un modelo de predicción y realizar una partición de datos en un conjunto de entrenamiento y prueba.
- Documentar detalladamente cada uno de los pasos e interpretar las salidas del modelo y sus resultados.
3 Instrucciones y Lineamientos
- Cálculo del Margen Operativo
- La base de datos contiene ventas y costos trimestrales, a partir de los cuales puedes calcular el margen operativo. El margen operativo se define como:
Margen\_Operativo = \frac{EBIT}{revenue}
donde:
EBIT = \text{revenue} - \text{cogs} - \text{sgae}
- El EBIT (Earnings Before Interest and Taxes) representa las ganancias antes de intereses e impuestos, calculado restando los costos operativos (cogs y sgae).
- Una vez calculado el margen operativo, es necesario estimar el cambio porcentual trimestral del margen operativo para capturar el crecimiento o disminución en comparación con el trimestre anterior. El cambio porcentual se calcula comparando el margen operativo actual con el margen del trimestre anterior:
Crecimiento\_Margen\_Operativo = \left( \frac{Margen\_Operativo_{t}}{Margen\_Operativo_{t-1}} - 1 \right)
donde:
- Margen_Operativo_{t} es el margen operativo del trimestre actual.
- Margen_Operativo_{t-1} es el margen operativo del trimestre anterior.
- La base de datos contiene ventas y costos trimestrales, a partir de los cuales puedes calcular el margen operativo. El margen operativo se define como:
Margen\_Operativo = \frac{EBIT}{revenue}
donde:
EBIT = \text{revenue} - \text{cogs} - \text{sgae}
- Creación de la Variable Dependiente: Margen Operativo Positivo
- Con el crecimiento porcentual del margen operativo calculado, se puede crear la variable dependiente margen_operativo_p, que será una variable binaria. Esta variable toma el valor de:
- 1 si el crecimiento del margen operativo es positivo, es decir, el margen operativo ha aumentado en términos trimestrales.
- 0 si el crecimiento del margen operativo es negativo o cero.
- Con el crecimiento porcentual del margen operativo calculado, se puede crear la variable dependiente margen_operativo_p, que será una variable binaria. Esta variable toma el valor de:
- Selección de Variables Independientes
- Crea dos variables independientes que ayuden a predecir si el crecimiento del margen operativo será positivo:
- Apalancamiento Financiero: \frac{longdebt}{totalassets} Relación entre la deuda a largo plazo y los activos totales de la empresa, lo cual nos indica el grado de apalancamiento.
- Crecimiento de Activos: El crecimiento trimestral de los activos totales, que mide el incremento o decremento de los activos de la empresa en comparación con el trimestre anterior. Se define como: Crecimiento\_de\_Activos = \left(\frac{totalassets_{t}}{totalassets_{t-1}}-1\right) donde:
- totalassets_{t} representa los activos totales del trimestre actual.
- totalassets_{t-1} representa los activos totales del trimestre anterior.
- Crea dos variables independientes que ayuden a predecir si el crecimiento del margen operativo será positivo:
- Construcción del Modelo de Machine Learning
- Usa un modelo de regresión logística con la variable dependiente margen_operativo_p y las dos variables independientes seleccionadas.
- División de Datos: Realiza una partición del 80/20 para entrenar y probar el modelo.
- Matriz de Confusión: Utiliza la matriz de confusión para evaluar la precisión del modelo y entender su capacidad predictiva.
- Interpretación de Resultados
- Interpreta los coeficientes del modelo logístico y discute qué variables resultaron ser más importantes o significativas.
- Reflexiona sobre cómo estas variables te ayudan a predecir si el crecimiento del margen operativo será positivo.
- Analiza la utilidad del modelo de Machine Learning a partir de la matriz de confusión, sus verdaderos positivos y negativos, así como sus falsos positivos y negativos.
4 Recursos Permitidos
- Puedes hacer uso de las soluciones de workshops anteriores y de tus notas personales para resolver el caso.
5 Reglas del Juego y Advertencias
- No está permitido el uso de inteligencia artificial generativa (ChatGPT, Gemini, etc.) para resolver el caso.
- Evita cometer FIAS (Faltas a la Integridad Académica):
- Citar o tomar trabajo de otros sin darle crédito correspondiente.
- Plagio o colaboración no permitida con otros compañeros.
- Tiempo asignado: 1 hora y 30 minutos.
- Elabora y documenta tu proceso de forma clara y estructurada, explicando cada decisión que tomes.
- No incluyas códigos que no sean necesarios. El objetivo no es hacer copy+paste de los workshops pasados.
6 Rúbrica de Evaluación
La rúbrica a continuación detalla los puntos asignados a cada sección clave del caso, con los porcentajes respectivos para cada criterio.
Criterio | Sobresaliente (100%) | Sólido (90%) | Básico (75%) | Incipiente (50%) | Porcentaje |
---|---|---|---|---|---|
1. Algoritmos y Manejo de Datos | Implementación correcta del manejo de datos con dplyr, cálculo preciso del retorno anual, creación precisa de la variable dependiente y filtrado adecuado por fiscalmonth == 12. | Manejo de datos correcto, pero con errores menores en el cálculo del retorno o en la creación de la variable dependiente. | Cálculo incorrecto del retorno anual o de la variable dependiente, con errores evidentes en el manejo de los datos o el filtrado. | No se realiza ni el cálculo del retorno anual, ni la creación de la variable dependiente, ni el filtrado de los datos. | 30% |
2. Cálculo de Razones Financieras | Cálculo y selección clara de al menos tres razones financieras relevantes y bien justificadas. | Cálculo correcto de razones financieras, pero con explicaciones superficiales o insuficientes. | Selección pobre o cálculo incorrecto de las razones financieras. | No se calculan ni seleccionan razones financieras. | 20% |
3. Modelo de Machine Learning | Modelo implementado correctamente con partición 80/20, matriz de confusión y análisis detallado de resultados. | Modelo implementado correctamente, pero con una interpretación incompleta o sin análisis profundo de resultados. | Implementación incompleta del modelo o errores en la partición de los datos y en la matriz de confusión. | No se implementa un modelo de Machine Learning. | 30% |
4. Interpretación de Resultados | Interpretación coherente y detallada de los resultados y su impacto en la predicción de los retornos. | Interpretación correcta, pero sin suficiente profundidad o análisis crítico. | Interpretación superficial o poco clara de los resultados. | No se realiza interpretación de resultados. | 20% |
¡Muchos éxitos!
7 Solución
7.1 Instalar y cargar las liberarías
7.2 Descargar los datos
# Descargar el archivo CSV del sitio web:
download.file("http://www.apradie.com/datos/uspanel1.csv", "uspanel1.csv")
# Importar el panel
<- read.csv("uspanel1.csv") uspanel
Lista de las variables en el dataset
head(uspanel)
firm q fiscalmonth revenue cogs sgae otherincome extraordinaryitems
1 BCPC 2021q1 3 185656 126929 28152 133 0
2 BCPC 2021q2 6 388021 269847 57006 167 0
3 BCPC 2021q3 9 585890 406782 85427 295 0
4 BCPC 2021q4 12 799023 555849 115672 187 0
5 BCPC 2022q1 3 228867 157361 33170 -161 0
6 BCPC 2022q2 6 465560 322178 65126 137 0
finexp incometax totalassets currentassets inventory totalliabilities
1 725 6572 1169215 281204 77022 319723
2 1333 13860 1173606 287263 78333 303964
3 1889 20932 1179349 300275 81925 290271
4 2456 29129 1199325 322232 91058 322310
5 545 8700 1194908 319804 108411 321226
6 1505 18176 1606674 377141 140840 705304
currentliabilities longdebt adjprice originalprice sharesoutstanding year
1 88763 172337 123.1968 125.41 32392.81 2021
2 100759 143660 128.9436 131.26 32435.69 2021
3 104926 129069 142.5098 145.07 32370.53 2021
4 143802 129395 166.2726 168.60 32381.61 2021
5 123529 150664 134.8130 136.70 32187.35 2022
6 144143 458794 127.9491 129.74 32116.46 2022
fixedassets yearf cto fiscalq Nombre status partind
1 226513 2021 1 1 Balchem Corp activo NA
2 228289 2021 2 2 Balchem Corp activo NA
3 229798 2021 3 3 Balchem Corp activo NA
4 237517 2021 4 4 Balchem Corp activo NA
5 240419 2022 1 1 Balchem Corp activo NA
6 252145 2022 2 2 Balchem Corp activo NA
naics1 naics2 SectorEconomatica
1 Industrias manufactureras Industria química Química
2 Industrias manufactureras Industria química Química
3 Industrias manufactureras Industria química Química
4 Industrias manufactureras Industria química Química
5 Industrias manufactureras Industria química Química
6 Industrias manufactureras Industria química Química
8 Creación de variables
# Calcular las variables dependientes e independientes.
<- uspanel %>%
uspanel group_by(firm) %>%
# Organizar los datos por empresa y trimestre
arrange(firm, q) %>%
# Calcular el EBIT, el mar
mutate(ebit = revenue - cogs - sgae,
margen_operativo = ifelse(revenue == 0, NA, ebit / revenue),
crecimiento_margen = margen_operativo / lag(margen_operativo) -1,
apalancamiento = ifelse(totalassets == 0, NA, longdebt / totalassets),
crecimiento_activos = ifelse(totalassets == 0, NA, totalassets / lag(totalassets) - 1),
margen_operativo_p = ifelse(crecimiento_margen > 0, 1, 0))
# Mostrar las primeras observaciones de las variables del modelo
%>%
uspanel select(firm, q, margen_operativo, margen_operativo_p, apalancamiento, crecimiento_activos) %>%
head()
# A tibble: 6 × 6
# Groups: firm [1]
firm q margen_operativo margen_operativo_p apalancamiento
<chr> <chr> <dbl> <dbl> <dbl>
1 BCPC 2021q1 0.165 NA 0.147
2 BCPC 2021q2 0.158 0 0.122
3 BCPC 2021q3 0.160 1 0.109
4 BCPC 2021q4 0.160 0 0.108
5 BCPC 2022q1 0.168 1 0.126
6 BCPC 2022q2 0.168 1 0.286
# ℹ 1 more variable: crecimiento_activos <dbl>
9 Entrenamiento del modelo
Creamos la partición 80/20
set.seed(123456)
# Aleatorizar el índice de las filaes:
<-sample(nrow(uspanel))
rows_shuffled
# Aleatorizar el orden de los datos
<- uspanel[rows_shuffled, ] shuffled_uspanel
Dejamos las variables que se usarán en el modelo
<- shuffled_uspanel %>%
shuffled_uspanel select(firm, q, margen_operativo_p, apalancamiento, crecimiento_activos)
head(shuffled_uspanel)
# A tibble: 6 × 5
# Groups: firm [5]
firm q margen_operativo_p apalancamiento crecimiento_activos
<chr> <chr> <dbl> <dbl> <dbl>
1 HUN 2021q4 0 0.203 0.0960
2 OLN 2022q4 0 0.356 -0.0214
3 ECVT 2024q2 1 0.479 -0.00594
4 IOSP 2021q1 NA 0.0172 NA
5 FMC 2023q4 0 0.254 0.0885
6 OLN 2021q2 0 0.425 0.0241
# Dividimos los datos entre la base de entrenamiento y la de evaluación y creamos los dos sets
<- round(nrow(shuffled_uspanel)*.80)
split split
[1] 90
# Crear base de entrenamiento
<- shuffled_uspanel[1:split, ]
train
# Crear base de evaluación
<- shuffled_uspanel[(split+1):nrow(shuffled_uspanel), ] test
Correr el modelo de regresión logísitca con la base de entrenamiento
<- glm(margen_operativo_p ~ apalancamiento + crecimiento_activos, data= train, family="binomial",na.action=na.omit)
logit_train summary(logit_train)
Call:
glm(formula = margen_operativo_p ~ apalancamiento + crecimiento_activos,
family = "binomial", data = train, na.action = na.omit)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -0.3815 0.4969 -0.768 0.443
apalancamiento 0.4361 1.3525 0.322 0.747
crecimiento_activos -3.5313 4.2639 -0.828 0.408
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 111.29 on 80 degrees of freedom
Residual deviance: 110.31 on 78 degrees of freedom
(9 observations deleted due to missingness)
AIC: 116.31
Number of Fisher Scoring iterations: 4
10 Predicciones y evaluación del modelo
Luego, se crean las predicciones de la probabilidad de ocurrencia del evento con el modelo.
$margen_operativo_p_predprob = predict(logit_train,newdata=test, type="response")
testhead(test)
# A tibble: 6 × 6
# Groups: firm [5]
firm q margen_operativo_p apalancamiento crecimiento_activos
<chr> <chr> <dbl> <dbl> <dbl>
1 OLN 2022q2 0 0.331 0.0117
2 IOSP 2021q2 1 0.0167 0.0250
3 FMC 2024q1 0 0.253 0.00438
4 BCPC 2023q1 0 0.292 0.00262
5 CC 2021q1 NA 0.580 NA
6 OLN 2021q3 1 0.363 -0.000520
# ℹ 1 more variable: margen_operativo_p_predprob <dbl>
Para asignar cuándo el modelo predice la ocurrencia del evento, debo asignar un punto de corte. En este caso, si el modelo predice la ocurrencia con una probabilidad mayor al 50%, entonces determino que el evento ocurrirá.
# Crear margen_operativo_p_pred
<- test %>%
test mutate(margen_operativo_p_pred = ifelse(margen_operativo_p_predprob > 0.5, 1, 0))
head(test)
# A tibble: 6 × 7
# Groups: firm [5]
firm q margen_operativo_p apalancamiento crecimiento_activos
<chr> <chr> <dbl> <dbl> <dbl>
1 OLN 2022q2 0 0.331 0.0117
2 IOSP 2021q2 1 0.0167 0.0250
3 FMC 2024q1 0 0.253 0.00438
4 BCPC 2023q1 0 0.292 0.00262
5 CC 2021q1 NA 0.580 NA
6 OLN 2021q3 1 0.363 -0.000520
# ℹ 2 more variables: margen_operativo_p_predprob <dbl>,
# margen_operativo_p_pred <dbl>
Ahora construyo la matriz de confusión para evaluar el modelo.
# Convierto las variables a factores
$margen_operativo_p = factor(test$margen_operativo_p,levels=c("1","0"))
test$margen_operativo_p_pred = factor(test$margen_operativo_p_pred,levels=c("1","0"))
test
# Construyo la matriz de confusión
<- confusionMatrix(test$margen_operativo_p,test$margen_operativo_p_pred, positive='1')
CM1 CM1
Confusion Matrix and Statistics
Reference
Prediction 1 0
1 0 8
0 0 13
Accuracy : 0.619
95% CI : (0.3844, 0.8189)
No Information Rate : 1
P-Value [Acc > NIR] : 1.00000
Kappa : 0
Mcnemar's Test P-Value : 0.01333
Sensitivity : NA
Specificity : 0.619
Pos Pred Value : NA
Neg Pred Value : NA
Prevalence : 0.000
Detection Rate : 0.000
Detection Prevalence : 0.381
Balanced Accuracy : NA
'Positive' Class : 1
11 Análisis de la Matriz de Confusión y Métricas
La matriz de confusión muestra que el modelo no identificó ningún verdadero positivo, con 8 falsos positivos y 13 verdaderos negativos:
Referencia (Real) | ||
---|---|---|
Predicción | 1 | 0 |
1 | 0 | 8 |
0 | 0 | 13 |
11.1 Sensitivity (Sensibilidad)
La sensibilidad mide la capacidad del modelo para detectar verdaderos positivos. Dado que no hubo positivos reales ni predicciones correctas de la clase 1, no se puede calcular (NA). Esto refleja un problema significativo: el modelo no logra detectar ninguna instancia de la clase positiva.
11.2 Specificity (Especificidad)
La especificidad, que mide la capacidad del modelo para identificar correctamente los negativos, es de 0.619. Esto indica que el 61.9% de las veces el modelo predice correctamente la clase negativa, lo cual es moderadamente bueno.
11.3 Falsos Positivos y Falsos Negativos
El modelo presenta 8 falsos positivos (predijo 1 cuando era 0) y 0 falsos negativos (no falló al predecir ningún positivo). Los falsos positivos son un gran problema, ya que el modelo predice la clase positiva erróneamente en varias ocasiones.
11.4 Valor Predictivo Positivo (PPV) y Negativo (NPV)
Debido a la ausencia de verdaderos positivos, tanto el PPV como el NPV no se pueden calcular (NA). Esto significa que no podemos evaluar qué tan confiable es el modelo al predecir casos positivos o negativos.
11.5 Kappa
El coeficiente Kappa es 0, lo que indica que el rendimiento del modelo no es mejor que una predicción al azar. Esto sugiere que el modelo carece de utilidad para identificar correctamente ambas clases.
11.6 Problemas Clave del Modelo:
- Incapacidad para detectar positivos: El modelo no identifica ningún verdadero positivo, lo que anula su sensibilidad.
- Falsos positivos elevados: Los falsos positivos son una preocupación crítica, ya que el modelo frecuentemente clasifica instancias negativas como positivas.
- Rendimiento general pobre: Con un Kappa de 0 y métricas clave no calculables, el modelo está sesgado y no es útil para la tarea de clasificación.