Capítulo 9 Análisis de Regresión Lineal Múltiple.

9.1 Introducción.

El análisis de regresión (lineal) múltiple es una de las técnicas de análisis de dependencias más profusamente utilizadas. En el modelo de regresión múltiple la variable dependiente tiene escala métrica. Las variables explicativas pueden ser métricas o ser atributos.

Según los datos de los que se alimenta el modelo, se aplicarán diferentes métodos de estimación, especificaciones y pruebas:

  • Series temporales.

  • Datos de corte transversal.

  • Paneles de datos.

En este capítulo nos centraremos en los modelos estimados con base en datos de corte transversal (es decir, las variables tienen datos referentes a distintos casos o individuos: personas, empresas, países, etc.)

La construcción de un modelo de regresión cuenta con una serie de etapas, que son:

  1. Especificación del modelo: establecer las variables que entrarán a formar parte del modelo (dependiente, explicativas).

  2. Estimación: calcular el valor de los parámetros o coeficientes estructurales del modelo.

  3. Contraste y validación: verificar si el modelo estimado cumple con las hipótesis que garantizan unas buenas propiedades y si es adecuado para representar la realidad.

  4. Utilización del modelo: a efectos de previsión, análisis estructural o simulación de escenarios.

Vamos a partir del modelo básico de regresión (MBR). Es cierto que, para superar ciertas carencias de este, se ha procedido a desarrollar especificaciones y métodos de estimación más elaborados; pero no es menos cierto que no es conveniente “quemar” etapas sin conocer las características del modelo fundamental, como cimiento donde se posan modelados más complejos.

En el MBR vamos a suponer que existen:

  • Una variable dependiente y.

  • k variables explicativas \(x_j\).

  • Variable o perturbación aleatoria u, que recoge el efecto conjunto de todas aquellas variables que afectan al comportamiento de y pero que no están explicitadas en la especificación como variables x.

  • El tamaño de la muestra es n.

El modelo que se plantea es:

\[ y_i=\beta_1x_{i1}+\beta_2x_{i2}+\cdots+\beta_kx_{ik}+u_i \]

con \(i=1,2,...,n\) .

O, en notación matricial:

\[ y=X\beta+u \]

Donde y es un vector (nx1), X una matriz (nxk), \(\beta\) un vector (kx1), y u un vestor (nx1).

En el MBR, la perturbación aleatoria u debe cumplir con una serie de hipótesis básicas: normalidad en su comportamiento, esperanza nula, homoscedasticidad o varianza constante, y ausencia de autocorrelación (covarianza nula entre diferentes elementos del vector de la perturbación). Estas hipótesis, junto a las de permanencia estructural (valores de los elementos de \(\beta\) constantes a lo largo de la muestra), no endogeneidad o regresores no-estocásticos (covarianza nula entre la matriz X y el vector u), y rango pleno (las columnas de la matriz X o variables explicativas no han de ser combinaciones lineales unas de otras); permiten que el MBR pueda ser estimado por el método de mínimos cuadrados ordinarios (MCO), obteniendo estimadores con las mejores propiedades: insesgadez, eficiencia, consistencia.

En la medida en que alguna o algunas de las hipótesis básicas no se cumplan, la calidad de los estimadores MCO perderan calidad, en el sentido de no gozar de las propiedades deseables, desde un punto de vista inferencial. En tal caso, podrán aplicarse otros métodos de estimación, diversos métodos econométricos, o asumir que los estimadores carecen de algunas de las propiedades deseables.

El modelo estimado será:

\[ \hat{y}_i=\hat{\beta}_1x_{i1}+\hat{\beta}_2x_{i2}+\cdots+\hat{\beta}_kx_{ik} \]

Y el error o residuo será, para cada observación, \(\hat{u}_i=y_i-\hat{y}_i\). El vector de residuos se considera una estimación del vector de perturbaciones aleatorias. Es por ello que el vector de residuos se utiliza para verificar el cumplimiento de las hipótesis del modelo básico referentes al comportamiento de la perturbación (normalidad, homoscedasticidad, ausencia de autocorrelación…)

Tras estas breves notas formales del MBR, pasaremos a construir un modelo que intentará explicar el comportamiento de la rentabilidad económica de un grupo de empresas en función de una serie de variables aleatorias.

9.2 Especificación del MBR. Datos de corte transversal.

Vamos a explicar, mediante un modelo de regresión múltiple, el comportamiento de la rentabilidad económica (RENECO) de las empresas de producción eléctrica mediante tecnología eólica en función del resultado del ejercicio (RES), el activo (ACTIVO), del grado de endeudamiento (ENDEUDA), del grado de apalancamiento (APALANCA), y del tamaño del grupo corporativo (matriz) al que pertenece (DIMENSION). Para ello se ha seleccionado una muestra constituida por 50 empresas.

Supondremos que trabajamos en un proyecto de RStudio de nombre, por ejemplo, “regresion”. Los datos de las empresas se encuentran en la hoja “Datos” del archivo de Microsoft® Excel® “eolica_50.xlsx”. El script con el código que vamos a ir ejecutando se llama “regresion_eolica.R”.

En primer lugar, como de costumbre, hemos de preparar los datos: importarlos en R y gestionar missing values y outliers.

Así, una vez abierto el script en el editor de RStudio , comprobaremos que la primera parte del código está dedicada a la limpieza de la memoria (Environment) y a la importación de los datos. Para ello, activaremos el paquete {readxl} y utilizaremos la función read_excel(), indicando en los argumentos el archivo a explorar, y la hoja en la cual se encuentran los datos (hoja “Datos”). También hemos de prestar atención a la cuestión de si existen en la hoja de Excel anotaciones en las celdas donde no haya dato, para completar adecuadamente el argumento na= . Los datos se almacenarán en el data frame “datos”. En este data frame, la primera columna no es una verdadera variable, sino que se compone de los nombres de los casos o empresas. Con una línea de código adicional transformaremos esa primera columna en el nombre de las filas, de modo que tal columna abandona su rol de variable. En definitiva, el código para importar los datos es:

## Regresion multiple empresas eolicas. Disculpen la falta de tildes.

