Se realiza este análisis respondiendo a la convocatoria del concurso Data Jam CRC y dar respuesta a los dos retos plnateados. Después de analizar la data y los retos, el equipo se plantea las siguientes preguntas de investigación, las cuales se espera ser respondidads haciendo uso de diferentes técnicas de la ciencia de datos:
¿Cómo las variables socioeconómicas y macroeconómicas afectan la adopción digital en las diferentes regiones de Colombia?
¿Cómo se relacionan las dimensiones más determinantes de la calidad en el servicio de los operadores de telecomunicaciones con la portabilidad numérica neta?
#Importación de librerías principales a usar:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os
import seaborn as sns
os.listdir(os.path.join(os.getcwd()))
#Importación de la data de accesos web, debido a su tamaño no es adecuado ser procesada en hojas de cálculo:
list_archivos = os.listdir(os.path.join(os.getcwd()))
ruta_accesos= os.path.join(os.getcwd(),'ACCESOS_INTERNET_FIJO.csv')
#Importar y limpiar la data de accesos web por departamento y municipio:
data_acces= pd.read_csv(ruta_accesos, sep=(';'),engine='python')
data_acces1 = data_acces.groupby(['ANO','DESC_DEPARTAMENTO','DESC_MUNICIPIO']).aggregate({'CANTIDAD_ACCESOS': np.sum})
data_acces2 = data_acces1.reset_index()
#Se observa la data:
data_acces2.head(10)
data_acces2.describe()
data_acces2.info()
data_acces2.DESC_DEPARTAMENTO.unique()
Se observa que se debe arreglar la data, ya que a lo largo del tiempo el nombre de algunos departamentos cambió. Es el caso para BOGOTA y para SAN ANDRES. Por otra parte, aparece el departamento "Colombia" el cual caramente no hace referencia a ningún departamento.
#Se extrae la información para ser publicada en tableau:
data_acces2.to_excel(os.path.join(os.getcwd(),'Data_tableau','accesos_web_municipios.xlsx'), index= False)
#Cargar la data:
ruta_corr= os.path.join(os.getcwd(),'Data_limpia','Pentracion_digital_correlacion.xlsx')
data_corr= pd.read_excel(ruta_corr, sheet='Hoja1')
#Observamos una muestra de los datos:
data_corr.head(10)
#Coeficiente de correlación entre las variables de computadores, uso de internet y acceso web:
df_corr= data_corr[['computador_pobla','internet_pobla','y_accesos_pobla']].copy()
df_corr_nombres= ['computador_pobla','internet_pobla','y_accesos_pobla']
df_corr.corr(method='pearson')
#Lo vemos matricialmente:
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(df_corr.corr(), interpolation='nearest')
fig.colorbar(cax)
ax.set_xticklabels(['']+df_corr_nombres, rotation = 45)
ax.set_yticklabels(['']+df_corr_nombres)
plt.show()
#Se grafica el comportamiento de la relación entre el uso del computador e internet con el acceso web:
sns.pairplot(data_corr, x_vars=['computador_pobla','internet_pobla'],y_vars='y_accesos_pobla', height=5, kind='reg')
Como se puede observar la correlación entre las variables es muy fuerte, razón por la cual es posible hacer el supuesto de que según como sea el comportamiento de accesos a la web, de manera similar será la proporción de la población que usará el computador o usará internet, dos indicadores clave para medir la penetración digital. Es así como la data de postdata se vuelve un insumo valiso, considerando además que los datos de uso de computador e internet son pobres en calidad, y disponibilidad para todas los departamentos del pais, mientras que la data de accesos si está disponible. Los accesos a la web sobre población será la variable dependiente de nuestro modelo para medir adopción de la tecnología en los departamentos.
#Cargue de información para realizar la regresión
ruta_reg= os.path.join(os.getcwd(),'Data_limpia','Variables_regresion.xlsx')
data_reg= pd.read_excel(ruta_reg, sheet='Hoja1')
#Se crean variables dummy para manejar los departamentos:
data_reg_dummy = pd.get_dummies(data_reg, columns=['departamento'])
#Ahora se elimina la dummy del "departamento" Total nacional, ya que será absorbido por el beta cero de la regresión
#al considerar que si todas las variables dummies son cero, significa que será el valor del total nacional. Se eliminan
#otras variables que no serán usadas en esta etapa del proceso.
data_reg_dummy = data_reg_dummy.drop(['anio', 'concatenado','departamento_Total Nacional','y_accesos'], axis=1)
#Observamos la data:
data_reg_dummy.head(10)
#separamos la data:
X= data_reg_dummy.copy()
X= X.drop(['y_accesos_pobla'], axis=1)
Y= data_reg_dummy['y_accesos_pobla']
#Verificamos correlaciones de variables independientes para evadir la colinealidad:
X_corr = X.copy()
X_corr = X_corr[['pib_percapita', 'desempleo', 'poblacion', 'ninios_mujer','esperanza_vida',
'tasa_migracion', 'mortalidad', 'natalidad','mortalidad_infantil', 'gini',
'tasa_min_exp', 'trm']]
dframe_xcorr=X_corr.corr(method='pearson')
dframe_xcorr
El analisis muestra que hay una alta correlación entre las variables natalidad, mortalidad_infantil y niños por mujer. Por lo que será necesario transformarlas o eliminarlas.
#Eliminamos ninios_mujer y obtenemos las correlaciones:
X_corr = X_corr.drop(['ninios_mujer'],axis=1)
dframe_xcorr1=X_corr.corr(method='pearson')
dframe_xcorr1
#Aun sigue habiendo demasiada correlación entre natalidad y mortalidad infantil.
#Así que se buscará combinar estas dos variables como mortalidad_infantil/natalidad:
X_corr['mortalidad_ratio']= X_corr['mortalidad_infantil']/X_corr['natalidad']
X_corr = X_corr.drop(['mortalidad_infantil','natalidad'],axis=1)
#obtenemos las correlaciones:
dframe_xcorr2=X_corr.corr(method='pearson')
dframe_xcorr2
#Ahora se hace el ajuste en X para proceder con el modelo:
X['mortalidad_ratio']= X['mortalidad_infantil']/X['natalidad']
X= X.drop(['ninios_mujer','mortalidad_infantil','natalidad'],axis=1)
#Implementamos la regresion lineal:
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(X,Y)
r_sq = model.score(X, Y)
print('coefficient of determination:', round(r_sq,4))
#Ahora obtenemos el R2 del modelo, los coeficientes de las variables y su significancia:
import statsmodels.api as sm
X2 = sm.add_constant(X)
est = sm.OLS(Y, X2)
est2 = est.fit()
est2.summary()
#Se observa un R2 bueno, al parecer la variable de tasa minima de expamsion
#no es significativa, por lo cual se elimina:
X= X.drop(['tasa_min_exp'],axis=1)
#Se corre un segundo modelo:
model1 = LinearRegression().fit(X,Y)
r_sq1 = model1.score(X, Y)
print('coefficient of determination:', round(r_sq1,4))
#Ahora obtenemos el R2 del modelo, los coeficientes de las variables y su significancia:
import statsmodels.api as sm
X2 = sm.add_constant(X)
est = sm.OLS(Y, X2)
est2 = est.fit()
est2.summary()
El R2 sigue siendo bueno, y se observa que todas las variables no instrumentales son significativas.
#Se compara la proyección con el real:
Y_pred= model1.predict(X)
compare= pd.DataFrame()
compare['curva']=Y_pred
compare['tipo']='predicción'
compare['x']=np.arange(0,435)
compare1= pd.DataFrame()
compare1['curva']=Y
compare1['tipo']='real'
compare1['x']=np.arange(0,435)
compare= pd.concat([compare,compare1])
sns.lineplot(x= 'x',y='curva', hue='tipo',data=compare)
Al comparar la preducción con el real se obser que
#Ahora varificamos la distribución de los residuales:
errors= Y_pred - Y
errors_x= [number for number in range(0,len(errors))]
#Ploteamos los residuales:
plt.scatter(errors_x,errors)
z = np.polyfit(errors_x,errors, 1)
p = np.poly1d(z)
plt.plot(errors_x,p(errors_x),"r--")
plt.show()
Cómo se observa no hay tendencia alguna de los residuales, se distribuyen de manera aleatoria alrededor del cero, por lo que se considera que es una buena aproximación de la regresión y el modelo se ajusta a los datos.
Después de identificar las variables significativas en el modelo de regresión y comprobar que hay relación entre ellas, ahora se procede a tratar de encontrar una relación entre las regiones que nos indique si hay clusters de tipos de regiones y si ese comportamiento se mantiene por cada año o no.
data_clusters= data_reg.copy()
#Elegir sólo data de los departamentos, quitar el total nacional:
data_clusters=data_clusters.loc[(data_clusters.departamento != 'Total Nacional')]
#Eliminamos las variables que no son necesarias y las que ya encontramos que no son significativas, así como la
#creación de una nueva.
data_clusters['mortalidad_ratio'] = data_clusters['mortalidad_infantil']/data_clusters['natalidad']
data_clusters= data_clusters.drop(['ninios_mujer','mortalidad_infantil','natalidad','departamento',
'anio','tasa_min_exp','y_accesos'],axis=1)
#Usados PCA para obtener los componentes más importantes y poder graficar en 2D:
from sklearn.decomposition import PCA
data_pca=data_clusters[['pib_percapita', 'desempleo', 'poblacion', 'esperanza_vida', 'tasa_migracion', 'mortalidad',
'gini', 'trm','y_accesos_pobla', 'mortalidad_ratio']]
#Ahora estandarizamos las variables para quitar efecto de magnitudes:
from sklearn import preprocessing
data_pca_std = preprocessing.scale(data_pca)
componts=PCA(n_components=2)
componts.fit(data_pca_std)
trans=componts.transform(data_pca_std) #Aplica la reducción de dimensionalidad
print (componts.explained_variance_ratio_) #Varianza explicada por los componentes
Se observa que con sólo dos componentes se obtiene el 50% de la información.
#Ploteamos los datos:
plt.scatter(trans[:, 0], trans[:, 1])
Pareciera que PCA no es la mejor manera de obtner los componentes más importantes porque la división no es limpia así que se probará con t-SNE:
from sklearn.manifold import TSNE
tcomponts= TSNE(n_components=2,perplexity=25).fit_transform(data_pca_std)
#Se obtiene el número de componentes y como se ven:
tcomponts.shape
#Datos transformados para la primera compoente:
tcomponts[:,0]
#Graficar la iformación usando T-SNE:
plt.scatter(tcomponts[:,0],tcomponts[:,1])
Con t-SNE se obserba más claro que hay ciertos grupos diferentes, pero se debe realizar clustering para poderlos dierenciar claramente.
#Implementar cluster de skalearn:
from sklearn.cluster import KMeans
#Se implementa el método del codo para obtener el No. de K, así como su visualización:
#Método del codo:
sse=[] #Se crea una lista que contenga la suma de los errores en cada iteración
iterar=10 #número de iteraciones a considerar
rango = range(1, iterar)
for i in rango:
kmeans = KMeans(n_clusters = i, init = 'k-means++',random_state = 0)
kmeans.fit(tcomponts)
sse.append(kmeans.inertia_) #Con este atributo se obtiene la suma de distancias al cuadrado de cada punto a su centroide.
#Graficar el codo:
plt.scatter(rango,sse)
plt.plot(rango,sse)
plt.xlabel('K grupos')
plt.ylabel('SSE')
Se debe probar con 4 o 5 grupos:
#Con 4 grupos:
n=4
km=KMeans(n_clusters=n,max_iter=1000).fit(tcomponts)
y_km=KMeans(n_clusters=n,max_iter=1000).fit_predict(tcomponts)
km.cluster_centers_
plt.scatter(tcomponts[:, 0], tcomponts[:, 1],c=y_km)
plt.title("Clusters generados")
#Con 5 grupos:
n=5
km=KMeans(n_clusters=n,max_iter=1000).fit(tcomponts)
y_km=KMeans(n_clusters=n,max_iter=1000).fit_predict(tcomponts)
km.cluster_centers_
plt.scatter(tcomponts[:, 0], tcomponts[:, 1],c=y_km)
plt.title("Clusters generados")
#K-means no es calro así que hacemos uso de un modelo por densidad:
from sklearn.cluster import DBSCAN
neps=5
sampls=10
clusters = DBSCAN(eps=neps, min_samples=sampls).fit(tcomponts)
y_clusters = DBSCAN(eps=neps, min_samples=sampls).fit_predict(tcomponts)
plt.scatter(tcomponts[:, 0], tcomponts[:, 1],c=y_clusters)
plt.xlabel('T-SNE1')
plt.ylabel('T-SNE2')
#Probamos otra configuración:
neps=6
sampls=10
clusters = DBSCAN(eps=neps, min_samples=sampls).fit(tcomponts)
y_clusters = DBSCAN(eps=neps, min_samples=sampls).fit_predict(tcomponts)
plt.scatter(tcomponts[:, 0], tcomponts[:, 1],c=y_clusters)
plt.xlabel('T-SNE1')
plt.ylabel('T-SNE2')
Con esta información creemos que la mejor configuración será de 4 clusters. Así que se vuelve a correr para guardar esa información.
#Ahora se relaciona la data original con los clusters:
data_clusters['Cluster']= y_km
#Se descarga para ser visualizada en tableau::
data_clusters.to_excel(os.path.join(os.getcwd(),'Data_tableau','clusters.xlsx'), index= False)
#Se importan las librerías pertinentes:
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
#Se selecciona la data a usar:
data_tree= data_clusters[['pib_percapita', 'desempleo', 'poblacion','esperanza_vida','tasa_migracion',
'mortalidad', 'gini', 'trm','mortalidad_ratio','Cluster']]
#Se separa x y Y(será la clasificación de cluster obtenida anteriormente):
data_tree_x= data_tree.copy()
data_tree_x= data_tree_x.drop(['Cluster'],axis=1)
data_tree_y= data_tree['Cluster']
#Separamos los datos en entrenamiento y test:
xtrain,xtest,ytrain,ytest = train_test_split(data_tree_x, data_tree_y)
#Toma el 75% de los datos para entrenar
print (xtrain.shape)
data_tree['Cluster'].unique()
#Se implementa el algoritmo:
arbol=DecisionTreeClassifier()
arbol.fit(xtrain,ytrain)
#Se obtiene el resultado sobre el set de test:
print ("primera iteración sobre test:", round(arbol.score(xtest,ytest),4)*100) #Capacidad predictiva
#Se verifica sobre el set de entrenamiento:
print ("primera iteración sobre train:",round(arbol.score(xtrain,ytrain),4)*100)
Se observa que hay sobre ajuste al tener un score del 100% dobre train, así que se modifica la profundidad del arbol:
#Para evitar el sobre ajuste se indica una profundidad inferior:
arbol=DecisionTreeClassifier(max_depth=5)
arbol.fit(xtrain,ytrain)
#Se obtiene el resultado sobre el set de test:
print ("segunda iteración sobre test:",round(arbol.score(xtest,ytest),4)*100) #Capacidad predictiva
#Se verifica sobre el set de entrenamiento:
print ("segunda iteración sobre train:",round(arbol.score(xtrain,ytrain),4)*100)
#Mostrar las características más importantes:
targetnames= data_tree_x.columns
caract=data_tree_x.shape[1]
plt.barh(range(caract),arbol.feature_importances_)
plt.yticks(np.arange(caract),targetnames)
plt.xlabel('Importancia de las características')
plt.ylabel('Características')
plt.show()
Según el resultado se observa que la mortalidad_ratio, la tasa de migración y el pib_percapita son variables importantes al momento de diferenciar los custers. Además son variables que se podrían afectar de alguna manera para entender como mejorar la penetración digital, cosa que no se podría por ejemplo con la trm, que al parecer afecta, pero en su comportamiento interfieren demasiadas variables exogenas.
#Se importa la librería de holt-winters:
from statsmodels.tsa.holtwinters import ExponentialSmoothing
#Obtenemos toda la data procesada hasta el momento:
data_forecast= X.merge(data_reg)
data_forecast.head(10)
Debido a que hay valores negativos de la serie temporal de tasa de migración, se debe realizar un tratamiento especial para que el algoritmo sea capaz de aceptar el input, pero además procesar el output para que tenga sentido, es así como el input se eleva al cuadrado y la salida se le saca raiz, siempre que el valor anterior sea positivo, si el valor anterior fuera negativo se debe no sólo sacarle la raiz sino además multiplicar por -1 para que siga la tendencia negativa.
#Se procede a obtener la predicción de la tasa de migración para cada departamento:
#Se hace una prueba con Arauca que tiene un comportamiento especial:
df=data_forecast.loc[(data_forecast.departamento=="Arauca"),'tasa_migracion']
df= df.reset_index(drop=True)
#Debido a que hay valores negativos de la serie temporal, se eleva al cuadrado para que el algoritmo lo pueda
#trabajar:
train= df.iloc[:14]
test = df.iloc[11:]
model = ExponentialSmoothing(train**2, seasonal='mul', seasonal_periods=11).fit()
pred = model.predict(start=14, end= 19)
#Posteriormente se trabaja la predicción para volverla a dejar en negativo y en la escala adecuada si es el caso:
if np.sign(train[13]) > 0:
pred = np.sqrt(pred)
else:
pred = np.sqrt(pred)*-1
#Se muestra un ejemplo de la capacidad predictiva:
plt.plot(train.index, train, label='Train')
plt.plot(test.index, test, label='Test')
plt.plot(pred.index, pred, label='Holt-Winters')
plt.legend(loc='best')
Como se observa, así los valores sean negativos, el modelo es capaz de predecir a futuro la serie temporal.
#Se procede a hacer la proyección de todos los departamentos y todas las variables. Para el pib percapita y la mortalidad ratio
#debido a su naturaleza "suave" se decide proyectar con regresiones lineales:
#Se crean los data frames que guardarán la información, para cada departamento, cada año y variable:
depart= data_forecast.departamento.unique()
pred_tasa_migracion=pd.DataFrame(columns=['tasa_migracion','departamento'])
pred_pib_percapita=pd.DataFrame(columns=['pib_percapita','departamento'])
pred_mortalidad_ratio=pd.DataFrame(columns=['mortalidad_ratio','departamento'])
pred_tasa_migracion1=pd.DataFrame(columns=['tasa_migracion','departamento'])
pred_pib_percapita1=pd.DataFrame(columns=['pib_percapita','departamento'])
pred_mortalidad_ratio1=pd.DataFrame(columns=['mortalidad_ratio','departamento'])
for i in range(0,len(depart)):
df=data_forecast.loc[(data_forecast.departamento==depart[i]),'tasa_migracion']
df= df.reset_index(drop=True)
#Debido a que hay valores negativos de la serie temporal, se eleva al cuadrado para que el algoritmo lo pueda
#trabajar:
train= df.iloc[:15]
model_tm = ExponentialSmoothing(train**2, seasonal='mul', seasonal_periods=11).fit()
pred_tm = model_tm.predict(start=15, end= 19)
#Posteriormente se trabaja la predicción para volverla a dejar en negativo y en la escala adecuada si es el caso:
if np.sign(train[14]) > 0:
pred_tm = np.sqrt(pred_tm)
else:
pred_tm = np.sqrt(pred_tm)*-1
pred_tasa_migracion1['tasa_migracion']= pred_tm
pred_tasa_migracion1['departamento']= depart[i]
pred_tasa_migracion=pd.concat([pred_tasa_migracion,pred_tasa_migracion1])
#Modelo para predecir el PIB_percapita:
df2=data_forecast.loc[(data_forecast.departamento==depart[0]),'pib_percapita']
df2= df2.reset_index(drop=True)
train2= df2.iloc[:15]
x_pp= pd.DataFrame(np.arange(0, 15))
model_pp = LinearRegression().fit(x_pp,train2)
pred_pp= model_pp.predict(pd.DataFrame(np.arange(15, 20)))
pred_pib_percapita1['pib_percapita'] = pred_pp
pred_pib_percapita1['departamento']= depart[i]
pred_pib_percapita=pd.concat([pred_pib_percapita,pred_pib_percapita1])
#Modelo para predecir la mortalidad_ratio:
df3=data_forecast.loc[(data_forecast.departamento==depart[i]),'mortalidad_ratio']
df3= df3.reset_index(drop=True)
train3= df3.iloc[:15]
x_mr= pd.DataFrame(np.arange(0, 15))
model_mr = LinearRegression().fit(x_mr,train3)
pred_mr= model_mr.predict(pd.DataFrame(np.arange(15, 20)))
pred_mortalidad_ratio1['mortalidad_ratio']= pred_mr
pred_mortalidad_ratio1['departamento']=depart[i]
pred_mortalidad_ratio=pd.concat([pred_mortalidad_ratio,pred_mortalidad_ratio1])
#Se junta toda la data:
pred_tasa_migracion=pred_tasa_migracion.reset_index()
pred_pib_percapita=pred_pib_percapita.reset_index()
pred_mortalidad_ratio=pred_mortalidad_ratio.reset_index()
data_proyecx= pred_tasa_migracion.merge(pred_pib_percapita[['pib_percapita']],left_index=True,right_index=True)
data_proyecx= data_proyecx.merge(pred_mortalidad_ratio[['mortalidad_ratio']],left_index=True,right_index=True)
data_proyecx.head(10)
#Se baja la información para realizar todo el ajuste y pruebas de aumento y disminución de valores de variables a futuro:
data_proyecx.to_excel(os.path.join(os.getcwd(),'Data_tableau','proyecciones_variables_x.xlsx'), index= False)
X.to_excel(os.path.join(os.getcwd(),'Data_tableau','X.xlsx'), index= False)
Utilizar el primer modelo de regresión lineal hecho para proyectar los valores a futuro junto con las posibles combinaciones de variación de variables x. Habrá 343 escenarios diferentes, ya que son 7 posibles estados: 0%,+5,+10%,+15%,-5%,-10%,-15%; para 3 diferentes variables: mortalidad_ratio, tasa_migracion, pib_percapita => 7x7x7 = 343 escenarios.
Para obtener las diferentes combinaciones se usó el siguiente algoritmo:
lista_posi= [0,0.05,0.1,0.15,-0.05,-0.1,-0.15]
lista_completa=[]
for i in range(0,len(lista_posi)):
for j in range(0,len(lista_posi)):
for k in range(0,len(lista_posi)):
lista_completa.append((lista_posi[i],lista_posi[j],lista_posi[k]))
lista dataframe=pd.DataFrame(lista_completa1)
lista_completa1=lista_completa for i in range(0,144): lista_completa1=lista_completa1+lista_completa
#Se carga la data con los escenarios construidos:
ruta_proy= os.path.join(os.getcwd(),'Data_limpia','X_escenarios.xlsx')
data_proy_escenarios= pd.read_excel(ruta_proy, sheet='Sheet1')
data_proy_escenarios.head(10)
#Obtenemos sólo la data que le corresponde al modelo:
X_escenarios = data_proy_escenarios[X.columns]
#Hacemos la predicción con el modelo entrenado anteriormente:
pred_escenarios= model1.predict(X_escenarios)
#Unir la predicción con la base original:
data_proy_escenarios['y_accesos_predict']=pred_escenarios
#Comparar las curvas de predicción de algunos deparamentos:
#Ejemplo para Antioquia:
plt.plot(data_proy_escenarios.loc[(data_proy_escenarios['departamento_Antioquia']==1) & (data_proy_escenarios['tipo']=='real'),'y_accesos_predict'], label='prediccion')
plt.plot(data_proy_escenarios.loc[(data_proy_escenarios['departamento_Antioquia']==1) & (data_proy_escenarios['tipo']=='real'),'y_accesos_pobla'], label='real')
plt.legend(loc='best')
#Ejemplo para Valle del Cauca:
plt.plot(data_proy_escenarios.loc[(data_proy_escenarios['departamento_Valle del Cauca']==1) & (data_proy_escenarios['tipo']=='real'),'y_accesos_predict'], label='prediccion')
plt.plot(data_proy_escenarios.loc[(data_proy_escenarios['departamento_Valle del Cauca']==1) & (data_proy_escenarios['tipo']=='real'),'y_accesos_pobla'], label='real')
plt.legend(loc='best')
#Verificamos de Accuracy:
r_sq2 = model1.score(X_escenarios[0:435], data_proy_escenarios['y_accesos_pobla'][0:435])
print('coefficient of determination:', round(r_sq2,4))
Se observa que el modelo tiene un buen ajuste a los datos de penetración digital, sin necesidad de ser una línea recta sino que responde al comportamiento de la interacción de diferentes variables, permitiendo así ajustarle a cambios de pendiente.
#Por último se descargan los archivos para realizar las gráficas en tableau. Por favor continúe con el proceso
#e interactúe con los dashboards que permiten predecir el comportamiento futuro de la penetración digital para los
#siguientes 5 años!:
data_proy_escenarios.to_excel(os.path.join(os.getcwd(),'Data_tableau','simulacion_escenarios.xlsx'), index= False)