Aplicación 2.3: Inferencia estadística
Modelo CAPM para las acciones del Banco Santander
En esta aplicación se estimará una versión simple del modelo CAPM para las acciones del Banco Santander:
\[erSAN_{t} = \beta_{1} + \beta_{2}\, erIBEX_{t} + e_{t}\]
donde \(erSAN\) representa el exceso de rendimiento de las acciones del Banco Santander, definido como la diferencia entre la rentabilidad de las acciones del banco y la rentabilidad de un activo libre de riesgo, en nuestro caso las letras del tesoro, y \(erIBEX\) es el exceso de rendimiento del mercado, representado por el índice IBEX35 de la bolsa española.
Como ejercicio, se pueden repetir todos los cálculos de la aplicación para analizar las empresas Telefónica e Inditex, cuyos datos están contenidos en el fichero usado en la aplicación.
Code
# Lectura de librerías
library(tidyverse)
# Lectura de datos
<- read_delim("data/CAPM_ESP.csv", delim = ";")
CAPM_ESP head(CAPM_ESP)
# A tibble: 6 × 6
date P_INDITEX P_SANTANDER P_TELEFONICA P_IBEX35 R_LT1Y
<date> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2001-05-22 2.94 6.55 14.1 9543. 4.54
2 2001-06-22 3.75 6.26 11.5 9602. 4.25
3 2001-07-22 3.61 5.76 10.5 8788. 4.12
4 2001-08-22 3.86 5.81 9.81 8565. 4.09
5 2001-09-22 3.06 4.08 8.67 8117. 3.84
6 2001-10-22 3.88 5.32 10.0 7169. 3.28
Code
tail(CAPM_ESP)
# A tibble: 6 × 6
date P_INDITEX P_SANTANDER P_TELEFONICA P_IBEX35 R_LT1Y
<date> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2017-08-22 33.7 5.44 9.16 10658. -0.41
2 2017-09-22 31.9 5.70 9.05 10180. -0.37
3 2017-10-22 31.0 5.6 8.86 10215. -0.35
4 2017-11-22 29.3 5.50 8.50 10358. -0.38
5 2017-12-22 29.6 5.59 8.26 10211. -0.37
6 2018-01-22 28.7 6.03 8.42 10411. -0.48
Code
# Transformación de variables
$r_SAN = c(NA,100*diff(log(CAPM_ESP$P_SANTANDER)))
CAPM_ESP$r_IBEX = c(NA,100*diff(log(CAPM_ESP$P_IBEX35)))
CAPM_ESP$r_LT1Y = CAPM_ESP$R_LT1Y/12
CAPM_ESP$er_SAN = CAPM_ESP$r_SAN - CAPM_ESP$r_LT1Y
CAPM_ESP$er_IBEX = CAPM_ESP$r_IBEX - CAPM_ESP$r_LT1Y
CAPM_ESP# Formato de series temporales
<- ts(CAPM_ESP,
ts_CAPM_ESP start = c(2001,5),
end = c(2018,1),
frequency = 12)
# Algunas gráficas de las variables básicas del modelo
ts.plot(ts_CAPM_ESP[,"P_IBEX35"])
Code
ts.plot(ts_CAPM_ESP[,"P_SANTANDER"])
Code
ts.plot(ts_CAPM_ESP[,"er_SAN"],ts_CAPM_ESP[,"er_IBEX"])
Code
# Modelo CAPM para las acciones del Banco Santander
<- lm(er_SAN ~ er_IBEX, data = ts_CAPM_ESP)
CAPM_SANTANDER summary(CAPM_SANTANDER)
Call:
lm(formula = er_SAN ~ er_IBEX, data = ts_CAPM_ESP)
Residuals:
Min 1Q Median 3Q Max
-33.114 -3.816 0.342 4.659 32.331
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.1461 0.6464 -0.226 0.821
er_IBEX 0.4632 0.1016 4.558 9.03e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.14 on 198 degrees of freedom
(1 observation deleted due to missingness)
Multiple R-squared: 0.09496, Adjusted R-squared: 0.09039
F-statistic: 20.77 on 1 and 198 DF, p-value: 9.03e-06
Code
# Intervalos de confianza para los parámetros estructurales del modelo
# Cálculo de forma 'manual'
<- 0.05
alpha <- df.residual(CAPM_SANTANDER) # grados de libertad
df <- qt(1-alpha/2, df)
t_c <- coef(CAPM_SANTANDER)[[1]] # estimación del parámetro beta1
b1 b1
[1] -0.1461435
Code
<- sqrt(vcov(CAPM_SANTANDER)[1,1]) # est. desviación típica beta1
seb1 seb1
[1] 0.6463839
Code
<- b1-t_c*seb1 # cota inferior
inf_b1 <- b1+t_c*seb1 # cota superior
sup_b1 inf_b1 ; sup_b1
[1] -1.420824
[1] 1.128537
Code
<- coef(CAPM_SANTANDER)[[2]] # estimación del parámetro beta2
b2 b2
[1] 0.4632261
Code
<- sqrt(vcov(CAPM_SANTANDER)[2,2]) # est. desviación típica beta2
seb2 seb2
[1] 0.1016302
Code
<- b2-t_c*seb2 # cota inferior
inf_b2 <- b2+t_c*seb2 # cota superior
sup_b2 inf_b2 ; sup_b2
[1] 0.2628095
[1] 0.6636427
Code
# Cálculo automático
confint(CAPM_SANTANDER)
2.5 % 97.5 %
(Intercept) -1.4208239 1.1285369
er_IBEX 0.2628095 0.6636427
Code
# Ajuste del modelo (R^2) y ANOVA
<- summary(CAPM_SANTANDER)
s_CAPM_SANTANDER print(s_CAPM_SANTANDER)
Call:
lm(formula = er_SAN ~ er_IBEX, data = ts_CAPM_ESP)
Residuals:
Min 1Q Median 3Q Max
-33.114 -3.816 0.342 4.659 32.331
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.1461 0.6464 -0.226 0.821
er_IBEX 0.4632 0.1016 4.558 9.03e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.14 on 198 degrees of freedom
(1 observation deleted due to missingness)
Multiple R-squared: 0.09496, Adjusted R-squared: 0.09039
F-statistic: 20.77 on 1 and 198 DF, p-value: 9.03e-06
Code
names(s_CAPM_SANTANDER)
[1] "call" "terms" "residuals" "coefficients"
[5] "aliased" "sigma" "df" "r.squared"
[9] "adj.r.squared" "fstatistic" "cov.unscaled" "na.action"
Code
<- s_CAPM_SANTANDER$r.squared
R2 R2
[1] 0.0949604
Code
anova(CAPM_SANTANDER)
Analysis of Variance Table
Response: er_SAN
Df Sum Sq Mean Sq F value Pr(>F)
er_IBEX 1 1735.4 1735.45 20.775 9.03e-06 ***
Residuals 198 16540.0 83.54
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
# Número de obsevaciones
<- nobs(CAPM_SANTANDER)
T T
[1] 200
Code
# Número de parámetros
<- T-df
K K
[1] 2
Code
# Estadístico F de significación global
<- (R2/(K-1))/((1-R2)/(T-K))
F_0 F_0
[1] 20.77496
Code
# Residuos del modelo
<- s_CAPM_SANTANDER$residuals
res <- ts(res, start=c(2001,5), end = c(2018,1), frequency = 12)
res plot(res)
Code
# Histograma de los residuos y comparación con la distribución normal
<- resid(CAPM_SANTANDER)
ehat <- mean(ehat)
ebar <- sd(ehat)
sdehat hist(ehat, col="grey", breaks = 20, freq=FALSE, main="",
ylab="density", xlab="ehat")
curve(dnorm(x, ebar, sdehat), col=2, add=TRUE, ylab="density", xlab="ehat")
Code
# Contrastes de hipótesis bilaterales
#
# "Valor alfa" del activo: beta1=0 versus beta1≠0
# Estadístico t
<- 0.05 # nivel de significación
alpha <- coef(CAPM_SANTANDER)[[1]] # estimación del parámetro beta1
b1 b1
[1] -0.1461435
Code
<- sqrt(vcov(CAPM_SANTANDER)[1,1]) # est. desviación típica beta1
seb1 seb1
[1] 0.6463839
Code
<- 0
c <- df.residual(CAPM_SANTANDER) # grados de libertad
df <- (b1-c)/seb1 # estadístico t
t_0 t_0
[1] -0.2260939
Code
# Método del valor crítico
<- qt(1-alpha/2, df) # valor crítico
t_c t_c
[1] 1.972017
Code
# Gráfico de la función de densidad de la distribución t de Student,
# del valor crítico y del estadístico t_0
curve(dt(x, df), -5, 5, ylab=" ", xlab="t")
abline(v=c(-t_c, t_c, t_0), col=c("red", "red", "blue"), lty=c(2,2,3))
legend("topleft", legend=c("-t_c", "t_c", "t_0"),
col=c("red", "red", "blue"), lty=c(2, 2, 3))
Code
# Método del P-valor
<- 2*(1-pt(abs(t_0), df))
p p
[1] 0.8213616
Code
# Gráfico de la función de densidad de la distribución t y del estadístico t_0
curve(dt(x, df), -5, 5, ylab=" ", xlab="t")
abline(v=c(t_0), col=c("blue"), lty=c(3))
legend("topleft", legend=c("t_0"), col=c("blue"), lty=c(3))
Code
# Estadístico F
library(car)
<- "(Intercept) = 0"
H_0 linearHypothesis(CAPM_SANTANDER, H_0, test="F")
Linear hypothesis test:
(Intercept) = 0
Model 1: restricted model
Model 2: er_SAN ~ er_IBEX
Res.Df RSS Df Sum of Sq F Pr(>F)
1 199 16544
2 198 16540 1 4.2702 0.0511 0.8214
Code
# Gráfico de la función de densidad de la distribución F de Fisher-Snedecor,
# del valor crítico y del estadístico F_0
<- linearHypothesis(CAPM_SANTANDER, H_0, test="F")[2,5] # estadístico F
F_0 F_0
[1] 0.05111845
Code
<- qf(1-alpha, 1, df) # valor crítico
F_c F_c
[1] 3.888853
Code
curve(df(x, 1, df), 0, 5, ylab=" ", xlab="F")
abline(v=c(F_0,F_c), col=c("blue","red"), lty=c(2,3))
legend("topleft", legend=c("F","Fcr"), col=c("blue","red"), lty=c(2,3))
Code
# "Valor beta" del activo: beta2=1 versus beta2≠1
# Estadístico t
<- 0.05 # nivel de significación
alpha <- coef(CAPM_SANTANDER)[[2]] # estimación del parámetro beta2
b2 b2
[1] 0.4632261
Code
<- sqrt(vcov(CAPM_SANTANDER)[2,2]) # est. desviación típica beta2
seb2 seb2
[1] 0.1016302
Code
<- 1
c <- df.residual(CAPM_SANTANDER) # grados de libertad
df <- (b2-c)/seb2 # estadístico t
t_0 t_0
[1] -5.281636
Code
# Método del valor crítico
<- qt(1-alpha/2, df) # valor crítico
t_c t_c
[1] 1.972017
Code
# Gráfico de la función de densidad de la distribución t, del valor crítico
# y del estadístico t_0
curve(dt(x, df), -6, 6, ylab=" ", xlab="t")
abline(v=c(-t_c, t_c, t_0), col=c("red", "red", "blue"), lty=c(2,2,3))
legend("topleft", legend=c("-t_c", "t_c", "t_0"),
col=c("red", "red", "blue"), lty=c(2, 2, 3))
Code
# Método del P-valor
<- 2*(1-pt(abs(t_0), df))
p p
[1] 3.357104e-07
Code
# Gráfico de la función de densidad de la distribución t y del estadístico t_0
curve(dt(x, df), -6, 6, ylab=" ", xlab="t")
abline(v=c(t_0), col=c("blue"), lty=c(3))
legend("topleft", legend=c("t_0"), col=c("blue"), lty=c(3))
Code
# Estadístico F
<- "er_IBEX = 1"
H_0 linearHypothesis(CAPM_SANTANDER,H_0,test="F")
Linear hypothesis test:
er_IBEX = 1
Model 1: restricted model
Model 2: er_SAN ~ er_IBEX
Res.Df RSS Df Sum of Sq F Pr(>F)
1 199 18870
2 198 16540 1 2330.3 27.896 3.357e-07 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
# Contraste conjunto de la hipótesis beta1=0, beta2=1
<- c("(Intercept) = 0", "er_IBEX = 1")
H_0 linearHypothesis(CAPM_SANTANDER,H_0,test="F")
Linear hypothesis test:
(Intercept) = 0
er_IBEX = 1
Model 1: restricted model
Model 2: er_SAN ~ er_IBEX
Res.Df RSS Df Sum of Sq F Pr(>F)
1 200 18872
2 198 16540 2 2331.7 13.956 2.136e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
# Contrastes unilaterales
#
# ¿Activo defensivo o agresivo?: beta2≤1 versus beta2>1
<- 1
c <- 0.05
alpha <- (b2-c)/seb2
t_0 t_0
[1] -5.281636
Code
# Método del valor crítico
<- qt(1-alpha, df) # alpha no se divide por 2
t_c t_c
[1] 1.652586
Code
curve(dt(x, df), -6, 6, ylab=" ", xlab="t")
abline(v=c(t_c, t_0), col=c("red", "blue"), lty=c(2, 3))
legend("topleft", legend=c("t_c", "t_0"), col=c("red", "blue"), lty=c(2, 3))
Code
# Método del P-valor
<- 1-pt(t_0, df)
p p
[1] 0.9999998
Code
# ¿Activo ultra-agresivo?: beta2≥2 versus beta2<2
<- 2
c <- 0.05
alpha <- (b2-c)/seb2
t_0 t_0
[1] -15.12123
Code
# Método del valor crítico
<- qt(alpha, df) # alpha no se divide por 2
t_c t_c
[1] -1.652586
Code
curve(dt(x, df), -20, 20, ylab=" ", xlab="t")
abline(v=c(t_c, t_0), col=c("red", "blue"), lty=c(2, 3))
legend("topleft", legend=c("t_c", "t_0"), col=c("red", "blue"), lty=c(2, 3))
Code
# Método del P-valor
<- pt(t_0, df)
p p
[1] 3.786304e-35
Code
# NOTA:
# Distribuciones de probabilidad en R ->
# https://rstudio.github.io/r-manuals/r-intro/Probability-distributions.html
Code
# Lectura de librerías
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
import statsmodels.formula.api as smf
# Librería SciPy (stats): https://docs.scipy.org/doc/scipy/tutorial/stats.html
import scipy as sp
# Lectura de datos
= pd.read_csv('data/CAPM_ESP.csv', delimiter=';',
CAPM_ESP =['date'], index_col='date')
parse_dates CAPM_ESP.head()
P_INDITEX P_SANTANDER P_TELEFONICA P_IBEX35 R_LT1Y
date
2001-05-22 2.940 6.5528 14.1485 9542.7 4.54
2001-06-22 3.746 6.2643 11.4884 9601.8 4.25
2001-07-22 3.610 5.7639 10.4938 8787.6 4.12
2001-08-22 3.860 5.8110 9.8076 8564.7 4.09
2001-09-22 3.060 4.0801 8.6741 8117.3 3.84
Code
CAPM_ESP.tail()
P_INDITEX P_SANTANDER P_TELEFONICA P_IBEX35 R_LT1Y
date
2017-09-22 31.905 5.6989 9.047 10179.8 -0.37
2017-10-22 30.990 5.6000 8.864 10214.7 -0.35
2017-11-22 29.280 5.4970 8.497 10357.8 -0.38
2017-12-22 29.615 5.5940 8.255 10211.3 -0.37
2018-01-22 28.720 6.0340 8.417 10411.4 -0.48
Code
# Transformación de variables
def LogDiff(x):
= 100*np.log(x/x.shift(1))
x_diff return x_diff
'r_IBEX'] = LogDiff(CAPM_ESP['P_IBEX35'])
CAPM_ESP['r_SAN'] = LogDiff(CAPM_ESP['P_SANTANDER'])
CAPM_ESP['r_LT1Y'] = CAPM_ESP['R_LT1Y']/12
CAPM_ESP['er_IBEX'] = LogDiff(CAPM_ESP['P_IBEX35']) - CAPM_ESP['R_LT1Y']/12
CAPM_ESP['er_SAN'] = LogDiff(CAPM_ESP['P_SANTANDER']) - CAPM_ESP['R_LT1Y']/12
CAPM_ESP[# Gráficas de las variables del modelo
1)
plt.figure('er_IBEX'], label='Exceso de rendimiento IBEX35')
plt.plot(CAPM_ESP['er_SAN'], label='Exceso de rendimiento Banco Santander')
plt.plot(CAPM_ESP['Date')
plt.xlabel('%')
plt.ylabel('Variables del modelo CAPM para el Banco Santander')
plt.title(
plt.legend() plt.show()
Code
# Diagrama de puntos asociado a la regresión
2)
plt.figure('er_IBEX'], CAPM_ESP['er_SAN'])
plt.scatter(CAPM_ESP['er_IBEX')
plt.xlabel('er_SAN')
plt.ylabel('Exceso de rendimiento de SAN frente a IBEX')
plt.title( plt.show()
Code
# Modelo CAPM para las acciones del Banco Santander
= 'er_SAN ~ er_IBEX'
formula = smf.ols(formula, CAPM_ESP).fit()
CAPM_SANTANDER print(CAPM_SANTANDER.summary())
OLS Regression Results
==============================================================================
Dep. Variable: er_SAN R-squared: 0.095
Model: OLS Adj. R-squared: 0.090
Method: Least Squares F-statistic: 20.77
Date: Sun, 09 Feb 2025 Prob (F-statistic): 9.03e-06
Time: 13:11:35 Log-Likelihood: -725.31
No. Observations: 200 AIC: 1455.
Df Residuals: 198 BIC: 1461.
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
Intercept -0.1461 0.646 -0.226 0.821 -1.421 1.129
er_IBEX 0.4632 0.102 4.558 0.000 0.263 0.664
==============================================================================
Omnibus: 23.804 Durbin-Watson: 2.378
Prob(Omnibus): 0.000 Jarque-Bera (JB): 82.551
Skew: -0.352 Prob(JB): 1.19e-18
Kurtosis: 6.068 Cond. No. 6.36
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Code
# Intervalos de confianza para los parámetros estructurales del modelo
# Automático
CAPM_SANTANDER.conf_int()
0 1
Intercept -1.420824 1.128537
er_IBEX 0.262809 0.663643
Code
# Manualmente
= np.array(sp.stats.t.interval(0.95,CAPM_SANTANDER.df_resid))
t_c 0]+CAPM_SANTANDER.bse[0]*t_c CAPM_SANTANDER.params[
array([-1.42082387, 1.12853695])
Code
1]+CAPM_SANTANDER.bse[1]*t_c CAPM_SANTANDER.params[
array([0.26280949, 0.66364269])
Code
# ANOVA
sm.stats.anova_lm(CAPM_SANTANDER)
df sum_sq mean_sq F PR(>F)
er_IBEX 1.0 1735.445734 1735.445734 20.774958 0.000009
Residual 198.0 16540.021306 83.535461 NaN NaN
Code
# Ajuste del modelo (R^2)
CAPM_SANTANDER.rsquared
0.09496040403004358
Code
# Estadístico F de significación global
CAPM_SANTANDER.fvalue, CAPM_SANTANDER.f_pvalue
(20.774958445655425, 9.0300657558589e-06)
Code
# Estadístico F y R^2
= (CAPM_SANTANDER.rsquared/(CAPM_SANTANDER.df_model))/((1-CAPM_SANTANDER.rsquared)/(CAPM_SANTANDER.df_resid))
F_0 1-sp.stats.f.cdf(F_0, CAPM_SANTANDER.df_model, CAPM_SANTANDER.df_resid) F_0,
(20.774958445655436, 9.030065755810668e-06)
Code
# Número de obsevaciones
CAPM_SANTANDER.nobs
200.0
Code
# Número de parámetros
1+CAPM_SANTANDER.df_model
2.0
Code
# Errores estimados (residuos) del modelo
3)
plt.figure(='res')
plt.plot(CAPM_SANTANDER.resid, label'Date')
plt.xlabel('Residuos del modelo CAPM para el Banco Santander')
plt.title(
plt.legend() plt.show()
Code
# Histograma de los residuos y comparación con la distribución normal
= np.mean(CAPM_SANTANDER.resid)
ebar = np.std(CAPM_SANTANDER.resid)
sdehat 4)
plt.figure(= plt.subplots()
fig, ax =20, density=True,
ax.hist(CAPM_SANTANDER.resid, bins='white', linewidth=1.2)
edgecolor= np.linspace(min(CAPM_SANTANDER.resid), max(CAPM_SANTANDER.resid), 100)
x ='black',
ax.plot(x, sp.stats.norm.pdf(x, ebar, sdehat), color= 'Distribución normal')
label 'Residuos del modelo CAPM para el Banco Santander')
plt.title('Residuos')
plt.xlabel('Densidad')
plt.ylabel(='best')
plt.legend(loc
plt.tight_layout() plt.show()
Code
# Contrastes de hipótesis bilaterales
# beta1=0 versus beta1≠0
# Estadístico t
= (CAPM_SANTANDER.params['Intercept']-0)/CAPM_SANTANDER.bse['Intercept']
t_stat 2*sp.stats.t.cdf(t_stat, CAPM_SANTANDER.df_resid) t_stat,
(-0.22609389919232573, 0.8213616110920402)
Code
# Estadístico F y P-valor
= 'Intercept = 0'
H_0 = CAPM_SANTANDER.f_test(H_0)
F_0 print(F_0)
<F test: F=0.05111845125198955, p=0.8213616110920228, df_denom=198, df_num=1>
Code
# beta2=1 versus beta2≠1
# Estadístico t y P-valor
= (CAPM_SANTANDER.params['er_IBEX']-1)/CAPM_SANTANDER.bse['er_IBEX']
t_0 2*(1-sp.stats.t.cdf(abs(t_0), CAPM_SANTANDER.df_resid)) t_0,
(-5.281635967748109, 3.3571043878133366e-07)
Code
# Estadístico F y P-valor
= 'er_IBEX = 1'
H_0 = CAPM_SANTANDER.f_test(H_0)
F_0 print(F_0)
<F test: F=27.895678495810504, p=3.357104388598483e-07, df_denom=198, df_num=1>
Code
# Contraste conjunto de la hipótesis beta1=0, beta2=1
# Estadístico F y P-valor
= 'Intercept = 0 , er_IBEX = 1'
H_0 = CAPM_SANTANDER.f_test(H_0)
F_0 print(F_0)
<F test: F=13.956448346309095, p=2.1364485558886642e-06, df_denom=198, df_num=2>
Code
# Contrastes unilaterales
# beta2≤1 versus beta2>1
# Estadístico t y P-valor
= (CAPM_SANTANDER.params['er_IBEX']-1)/CAPM_SANTANDER.bse['er_IBEX']
t_0 1-sp.stats.t.cdf(t_0, CAPM_SANTANDER.df_resid) t_0,
(-5.281635967748109, 0.9999998321447806)
Code
# beta2≥2 versus beta2<2
# Estadístico t y P-valor
= (CAPM_SANTANDER.params['er_IBEX']-2)/CAPM_SANTANDER.bse['er_IBEX']
t_0 t_0, sp.stats.t.cdf(t_0, CAPM_SANTANDER.df_resid)
(-15.121227447088602, 3.786304271337718e-35)
Code
# NOTA:
# Distribuciones de probabilidad en Python (librería SciPy) ->
# https://docs.scipy.org/doc/scipy/reference/stats.html