rm(list = ls())

# DATOS

  # Importando

    library(readxl)
    eolicos <- read_excel("eolica_50.xlsx", sheet = "Datos",
                          na = c("n.d.", "s.d."))
    eolicos <- data.frame(eolicos, row.names = 1)
    summary (eolicos)
##       RES              ACTIVO            FPIOS        
##  Min.   :-5268.6   Min.   :  25355   Min.   : -10985  
##  1st Qu.:  919.6   1st Qu.:  35512   1st Qu.:   1897  
##  Median : 2321.2   Median :  50301   Median :  17256  
##  Mean   : 4986.7   Mean   : 189142   Mean   :  83164  
##  3rd Qu.: 4380.4   3rd Qu.: 101035   3rd Qu.:  44871  
##  Max.   :42737.0   Max.   :2002458   Max.   :1740487  
##                    NA's   :1                          
## 
##      RENECO           RENFIN            LIQUIDEZ       
##  Min.   :-2.708   Min.   :-263.639   Min.   :  0.0140  
##  1st Qu.: 1.817   1st Qu.:   1.251   1st Qu.:  0.6462  
##  Median : 3.957   Median :  14.322   Median :  1.1550  
##  Mean   : 5.758   Mean   :  32.071   Mean   :  4.2370  
##  3rd Qu.: 8.038   3rd Qu.:  36.193   3rd Qu.:  1.9185  
##  Max.   :35.262   Max.   : 588.190   Max.   :128.4330  
##  NA's   :2                                             
## 
##     ENDEUDA            MARGEN           SOLVENCIA      
##  Min.   :  0.917   Min.   :-2248.16   Min.   :-24.465  
##  1st Qu.: 37.272   1st Qu.:   14.74   1st Qu.:  4.327  
##  Median : 74.683   Median :   23.86   Median : 25.317  
##  Mean   : 66.646   Mean   :  -17.77   Mean   : 33.353  
##  3rd Qu.: 95.672   3rd Qu.:   45.88   3rd Qu.: 62.727  
##  Max.   :124.465   Max.   :  400.90   Max.   : 99.082  
## 
##     APALANCA            MATRIZ           DIMENSION        
##  Min.   :-6905.772   Length:50          Length:50         
##  1st Qu.:    7.586   Class :character   Class :character  
##  Median :  126.208   Mode  :character   Mode  :character  
##  Mean   :  784.430                                        
##  3rd Qu.:  763.952                                        
##  Max.   :12244.351

Posteriormente, seleccionaremos las variables que vamos a utilizar en el análisis, almacenándolas en otro data frame de nombre, por ejemplo, “originales”:

  # Seleccionando variables clasificadoras para el analisis

    library(dplyr)
    originales<-select(eolicos,
                       RENECO,
                       RES,
                       ACTIVO,
                       ENDEUDA,
                       APALANCA,
                       DIMENSION)
    summary (originales)
##      RENECO            RES              ACTIVO       
##  Min.   :-2.708   Min.   :-5268.6   Min.   :  25355  
##  1st Qu.: 1.817   1st Qu.:  919.6   1st Qu.:  35512  
##  Median : 3.957   Median : 2321.2   Median :  50301  
##  Mean   : 5.758   Mean   : 4986.7   Mean   : 189142  
##  3rd Qu.: 8.038   3rd Qu.: 4380.4   3rd Qu.: 101035  
##  Max.   :35.262   Max.   :42737.0   Max.   :2002458  
##  NA's   :2                          NA's   :1        
## 
##     ENDEUDA           APALANCA          DIMENSION        
##  Min.   :  0.917   Min.   :-6905.772   Length:50         
##  1st Qu.: 37.272   1st Qu.:    7.586   Class :character  
##  Median : 74.683   Median :  126.208   Mode  :character  
##  Mean   : 66.646   Mean   :  784.430                     
##  3rd Qu.: 95.672   3rd Qu.:  763.952                     
##  Max.   :124.465   Max.   :12244.351

Para localizar los casos concretos de missing values, puede recurrirse a utilizar las herramientas de manejo de data frames del paquete {dplyr}. Con la función vismiss() del paquete {visdat} podemos tener una visión gráfica general de los valores faltantes. Si hay casos faltantes en una de las variables, los identificaremos filtrando el data frame con la función filter() de {dplyr}. Ante la existencia de missing values, se puede actuar de varios modos. Por ejemplo, se puede intentar obtener por otro canal de información el conjunto de valores de las variables que no están disponibles, o recurrir a alguna estimación para los mismos y asignarlos. En caso de que esto sea difícil, se puede optar, simplemente, por eliminar estos casos, en especial cuando representan un porcentaje muy reducido respecto al total de casos. En nuestro ejemplo, vamos a suponer que hemos optado por esta última vía. Así, estos casos, finalmente, será exluidos del análisis, utilizando para ello un nuevo filtro:

  # Identificando missing values.

    library(visdat)
    vis_miss(originales)

    originales %>%
      filter(is.na(RENECO) | is.na(RES) | is.na(ACTIVO) |
             is.na(ENDEUDA) | is.na(APALANCA) | is.na(DIMENSION)) %>%
      select(RENECO, RES, ACTIVO, ENDEUDA, APALANCA, DIMENSION)  
##                              RENECO       RES ACTIVO ENDEUDA  APALANCA DIMENSION
## La Caldera Energia Burgos SL  2.643   511.304     NA 110.636 -1019.288    GRANDE
## Sargon Energias SLU              NA -2216.000  85745 112.811  -879.289     MEDIA
## Viesgo Renovables SL.            NA  4609.000 269730  34.116    13.330     MEDIA
    originales <- originales %>%
      filter(! is.na(RENECO) & ! is.na(RES) & ! is.na(ACTIVO) &
             ! is.na(ENDEUDA) & ! is.na(APALANCA) & ! is.na(DIMENSION))

Tras el código anterior, se han eliminado del data frame “originales” las empresas “Sargon Energías S. L. U.” y “Viesgo Renovables S. L.”, debido a que carecían de dato de rentabilidad económica (RENECO), y “La Caldera Energía Burgos, S. L.”, al no tener dato sobre el valor de sus activos (ACTIVO).

