import pandas as pd
import numpy as np
#para montar en drive
from google.colab import drive
import os
drive.mount('/content/gdrive')
# Establecer ruta de acceso en drive
import os
print(os.getcwd())
os.chdir("/content/gdrive/My Drive")
! pip install pingouin
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
from statsmodels.formula.api import ols
import statsmodels
from scipy import stats
from pingouin import pairwise_ttests #this is for performing the pairwise tests
Drive already mounted at /content/gdrive; to attempt to forcibly remount, call drive.mount("/content/gdrive", force_remount=True).
/content/gdrive/My Drive
Collecting pingouin
Downloading pingouin-0.5.0.tar.gz (182 kB)
|████████████████████████████████| 182 kB 5.0 MB/s
Requirement already satisfied: numpy>=1.19 in /usr/local/lib/python3.7/dist-packages (from pingouin) (1.19.5)
Collecting scipy>=1.7
Downloading scipy-1.7.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (38.1 MB)
|████████████████████████████████| 38.1 MB 1.3 MB/s
Requirement already satisfied: pandas>=1.0 in /usr/local/lib/python3.7/dist-packages (from pingouin) (1.1.5)
Requirement already satisfied: matplotlib>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from pingouin) (3.2.2)
Requirement already satisfied: seaborn>=0.9.0 in /usr/local/lib/python3.7/dist-packages (from pingouin) (0.11.2)
Collecting statsmodels>=0.12.0
Downloading statsmodels-0.13.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.8 MB)
|████████████████████████████████| 9.8 MB 52.1 MB/s
Requirement already satisfied: scikit-learn in /usr/local/lib/python3.7/dist-packages (from pingouin) (1.0.1)
Collecting pandas_flavor>=0.2.0
Downloading pandas_flavor-0.2.0-py2.py3-none-any.whl (6.6 kB)
Collecting outdated
Downloading outdated-0.2.1-py3-none-any.whl (7.5 kB)
Requirement already satisfied: tabulate in /usr/local/lib/python3.7/dist-packages (from pingouin) (0.8.9)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.2->pingouin) (0.11.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.2->pingouin) (1.3.2)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.2->pingouin) (3.0.6)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.2->pingouin) (2.8.2)
Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas>=1.0->pingouin) (2018.9)
Requirement already satisfied: xarray in /usr/local/lib/python3.7/dist-packages (from pandas_flavor>=0.2.0->pingouin) (0.18.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib>=3.0.2->pingouin) (1.15.0)
Requirement already satisfied: patsy>=0.5.2 in /usr/local/lib/python3.7/dist-packages (from statsmodels>=0.12.0->pingouin) (0.5.2)
Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from outdated->pingouin) (2.23.0)
Collecting littleutils
Downloading littleutils-0.2.2.tar.gz (6.6 kB)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->outdated->pingouin) (2.10)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->outdated->pingouin) (2021.10.8)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->outdated->pingouin) (1.24.3)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->outdated->pingouin) (3.0.4)
Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->pingouin) (3.0.0)
Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->pingouin) (1.1.0)
Requirement already satisfied: setuptools>=40.4 in /usr/local/lib/python3.7/dist-packages (from xarray->pandas_flavor>=0.2.0->pingouin) (57.4.0)
Building wheels for collected packages: pingouin, littleutils
Building wheel for pingouin (setup.py) ... done
Created wheel for pingouin: filename=pingouin-0.5.0-py3-none-any.whl size=193661 sha256=99bb9c6a8bb499301b95444bad2d105f67533ab0b0f555fd1bcdf81af435abb2
Stored in directory: /root/.cache/pip/wheels/14/46/f9/cedd81d68d2515c24bbbd000d5b347e4fe092ccc4b568f7f70
Building wheel for littleutils (setup.py) ... done
Created wheel for littleutils: filename=littleutils-0.2.2-py3-none-any.whl size=7048 sha256=18d385ac1c4f3103b43d705ab7e4fb3b193f10562bd786308b860f3dcfcdd14f
Stored in directory: /root/.cache/pip/wheels/d6/64/cd/32819b511a488e4993f2fab909a95330289c3f4e0f6ef4676d
Successfully built pingouin littleutils
Installing collected packages: scipy, littleutils, statsmodels, pandas-flavor, outdated, pingouin
Attempting uninstall: scipy
Found existing installation: scipy 1.4.1
Uninstalling scipy-1.4.1:
Successfully uninstalled scipy-1.4.1
Attempting uninstall: statsmodels
Found existing installation: statsmodels 0.10.2
Uninstalling statsmodels-0.10.2:
Successfully uninstalled statsmodels-0.10.2
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
albumentations 0.1.12 requires imgaug<0.2.7,>=0.2.5, but you have imgaug 0.2.9 which is incompatible.
Successfully installed littleutils-0.2.2 outdated-0.2.1 pandas-flavor-0.2.0 pingouin-0.5.0 scipy-1.7.3 statsmodels-0.13.1
bank = pd.read_csv("bank-full.csv")
print(bank.shape)
bank.head()
(45211, 17)
age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 58 | management | married | tertiary | no | 2143 | yes | no | unknown | 5 | may | 261 | 1 | -1 | 0 | unknown | no |
1 | 44 | technician | single | secondary | no | 29 | yes | no | unknown | 5 | may | 151 | 1 | -1 | 0 | unknown | no |
2 | 33 | entrepreneur | married | secondary | no | 2 | yes | yes | unknown | 5 | may | 76 | 1 | -1 | 0 | unknown | no |
3 | 47 | blue-collar | married | unknown | no | 1506 | yes | no | unknown | 5 | may | 92 | 1 | -1 | 0 | unknown | no |
4 | 33 | unknown | single | unknown | no | 1 | no | no | unknown | 5 | may | 198 | 1 | -1 | 0 | unknown | no |
Las características relevantes que usaremos en este caso son:
bank.describe()
age | balance | day | duration | campaign | pdays | previous | |
---|---|---|---|---|---|---|---|
count | 45211.000000 | 45211.000000 | 45211.000000 | 45211.000000 | 45211.000000 | 45211.000000 | 45211.000000 |
mean | 40.936210 | 1362.272058 | 15.806419 | 258.163080 | 2.763841 | 40.197828 | 0.580323 |
std | 10.618762 | 3044.765829 | 8.322476 | 257.527812 | 3.098021 | 100.128746 | 2.303441 |
min | 18.000000 | -8019.000000 | 1.000000 | 0.000000 | 1.000000 | -1.000000 | 0.000000 |
25% | 33.000000 | 72.000000 | 8.000000 | 103.000000 | 1.000000 | -1.000000 | 0.000000 |
50% | 39.000000 | 448.000000 | 16.000000 | 180.000000 | 2.000000 | -1.000000 | 0.000000 |
75% | 48.000000 | 1428.000000 | 21.000000 | 319.000000 | 3.000000 | -1.000000 | 0.000000 |
max | 95.000000 | 102127.000000 | 31.000000 | 4918.000000 | 63.000000 | 871.000000 | 275.000000 |
El año pasado, el saldo promedio de toda la población de clientes del banco fue de \$1341.12. ¿Cuánto más alto es el saldo promedio de la muestra este año? ¿Es esta diferencia significativa?
Respuesta.
bank['balance'].mean()
1362.2720576850766
Esta es una diferencia de \$1362.27 - \$1341.12 = \$22.15. La dirección del banco cree que el comportamiento del consumidor podría haber cambiado ligeramente y que, en promedio, los clientes mantienen más dinero en su cuenta bancaria.
Sin embargo, esta diferencia podría deberse a una variación estadística debido a la variabilidad del muestreo. Necesitaríamos realizar una prueba más rigurosa para determinar si esta diferencia es realmente significativa después de tener en cuenta dicha variabilidad.
El procedimiento que usamos para ayudarnos a decidir si una diferencia entre la media $ \mu $ de una población y un valor de referencia $\mu_0$ es estadísticamente significativa se llama prueba de hipótesis. En nuestro contexto, $\mu$ es el saldo bancario promedio de los clientes este año y $ \mu_0$ es su saldo bancario promedio el año pasado.
El primer paso es definir la hipótesis nula (a menudo indicada como $H_0$). Para nosotros, la hipótesis nula siempre corresponderá a la hipótesis de no cambio; es decir, el statu quo sigue siendo válido. Formalmente, esto se escribe como: $H_0:\mu=\mu_0$.
En nuestro caso, $H_0$ sería equivalente al plantear la hipótesis de que el saldo promedio para toda la población de clientes $\mu$ es el mismo que el saldo promedio del año pasado $\mu_0 =\$1341.12$. Deseamos probar si $H_0$ es incorrecto; es decir, si $\mu$ es diferente de $\mu_0 = \$ 1341.12$.
Antes de profundizar en el caso, piense en la siguiente pregunta:
Suponga que usted es Ronald Fisher en la década de 1920 y descubriera la prueba de hipótesis por primera vez. ¿Cómo probaría la veracidad de la hipótesis nula $H_0$?
Respuesta:
Podemos usar un enfoque similar al del sistema judicial en todo el mundo: Uno se considera inocente hasta que se demuestre lo contrario. Aceptaremos la hipótesis nula a menos que encontremos suficiente evidencia en su contra. Nótese que este enfoque, como es el caso del sistema judicial, no es perfecto. Incluso si no encontramos evidencia que vaya en contra de la hipótesis nula, la hipótesis nula puede resultar errónea.
In opposition to the null hypothesis, we define an alternative hypothesis (often indicated with $H_1$ or $H_a$) to challenge the status quo. We can have three different ways to define an alternative hypothesis:
$H_a: \mu \neq \mu_0$ (two-sided test)
$H_a: \mu > \mu_0$ (one-sided test)
$H_a: \mu < \mu_0$ (one-sided test)
La prueba estadística nos ayudará a decidir si hay suficiente evidencia para rechazar la hipótesis nula a favor de una alternativa.
Volviendo a nuestro caso, supongamos que deseamos realizar una prueba estadística para evaluar la hipótesis de gestión:
$$ H_0: \mu= 1341.12$$$$ H_a: \mu \neq 1341.12$$Hay dos resultados posibles para esta prueba: (1)Concluimos que $H_0$ es falso y decimos que rechazamos $H_0$. En este caso concluiremos que existe evidencia estadística para la alternativa $H_a$ y que el saldo bancario de los clientes este año es de hecho diferente de 1341,12 USD. O (2) no rechazamos $H_0$. En este caso, concluimos que no hay suficiente evidencia estadística para decir con certeza que $H_0$ es falso. Observe que en el segundo caso no podemos decir que la hipótesis original sea verdadera. (De hecho, no existe ninguna prueba que le diga que una hipótesis es verdadera. ¿Por qué cree que es así?)
Podemos usar Student's $t$-prueba para realizar una prueba de hipótesis para la media de una sola población. El siguiente comando nos permite ejecutar esta prueba:
stats.ttest_1samp(bank['balance'], popmean=1341.12)
Ttest_1sampResult(statistic=1.477137017325908, pvalue=0.13964587808442475)
Nota: Si la alternativa es unilateral, diga $ H_a: \mu>\mu_0$ o $H_a:\mu <\mu_0$. Luego, simplemente ejecute la prueba de dos lados y luego divida el valor $p$-por la mitad.
T,p = stats.ttest_1samp(bank['balance'], popmean=1341.122)
p_value = p/2
p_value
0.06984165666422609
Nos gustaría probar estadísticamente si las medias de los dos grupos son diferentes entre sí; es decir, si la diferencia entre el saldo medio en los grupos con un préstamo ($\mu_1 $) es diferente del saldo medio en el grupo sin préstamo ($\mu_2$). El procedimiento de prueba que describimos también se puede utilizar para responder a esta pregunta:
$$ H_0: \mu_1=\mu_2$$$$H_a: \mu_1 \neq \mu_2$$Obtenemos lo siguiente
loans=bank[bank.loan=="yes"].balance
no_loans=bank[bank.loan=="no"].balance
statistic, pvalue = stats.ttest_ind(loans,no_loans, equal_var=False)
print('Estadistico:',round(statistic,2),'p-valor:', round(pvalue,2))
Estadistico: -25.18 p-valor: 0.0
from scipy.stats import t
m = bank.balance.mean()
s = bank.balance.std()
dof = len(bank.balance)-1
confianza = 0.95
t_crit = np.abs(t.ppf((1-confianza)/2,dof)) # Valor critico
print(t_crit)
(m-s*t_crit/np.sqrt(len(bank.balance)), m+s*t_crit/np.sqrt(len(bank.balance))) # Intervalo
1.960016458188857
(1334.205344373427, 1390.3387709967262)
loans.mean(),no_loans.mean()
(774.3099116510215, 1474.4536307846288)
import numpy as np, statsmodels.stats.api as sms
X1, X2 = bank[bank.loan=="yes"].balance,bank[bank.loan=="no"].balance
cm = sms.CompareMeans(sms.DescrStatsW(X1), sms.DescrStatsW(X2))
print(cm.tconfint_diff(usevar='unequal'))
(-754.6437941384797, -645.6436441287349)
Hasta ahora, solo hemos analizado los datos de una de nuestras cinco empresas. Sigamos adelante y combinemos los cinco archivos CSV para analizar las cinco empresas juntas. Esto también reducirá la cantidad de trabajo de programación requerido, ya que el código se compartirá entre las cinco empresas.
Una forma de realizar esta tarea de agregación es utilizar el método pd.concat ()
de pandas
. Una entrada en este método puede ser una lista de DataFrames que le gustaría concatenar. Usaremos un bucle for para recorrer cada símbolo bursátil, cargar el archivo CSV correspondiente y luego agregar el resultado a una lista que luego se agrega usando pd.concat()
. Echemos un vistazo a cómo se hace esto.
print("Definiendo los simbolos de stock")
symbol_data_to_load = ['D','EXC','NEE','SO','DUK']
list_of_df = []
# Ciclo sobre simbolos
#llenar la lsita de dataframes
print(" --- Inicio de Loop --- ")
for i in symbol_data_to_load:
print("Procesando Simbolo: " + i)
temp_df = pd.read_csv(i+'.csv',sep=',')
temp_df['Volume_Millions'] = temp_df['Volume'] / 1000000.0
temp_df['Symbol'] = i # Agregar nueva columna con el simbolo
list_of_df.append(temp_df)
print(" --- Completado loop simbolos --- ")
# Combinar en un Dataframe unico usando concat
#permite pegar los dataframes de la lista
print("Agregando la data")
agg_df = pd.concat(list_of_df, axis=0)
# Agregar estadisticas de retorno y volatilidad
# es mas rápido agregarlo al dataframe que a cada uno de los registros
print('Calculando estadisticas finales')
agg_df['VolStat'] = (agg_df['High'] - agg_df['Low']) / agg_df['Open']
agg_df['Return'] = (agg_df['Close'] / agg_df['Open']) - 1.0
print("agg_df DataFrame dimension (filas, columnas): ")
print(agg_df.shape)
print("Head del DataFrame agg_df: ")
agg_df.head()
#print("agg_df['Symbol'].unique()")
Definiendo los simbolos de stock --- Inicio de Loop --- Procesando Simbolo: D Procesando Simbolo: EXC Procesando Simbolo: NEE Procesando Simbolo: SO Procesando Simbolo: DUK --- Completado loop simbolos --- Agregando la data Calculando estadisticas finales agg_df DataFrame dimension (filas, columnas): (6295, 11) Head del DataFrame agg_df:
Date | Open | High | Low | Close | Adj Close | Volume | Volume_Millions | Symbol | VolStat | Return | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2014-07-28 | 69.750000 | 71.059998 | 69.750000 | 70.879997 | 57.963978 | 1806400 | 1.8064 | D | 0.018781 | 0.016201 |
1 | 2014-07-29 | 70.669998 | 70.980003 | 69.930000 | 69.930000 | 57.187099 | 2231100 | 2.2311 | D | 0.014858 | -0.010471 |
2 | 2014-07-30 | 70.000000 | 70.660004 | 68.400002 | 68.970001 | 56.402020 | 2588900 | 2.5889 | D | 0.032286 | -0.014714 |
3 | 2014-07-31 | 68.629997 | 68.849998 | 67.580002 | 67.639999 | 55.314388 | 3266900 | 3.2669 | D | 0.018505 | -0.014425 |
4 | 2014-08-01 | 67.330002 | 68.410004 | 67.220001 | 67.589996 | 55.273487 | 2601800 | 2.6018 | D | 0.017674 | 0.003861 |
agg_df.Symbol.unique()
array(['D', 'EXC', 'NEE', 'SO', 'DUK'], dtype=object)
agg_df1=agg_df[['Date','Symbol','VolStat']].groupby(by=['Date','Symbol']).mean().reset_index()
agg_df1['Date']=pd.to_datetime(agg_df1['Date'])
agg_df1.head()
Date | Symbol | VolStat | |
---|---|---|---|
0 | 2014-07-28 | D | 0.018781 |
1 | 2014-07-28 | DUK | 0.017051 |
2 | 2014-07-28 | EXC | 0.023878 |
3 | 2014-07-28 | NEE | 0.016858 |
4 | 2014-07-28 | SO | 0.018153 |
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(15,8))
sns.lineplot(x= agg_df1.Date, y= agg_df1.VolStat, hue=agg_df1.Symbol)
plt.xlabel('Fecha')
plt.ylabel('VolStat')
plt.title('Comparacion de volatilidad relativa vs Fecha')
Text(0.5, 1.0, 'Comparacion de volatilidad relativa vs Fecha')
agg_df2=agg_df[['Date','Symbol','Return']].groupby(by=['Date','Symbol']).mean().reset_index()
agg_df2['Date']=pd.to_datetime(agg_df1['Date'])
agg_df2.head()
Date | Symbol | Return | |
---|---|---|---|
0 | 2014-07-28 | D | 0.016201 |
1 | 2014-07-28 | DUK | 0.014732 |
2 | 2014-07-28 | EXC | 0.017192 |
3 | 2014-07-28 | NEE | 0.011272 |
4 | 2014-07-28 | SO | 0.016585 |
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(15,8))
sns.lineplot(x= agg_df2.Date, y= agg_df2.Return, hue=agg_df2.Symbol)
plt.xlabel('Fecha')
plt.ylabel('VolStat')
plt.title('Comparacion de retorno vs Fecha')
Text(0.5, 1.0, 'Comparacion de retorno vs Fecha')
plt.figure(figsize=(18,8))
plt.subplot(121)
sns.lineplot(x= agg_df1.Date, y= agg_df1.VolStat, hue=agg_df1.Symbol)
plt.xlabel('Fecha')
plt.ylabel('VolStat')
plt.title('Comparacion de volatilidad relativa vs Fecha')
plt.subplot(122)
sns.lineplot(x= agg_df2.Date, y= agg_df2.Return, hue=agg_df2.Symbol)
plt.xlabel('Fecha')
plt.ylabel('VolStat')
plt.title('Comparacion de retorno vs Fecha')
Text(0.5, 1.0, 'Comparacion de retorno vs Fecha')
Donde invertirian?
Cada acción exhibe un agrupamiento de volatilidad: los períodos de alta volatilidad tienden a agruparse. Cada una de las acciones experimentó una alta volatilidad en momentos relativamente similares, lo que sugiere que algún factor de mercado más amplio puede estar afectando al sector energético.