Una vez tratados los casos con valores perdidos o missing values, es necesario detectar la presencia de outliers o casos atípicos en la muestra, que pudieran desvirtuar los resultados del análisis de regresión. Para realizar esta etapa, y dado que en nuestro análisis contamos con 5 variables, primero “resumiremos” el valor que toman dichas variables para cada caso, mediante el cálculo de la distancia de Mahalanobis. De hecho, las distancias de los diferentes casos se almacenarán en una nueva variable, a la que llamaremos MAHALANOBIS, que se incorporará al data frame “originales” por medio de la función mutate() de {dplyr}, y la función mahalanobis(). Recordemos que, en los diferentes argumentos de esta función, el punto “.” hace referencia al data frame que está delante del operador pipe (%>%). Posteriormente, construiremos el diagrama de caja de MAHALANOBIS para verificar la existencia de outliers (puntos), e identificaremoslos casos correspondientes con el filtro adecuado. Obtaremos por crear un nuevo data frame, “originales_so”, aplicando un nuevo filtro que elimine esos casos localizados como outliers:

  # Identificando outliers.

    originales <- originales %>%
      mutate(MAHALANOBIS = mahalanobis(select(.,
                                          RENECO,
                                          RES,
                                          ACTIVO,
                                          ENDEUDA,
                                          APALANCA),
                                        center = colMeans(select(.,
                                                            RENECO,
                                                            RES,
                                                            ACTIVO,
                                                            ENDEUDA,
                                                            APALANCA)),
                                        cov=cov(select(.,
                                                  RENECO,
                                                  RES,
                                                  ACTIVO,
                                                  ENDEUDA,
                                                  APALANCA))))

    library (ggplot2)
    ggplot(data = originales, map = (aes(y = MAHALANOBIS))) +
    geom_boxplot(fill = "orange") +
    ggtitle("DISTANCIA DE MAHALANOBIS", subtitle = "Empresas eólicas") +
    ylab("MAHALANOBIS")

    Q1M <- quantile (originales$MAHALANOBIS, c(0.25))
    Q3M <- quantile (originales$MAHALANOBIS, c(0.75))
    
    originales %>% filter(MAHALANOBIS > Q3M + 1.5*IQR(MAHALANOBIS) |
                      MAHALANOBIS < Q1M - 1.5*IQR(MAHALANOBIS)) %>%
                   select(MAHALANOBIS)
##                                       MAHALANOBIS
## Corporacion Acciona Eolica SL            13.67263
## Parque Eolico Sierra De Las Carbas SL    13.34797
## Naturgy Renovables SLU                   19.45720
## Global Power Generation SA.              19.88409
## Saeta Yield SA.                          20.40512
## Molinos Del Ebro SA                      21.73955
## Elecdey Lezuza SA                        18.97057
    originales_so <- originales %>%
                     filter(MAHALANOBIS <= Q3M + 1.5*IQR(MAHALANOBIS) &
                            MAHALANOBIS >= Q1M - 1.5*IQR(MAHALANOBIS)) 
    originales <- originales %>% select(-MAHALANOBIS)
    originales_so <- originales_so %>% select(-MAHALANOBIS)

Se han localizado 7 empresas con valores de la distancia de Mahalanobis atípicos, lo que hace pensar que, a su vez, estas empresas registran valores atípicos en una o varias de las variables originales. Como ya hemos señalado, eliminamos estos 7 casos de la muestra. Así, el data frame “originales_so”, que es el que emplearemos para estimar el modelo, cuenta con 40 observaciones.

Una de las variables explicativas es un atributo o factor, llamado DIMENSION, que cuenta con 3 niveles: “GRANDE”, “MEDIANA” y “PEQUEÑA”, según el número de empresas integradas en la matriz a la que pertenece cada empresa de la muestra. Hemos de informar a R de la condición de atributo o factor de esta variable (pues de momento solo la contempla como una variable cualitativa o alfanumérica). Para ello ejecutaremos el código:

  # Convertir variable DIMENSION en Factor.

    originales_so$DIMENSION <- as.factor(originales_so$DIMENSION)
    levels(originales_so$DIMENSION)
## [1] "GRANDE"  "MEDIA"   "PEQUEÑA"

Una vez preparadas todas las variables, es el momento de especificar y estimar una regresión múltiple inicial, que contenga todas las variables explicativas candidatas a formar parte de la versión final.

Para especificar y estimar este modelo inicial, deben de cargarse previamente algunos paquetes que es necesario utilizar: {knitr} y {kableExtra} para construir tablas con los resultados de la regresión, y {broom}. Este último paquete es fundamental, ya que permite disponer de un modo cómodo de todos los elementos de los que se compone un modelo estimado (no solo lineal, como es nuestro caso).

Por otro lado, El MBR lineal, estimado mediante el método de mínimos cuadrados ordinarios (MCO), se obtendrá mediante la función lm(). Los resultados los guardaremos en un objeto de nombre, por ejemplo, “ecua0”. Luego, se realizará un summary() para ver dicha estimación.

# ESPECIFICACION Y ESTIMACION

  # Cargar las librerías necesarias

    library (knitr)
    library (kableExtra)
    library (broom)
    library (car) # para obtener el vif

  # Especificar el modelo de regresión lineal
    ecua0 <- lm(data = originales_so,
                RENECO ~ RES + ACTIVO + ENDEUDA + APALANCA + DIMENSION)
    summary(ecua0)

El resultado es:

## 
## Call:
## lm(formula = RENECO ~ RES + ACTIVO + ENDEUDA + APALANCA + DIMENSION, 
##     data = originales_so)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -7.0302 -2.1468 -0.1034  1.5821  6.7527 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       5.045e+00  1.575e+00   3.203  0.00301 ** 
## RES               9.962e-04  2.137e-04   4.661 4.99e-05 ***
## ACTIVO           -3.060e-05  8.938e-06  -3.423  0.00167 ** 
## ENDEUDA          -6.297e-03  1.859e-02  -0.339  0.73698    
## APALANCA         -4.197e-04  3.508e-04  -1.196  0.24007    
## DIMENSIONMEDIA    1.462e+00  1.516e+00   0.964  0.34195    
## DIMENSIONPEQUEÑA  1.781e+00  1.563e+00   1.139  0.26282    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.43 on 33 degrees of freedom
## Multiple R-squared:  0.5943, Adjusted R-squared:  0.5205 
## F-statistic: 8.056 on 6 and 33 DF,  p-value: 2.122e-05

Podemos observar, en cuanto a la bondad del modelo, cómo es capaz de recoger el 52% de la varianza o comportamiento de la rentabilidad económica (RENECO), atendiendo al valor del coeficiente de determinación lineal corregido (Adjusted R-squared). Los coeficientes o parámetros estimados, en su conjunto, son estadísticamente significativos para una significación de 0,05 (p-valor muy pequeño, en el contraste F de significación conjunta). En cuanto a los coeficientes estimados considerados individualmente, encontramos que son estadísticamente significativos tanto el término independiente (intercept), como los asociados a las variables RES y ACTIVO, a juzgar por los p-valores correspondientes al contraste t de significación individual. En ambos casos, además, son coeficientes con signo positivo, lo que se interpreta como que ambas variables influyen sobre RENECO de modo que, a mayor valor de estas variables, en general se obtiene una mayor rentabilidad económica.

Por último, es conveniente advertir que el factor DIMENSION se especifica mediante dos variables dicotómicas que representan a dos de los niveles del factor (“MEDIA” y “PEQUEÑA”). sus coeficientes muestran el efecto relativo de ese nivel en relación con el nivel que no es especificado explicitamente (“GRANDE”), ya que no se pueden especificar todos los niveles de un factor para no generar un problema de multicolinealidad perfecta.

Hay otras informaciones importantes a la hora de valorar la especificación (inicial) del modelo que no se recogen en el summary() del mismo. Además, vamos a presentar todo en modo de tablas diseñadas con kable(). Para poder hacer esto no solo con este modelo inicial, sino con cualquier otra estimación, vamos a integrar el código correspondiente en una función de R. Llamaremos a esta función presenta_modelo(), y recibirá como input la estimación lineal de un modelo, ofreciendo como output tres tablas contenidas en una lista: la primera es una versión del summary(), la segunda es una tabla con otras informaciones adicionales, como el valor del Criterio de Información de Akaike (AIC), y la tercera contiene los valores del factor de inflación de la varianza (VIF) de las variables del modelo. Previamente a mostrar el código de la función, fijaremos un parámetro de nombre, por ejemplo, “knitr.table.format”, para recoger el formato en el que se generarán las tablas diseñadas:

  #diseña salida ordenador

    knitr.table.format = "html"

El código de la función se basa en el aprovechamiento, a su vez, de dos de las funciones del paquete {broom}. Para realizar la versión en tabla del summary() del modelo, se aplica la función tidy(). Esta función crea un data frame donde se almacenan las columnas con las distintas informaciones (coeficientes, desviaciones típicas, valores del estadístico t, p-valores…) con tantas filas como variables explicativas especificadas. La función glance(), por su lado, extre las siguientes informaciones:

  1. r.squared: El coeficiente de determinación R², que indica el porcentaje de variación explicada por el modelo.

  2. adj.r.squared: El R² ajustado, que toma en cuenta los grados de libertad.

  3. sigma: El error estándar de los residuos.

  4. statistic: El estadístico F del modelo.

  5. p.value: El valor p asociado con el estadístico F, que indica la significancia global del modelo.

  6. df: Los grados de libertad del numerador del estadístico F.

  7. logLik: El logaritmo de la verosimilitud del modelo.

  8. AIC: El criterio de información de Akaike.

  9. BIC: El criterio de información bayesiano.

  10. deviance: La desviación del modelo.

El código es, en definitiva:

  # Definir la función de presentación de resultados:  presenta_modelo() #####

    presenta_modelo <- function(modelo) {

    # Lista de piezas
      modelo_piezas <-list()
  
    # Aplicar la función tidy() al modelo
      resultados <- tidy(modelo)
  
    # Seleccionar las columnas deseadas
      resultados <- resultados[, c("term",
                                   "estimate",
                                   "std.error","statistic",
                                   "p.value")]
    # Añadir la columna 'stars' según los valores de 'p.value'
      resultados$stars <- cut(resultados$p.value,
                            breaks = c(-Inf, 0.001, 0.01, 0.05, 0.1, Inf),
                            labels = c("***", "**", "*", "·", " "),
                            right = FALSE)
    # formatear los valores de la columna "estimate" a 5 decimales
      resultados$estimate <- formatC(resultados$estimate,
                                     format = "f",
                                     digits = 5)
  
    # Crear la tabla con kable
      tabla1 <- resultados %>%
        kable(format = knitr.table.format,
          caption = "Modelo Lineal",
          col.names = c("Variable", "Coeficiente", "Desv. Típica",
                        "Estadístico t", "p-valor", "Sig."),
          digits = 3,
          align = c("l", "c", "c", "c", "c", "c")) %>%
        kable_styling(full_width = F,
                      bootstrap_options = "striped",
                                          "bordered",
                                          "condensed",
                      position = "center",
                      font_size = 11)
 
      modelo_piezas[[1]] <- tabla1
  
    # Aplicar la función glance
      estadisticos <- glance(modelo)
      estadisticos <- estadisticos[,c("r.squared",
                                      "adj.r.squared",
                                      "sigma",
                                      "statistic",
                                      "p.value",
                                      "AIC",
                                      "nobs")]
    # Crear la tabla con kable
      tabla2 <- estadisticos %>%
        kable(format = knitr.table.format,
          caption = "Estadísticos del modelo",
          col.names = c("R2", "R2 ajustado", "Sigma", "Estadístico F",
                        "p-valor", "AIC", "num. observaciones"),
          digits = 3,
          align = "c") %>%
        kable_styling(full_width = F,
                      bootstrap_options = "striped",
                                          "bordered",
                                          "condensed",
                      position = "center",
                      font_size = 11)
      
      modelo_piezas[[2]] <- tabla2  

    # Obtener VIF
      vif_df <- as.data.frame(vif(modelo))

    # Añadir nombres de filas
      library(tibble)
      vif_df <- vif_df %>%
      rownames_to_column(var = "Variable")
  
    # Crear tabla con kable
      tabla3 <- vif_df[,1:2] %>%
        kable(format = knitr.table.format,
              caption = "Factor de inflación de la varianza",
              col.names = c("Variable","Valor VIF"),
              digits = 3,
              align = "c") %>%
        kable_styling(full_width = F,
                      bootstrap_options = "striped",
                                          "bordered",
                                          "condensed",
                      position = "center",
                      font_size = 11)
      
      modelo_piezas[[3]] <- tabla3
  
    return(modelo_piezas)
  }
############################################################################

Una vez definida la función, podemos aplicarla al modelo estimado inicial, guardando las tres tablas generadas en la lista “modelo_0”, que pueden ser visualizadas:

  modelo_0 <- presenta_modelo(ecua0)

  modelo_0[[1]]
Table 9.1: Table 9.2: Modelo Lineal
Variable Coeficiente Desv. Típica Estadístico t p-valor Sig.
(Intercept) 5.04540 1.575 3.203 0.003 **
RES 0.00100 0.000 4.661 0.000 ***
ACTIVO -0.00003 0.000 -3.423 0.002 **
ENDEUDA -0.00630 0.019 -0.339 0.737
APALANCA -0.00042 0.000 -1.196 0.240
DIMENSIONMEDIA 1.46168 1.516 0.964 0.342
DIMENSIONPEQUEÑA 1.78055 1.563 1.139 0.263
  modelo_0[[2]]
Table 9.1: Table 9.1: Estadísticos del modelo
R2 R2 ajustado Sigma Estadístico F p-valor AIC num. observaciones
0.594 0.521 3.43 8.056 0 220.431 40
  modelo_0[[3]]
Table 9.1: Table 9.1: Factor de inflación de la varianza
Variable Valor VIF
RES 1.690
ACTIVO 1.873
ENDEUDA 1.279
APALANCA 1.318
DIMENSION 1.558

Los resultados de la primera y segunda tabla ya fueron comentados en casi su totalidad anteriormente, al comentar el summary() del modelo. Se ha añadido el valor del Criterio de Información de Akaike (AIC). Esta es una medida basada en la función de verosimilitud que permite comparar la adecuación de especificaciones alternativas para representar la realidad, de modo que, a menor AIC, mejor especificación.

La tercera tabla muestra los valores del factor de la inflación de la varianza (VIF) para cada variable explicativa. El VIF mide el riesgo de que, debido a la influencia de la variable en cuestión, exista un problema de multicolinealidad entre las variables explicativas del modelo. Un valor de 5/10 (dependiendo de los autores) sugiere que puede existir un problema de multicolinealidad importante. En el caso del modelo inicial, ningún valor del VIF sugiere un posible problema de multicolinealidad.

La búsqueda de una especificación alternativa que sea más adecuada puede atender a múltiples estrategias del analista, y del propio propósito con el que se quiere utilizar el modelo. Por tanto, implica una gran carga de subjetividad. No obstante, existen métodos automatizados para que, una vez se tiene la estimación inicial, se obtenga una especificación más sencilla (Principio de Parsimonia) sin una pérdida grande de bondad de la regresión. Por ejemplo, un método es el step / backward que, en función del Criterio de Información de Akaike (AIC), irá probando a estimar especificaciones más simples que disminuyan de AIC (lo que implica una mejor especificación). En nuestro caso, se aplicará con el código:

  ecuaDEF <- step(ecua0, scale = 0,
                  direction = c("backward"),
                  trace = 1, steps = 1000, k = 2)
## Start:  AIC=104.92
## RENECO ~ RES + ACTIVO + ENDEUDA + APALANCA + DIMENSION
## 
##             Df Sum of Sq    RSS    AIC
## - DIMENSION  2    17.324 405.63 102.66
## - ENDEUDA    1     1.350 389.65 103.06
## - APALANCA   1    16.843 405.14 104.61
## <none>                   388.30 104.92
## - ACTIVO     1   137.886 526.19 115.07
## - RES        1   255.671 643.97 123.15
## 
## Step:  AIC=102.66
## RENECO ~ RES + ACTIVO + ENDEUDA + APALANCA
## 
##            Df Sum of Sq    RSS    AIC
## - ENDEUDA   1      0.27 405.90 100.69
## <none>                  405.63 102.66
## - APALANCA  1     27.08 432.71 103.25
## - ACTIVO    1    278.04 683.67 121.54
## - RES       1    386.92 792.55 127.45
## 
## Step:  AIC=100.69
## RENECO ~ RES + ACTIVO + APALANCA
## 
##            Df Sum of Sq    RSS    AIC
## <none>                  405.90 100.69
## - APALANCA  1     34.56 440.46 101.96
## - ACTIVO    1    280.94 686.83 119.73
## - RES       1    394.36 800.26 125.84
  summary (ecuaDEF)
## 
## Call:
## lm(formula = RENECO ~ RES + ACTIVO + APALANCA, data = originales_so)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -6.3668 -2.5872 -0.3775  1.4791  7.3184 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5.759e+00  8.530e-01   6.752 6.97e-08 ***
## RES          1.110e-03  1.877e-04   5.914 9.05e-07 ***
## ACTIVO      -3.644e-05  7.301e-06  -4.992 1.54e-05 ***
## APALANCA    -5.376e-04  3.070e-04  -1.751   0.0885 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.358 on 36 degrees of freedom
## Multiple R-squared:  0.5759, Adjusted R-squared:  0.5406 
## F-statistic:  16.3 on 3 and 36 DF,  p-value: 7.444e-07

El resultado del algoritmo es una especificación del modelo, cuya estimación se almacena en memoria con el nombre, por ejemplo, “ecuaDEF”. Se ha mostrado el summary() del modelo. También se puede aplicar la función presenta_modelo(), para obtener un output de la estimación más detallado:

  modelo_DEF <- presenta_modelo(ecuaDEF)

  modelo_DEF[[1]]
Table 9.3: Table 9.4: Modelo Lineal
Variable Coeficiente Desv. Típica Estadístico t p-valor Sig.
(Intercept) 5.75901 0.853 6.752 0.000 ***
RES 0.00111 0.000 5.914 0.000 ***
ACTIVO -0.00004 0.000 -4.992 0.000 ***
APALANCA -0.00054 0.000 -1.751 0.088 ·
  modelo_DEF[[2]]
Table 9.3: Table 9.3: Estadísticos del modelo
R2 R2 ajustado Sigma Estadístico F p-valor AIC num. observaciones
0.576 0.541 3.358 16.295 0 216.204 40
  modelo_DEF[[3]]
Table 9.3: Table 9.3: Factor de inflación de la varianza
Variable Valor VIF
RES 1.360
ACTIVO 1.304
APALANCA 1.054

En el modelo definitivo (ecuaDEF), tan solo permanecen 3 variables explicativas: RES, ACTIVO (ambas significativas para una significación de 0,05) y APALANCA (significativa para una significación de 0,1). Una diferencia importante respecto al modelo inicial es que, el signo del coeficiente asociado a ACTIVO pasa a ser negativo. También el grado de apalancamiento tiene asociado un coeficiente negativo (a mayor apalancamiento, menor rentabilidad económica). SI observamos la segunda tabla, puede observarse que el valor de AIC es de 216,20, inferior al valor de AIC del modelo inicial (220,4). La bondad del ajuste, medida por medio del R2 ajustado, es de 0,541; superior al de modelo inicial (0,521). Finalmente, en la tercera tabla se muestran unos valores de VIF bajos, por lo que se descartan problemas de multicolinealidad.

Una vez decidida la especificación (final) del modelo, es necesario desarrollar la etapa de contrastación de las hipótesis básicas del modelo, con la intención de determinar el grado en que los estimadores obtenidos mediante MCO gozan de buenas propiedades, o si es necesario aplicar métodos de estimación alternativos, métodos econométricos específicos, o incluso re-especificar el modelo).

Es conveniente, previamente al análisis de cada hipótesis, generar varios gráficos de gran utilidad.

El primer paso, no obstante, es recurrir a la función augment() del paquete {broom}. Esta función, aplicada a un modelo, genera algunas series de datos fundamentales relacionadas con el mismo, y las almacena junto a las variables del modelo especificado en un data frame. En nuestro caso, hemos llamado al data frame “series_ecuaDEF”, y hemos cambiado el nombre a las series que vamos a utilizar posteriormente: los valores ajustados de la variable dependiente (RENECO.est), los residuos (residuos), y los valores de la distancia de Cook (cooksd). Además, hemos creado una variable llamada ORDEN para asignar un valor correlativo a cada observación o caso de la muestra:

# MODELO FINAL: CONTRASTACIÓN.

  series_ecuaDEF <- augment(ecuaDEF)
  series_ecuaDEF <- series_ecuaDEF %>%
  rename(RENECO.est = .fitted,
         residuos = .resid,
         cooksd = .cooksd)
  series_ecuaDEF$ORDEN = c(1:nrow(series_ecuaDEF))
  summary (series_ecuaDEF)
##   .rownames             RENECO            RES         
##  Length:40          Min.   :-2.708   Min.   :-5268.6  
##  Class :character   1st Qu.: 2.103   1st Qu.:  892.2  
##  Mode  :character   Median : 4.404   Median : 2321.2  
##                     Mean   : 5.547   Mean   : 2812.2  
##                     3rd Qu.: 8.270   3rd Qu.: 3951.2  
##                     Max.   :15.882   Max.   :12819.0  
## 
##      ACTIVO          APALANCA         RENECO.est    
##  Min.   : 25355   Min.   :-3037.8   Min.   :-8.564  
##  1st Qu.: 33162   1st Qu.:   21.4   1st Qu.: 3.970  
##  Median : 45903   Median :  146.5   Median : 5.620  
##  Mean   : 78664   Mean   :  868.9   Mean   : 5.547  
##  3rd Qu.: 81312   3rd Qu.: 1080.1   3rd Qu.: 7.432  
##  Max.   :443467   Max.   : 8049.4   Max.   :12.464  
## 
##     residuos            .hat             .sigma     
##  Min.   :-6.3668   Min.   :0.02906   Min.   :3.107  
##  1st Qu.:-2.5872   1st Qu.:0.03774   1st Qu.:3.350  
##  Median :-0.3775   Median :0.05202   Median :3.385  
##  Mean   : 0.0000   Mean   :0.10000   Mean   :3.356  
##  3rd Qu.: 1.4791   3rd Qu.:0.10120   3rd Qu.:3.403  
##  Max.   : 7.3184   Max.   :0.53601   Max.   :3.405  
## 
##      cooksd            .std.resid          ORDEN      
##  Min.   :0.0000143   Min.   :-1.9446   Min.   : 1.00  
##  1st Qu.:0.0007106   1st Qu.:-0.7888   1st Qu.:10.75  
##  Median :0.0083866   Median :-0.1147   Median :20.50  
##  Mean   :0.0514405   Mean   : 0.0116   Mean   :20.50  
##  3rd Qu.:0.0271051   3rd Qu.: 0.4557   3rd Qu.:30.25  
##  Max.   :1.4848788   Max.   : 2.4566   Max.   :40.00

Una vez obtenidas todas las series necesarias, diseñaremos los gráficos que facilitarán la comprensión y contraste del modelo final. El primero compara los valores reales de RENECO con las estimaciones del modelo, RENECO.est. Lógicamente, es deseable que, para cada caso, la distancia entre ambos puntos sea mínima. El segundo muestra los residuos. El tercero es el gráfico de densidad de los residuos, y el cuarto representa, para cada caso, el valor de la distancia de Cook. Los valores altos de la distancia de Cook en un modelo de regresión indican observaciones que tienen una influencia significativa en los coeficientes estimados del modelo. Son considerados “altos” los valores que superan el valor inverso de 4 por el número de observaciones muestrales.

Los 4 gráficos se agrupan mediante la gramática del paquete {patchwork}. Finalmente, se genera una tabla a partir de un filtro para identificar los casos concretos que tienen valores “altos” de la distancia de Cook. En definitiva, el código es:

  # Gráficos.
  
    g_real_pred <- ggplot(data = series_ecuaDEF) +
      geom_point(aes(x = ORDEN, y = RENECO.est),
                 size= 2,
                 alpha= 0.6,
                 color = "blue") +    
      geom_point(aes(x = ORDEN, y = RENECO),
                 size= 2,
                 alpha= 0.6,
                 color = "red") +
      geom_line(aes(x = ORDEN, y = RENECO.est),
                color = "blue",
                linetype = "dashed",
                size= 1) +
      geom_line(aes(x = ORDEN, y = RENECO),
                color = "red",
                linetype = "dashed",
                size= 1) +
      geom_segment(aes(x = ORDEN, xend = ORDEN, y = RENECO.est, yend = RENECO),
                   color = "orange") +
      ggtitle("RENTABILIDAD ECONÓMICA.",
              subtitle= "VALORES REALES (rojo) vs PREDICCIONES (azul).") +
      xlab("Casos") + 
      ylab("Rentabilidad Económica: Real y Predicción")

  g_resid <- ggplot(data = series_ecuaDEF, aes(x = ORDEN, y = residuos)) +
    geom_point(size=2, alpha= 0.6, color = "blue") +
    geom_smooth(color = "firebrick", span = 0.5) +
    geom_hline(yintercept = 0, color = "red")+
    ggtitle("RENTABILIDAD ECONÓMICA.", subtitle= "Residuos.")+
    xlab("Casos") + 
    ylab("Residuos")

  g_hresid <- ggplot(data = series_ecuaDEF, map = aes(x = residuos)) +
    geom_density(colour = "red", fill = "orange", alpha = 0.6) +
    ggtitle("RENTABILIDAD ECONÓMICA", subtitle = "Densidad Residuos")+
    xlab("Rentabilidad Económica") +
    ylab("Densidad")

  g_cook <- ggplot(data = series_ecuaDEF, aes(x = ORDEN, y = cooksd)) +
    geom_bar(stat = "identity") +
    geom_hline(yintercept = 4/nrow(series_ecuaDEF),
               linetype = "dashed",
               color = "red") +
    ggtitle("RENTABILIDAD ECONÓMICA.", subtitle= "Distancia de Cook.")+
    xlab("Casos") + 
    ylab("Distancias")

  library (patchwork)

  (g_real_pred | g_hresid) / (g_resid | g_cook)

  tablaCook <- series_ecuaDEF %>%
    filter ( cooksd > 4/nrow(series_ecuaDEF)) %>%
    select (.rownames, cooksd) %>%
    kable(format = knitr.table.format,
          caption = "Casos destacados distancia de Cook",
          col.names = c("Caso","Distancia de Cook"),
          digits = 3,
          align = c("l","c")) %>%
    kable_styling(full_width = F,
                  bootstrap_options = "striped",
                                      "bordered",
                                      "condensed",
                  position = "center",
                  font_size = 11)

    tablaCook
Table 9.5: Table 9.6: Casos destacados distancia de Cook
Caso Distancia de Cook
Innogy Spain SA. 1.485

De los gráficos anteriores se desprende, en general, que los residuos parecen mantener un comportamiento conforme a una distribución normal, y que hay una observación o caso con una distancia de Cook que puede influir de modo relevante en el valor de los coeficientes estimados. Esta empresa es identificada como “Innogy Spain S.A.” Podría estudiarse en detalle este caso o incluso reestimar el modelo sin su presencia, a fin de comprobar el efecto que tiene esta observación sobre la estimación en general.

Tras estudiar los gráficos, vamos a pasar a contrastar las hipótesis básicas del MBR referidas a la forma funcional, y la normalidad y comportamiento homoscedástico de la perturbación aleatoria. Para ello se aplicarán las pruebas Ramsey-Reset, Shapiro-Wilk y Breusch-Pagan, respectivamente. Los resultados de las tres pruebas se presentarán condensados en una tabla, en la que además se incluirá la conclusión del contraste, para una significación de 0,05. Para crear la tabla, primero se generará un data frame con sus elementos, denominado, por ejemplo, “check_hipotesis”. El código es:

  # Hipótesis básicas MBR.

    library (lmtest)
    reset_test <- resettest(ecuaDEF, data= originales_so) # F. Funcional
    shapiro_test <- shapiro.test(series_ecuaDEF$residuos) # Normalidad
    bp_test <- bptest(ecuaDEF) # Homoscedasticidad

  # Tabla resultados

    # Crear un data frame con los resultados

      check_hipotesis <- data.frame(
        "Tipo_de_prueba" = c("Forma Funcional",
                             "Normalidad de perturbación aleatoria",
                             "Homoscedasticidad de perturbación aleatoria"),
        "Prueba" = c("Ramsey-Reset",
                     "Shapiro-Wilk",
                     "Breusch-Pagan"),
        "Estadistico" = c(reset_test$statistic,
                          shapiro_test$statistic,
                          bp_test$statistic),
        "P_valor" = c(reset_test$p.value,
                      shapiro_test$p.value,
                      bp_test$p.value),
        "Conclusion" = c(ifelse(reset_test$p.value >= 0.05,
                                 "F. funcional correcta",
                                 "F. funcional incorrecta"),
                         ifelse(shapiro_test$p.value >= 0.05,
                                "Normalidad",
                                "No-Normalidad"),
                         ifelse(bp_test$p.value >= 0.05,
                                "Homoscedasticidad",
                                "Heteroscedasticidad")))

    row.names(check_hipotesis) <- NULL

  # Crear la tabla con kable
    
    tabla_check <- check_hipotesis %>%
      kable(format = knitr.table.format,
            caption = "Contrastes de hipótesis del MBR",
            col.names = c("Tipo de prueba",
                          "Prueba",
                          "Estadístico",
                          "P-valor",
                          "Conclusión"),
            digits = 3,
            align = c("l", "c", "c", "c", "c")) %>%
      kable_styling(full_width = F,
                    bootstrap_options = "striped",
                                        "bordered",
                                        "condensed",
                    position = "center",
                    font_size = 11)

    tabla_check
Table 9.7: Table 9.8: Contrastes de hipótesis del MBR
Tipo de prueba Prueba Estadístico P-valor Conclusión
Forma Funcional Ramsey-Reset 6.907 0.003 F. funcional incorrecta
Normalidad de perturbación aleatoria Shapiro-Wilk 0.963 0.212 Normalidad
Homoscedasticidad de perturbación aleatoria Breusch-Pagan 5.773 0.123 Homoscedasticidad

En la tabla de resultados del contraste de las hipótesis básicas del MBR, encontramos que el modelo no plantea problemas de falta de normalidad o heteroscedasticidad en la perturbación aleatoria. En cambio, la prueba de Ramsey-Reset rechaza la hipótesis nula de especificación lineal correcta. En concreto, este contraste plantea estas dos hipótesis:

  • Hipótesis nula (H0): El modelo está correctamente especificado, es decir, no hay errores de especificación. En términos más técnicos, esto significa que las combinaciones no lineales de los valores ajustados no tienen poder explicativo adicional sobre la variable dependiente.

  • Hipótesis alternativa (H1): El modelo está mal especificado, lo que implica que las combinaciones no lineales de los valores ajustados sí tienen poder explicativo adicional sobre la variable dependiente.

Si se asume que las variables especificadas son las correctas, y no hay omisión de variables relevantes; un rechazo de la hipótesis nula implica que la relación funcional planteada (lineal), no es correcta.

El incumplimiento de la hipótesis de linealidad podría acarrear que los estimadores MCO obtenidos adolecen de la pérdida de la propiedad de insesgadez.

Si el modelo supera razonablemente la fase de contraste de las hipótesis básicas; podría ser utilizado para los objetivos planteados en la investigación: análisis estructural, previsión, simulación. En este ejemplo, vamos a realizar un ejercicio de simulación.

Vamos a obtener la respuesta de la rentabilidad económica (RENECO), ante 3 escenarios alternativos. Dichos escenarios están guardados en la hoja “Simula” del archivo de Microsoft® Excel® “eolica_escenarios.xlsx”. Hay que tener en cuenta que en los escenarios se aportan valores para algunas variables que no aparecen en el modelo final. Lógicamente, lo importante son los datos del escenario correspondientes a las variables que sí están especificadas. Los escenarios se importan y se almacenan en el data frame de nombre, por ejemplo, “escenario”:

# SIMULACIÓN

  # Cargar escenario de Excel

    escenario <- read_excel("eolica_escenarios.xlsx", sheet = "Simula")
    escenario <- data.frame(escenario, row.names = 1)
    escenario
##              RES ACTIVO ENDEUDA APALANCA DIMENSION
## ESCENARIO A 2500  50000   20.25    0.238    GRANDE
## ESCENARIO B 2500  25000   50.00   50.000     MEDIA
## ESCENARIO C 3000   5000   90.00  100.000   PEQUEÑA

Mediante la función predict(), se generará la respuesta a los escenarios propuestos, por parte del modelo definitivo (ECUADEF). Esta respuesta se guardará en el data frame de nombre, por ejemplo, “estimación”, que luego uniremos al data frame “escenario” mediante la función cbind(), creando un único data frame llamado, por ejemplo, “simulacion”:

  # Simulación con el modelo

    estimacion <-predict (object= ecuaDEF,
                          newdata = escenario,
                          interval="prediction",
                          level=0.95)
    estimacion
##                  fit        lwr      upr
## ESCENARIO A 6.711704 -0.2161380 13.63955
## ESCENARIO B 7.596000  0.6413923 14.55061
## ESCENARIO C 8.852940  1.8523982 15.85348
    simulacion <- cbind(escenario, estimacion)

Finalmente, vamos a presentar la simulación en forma de tabla. En primer lugar, vamos a dar un formato específico a cada variable, cara a su volcado a la tabla, con la función format(). El argumento nsmall= indica el número mínimo de decimales que habrá a la derecha del punto decimal:

  # Formatear las columnas con el número mínimo de decimales deseado

    simulacion$ENDEUDA <- format(simulacion$ENDEUDA, nsmall = 3)
    simulacion$APALANCA <- format(simulacion$APALANCA, nsmall = 3)
    simulacion$fit <- format(simulacion$fit, nsmall = 3)
    simulacion$lwr <- format(simulacion$lwr, nsmall = 3)
    simulacion$upr <- format(simulacion$upr, nsmall = 3)

Tras definir el formato numérico de las variables, se construirá la tabla, de nombre, por ejemplo, “tablasimula”:

  # Tabla

    tablasimula <- simulacion %>%
      kable(format = knitr.table.format,
            caption = "Simulación Modelo Rentabilidad Económica",
            col.names = c("Escenario",
                          "Resultado",
                          "Activo",
                          "Endeuda",
                          "Apalancamiento",
                          "Dimensión",
                          "Previsión",
                          "Inferior 95%",
                          "Superior 95%"),
        digits = 3) %>%
      kable_styling(full_width = F,
                    bootstrap_options = "striped",
                    "bordered",
                    "condensed",
                    position = "center",
                    font_size = 11) %>%
      row_spec(0, bold= T, align = "c") %>%
      row_spec(1:(nrow(simulacion)), bold= F, align = "c")

  tablasimula
Table 9.9: Table 9.10: Simulación Modelo Rentabilidad Económica
Escenario Resultado Activo Endeuda Apalancamiento Dimensión Previsión Inferior 95% Superior 95%
ESCENARIO A 2500 50000 20.250 0.238 GRANDE 6.711704 -0.2161380 13.63955
ESCENARIO B 2500 25000 50.000 50.000 MEDIA 7.596000 0.6413923 14.55061
ESCENARIO C 3000 5000 90.000 100.000 PEQUEÑA 8.852940 1.8523982 15.85348

9.3 Materiales para realizar las prácticas del capítulo.

En esta sección se muestran los links de acceso a los diferentes materiales (scripts, datos…) necesarios para llevar a cabo los contenidos prácticos del capítulo.

Datos (en formato Microsoft (R) Excel (R)):