# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Laboratorio 1

# <NAME>  123972 \
# <NAME>  151280 \
# <NAME> 119523

# ## Data profiling - EDA

import re
import pandas as pd
import numpy as np
from pandas_profiling import ProfileReport
import csv
import requests

# +
import sys
sys.path.append('./../')

# OPTIONAL: Load the "autoreload" extension so that code can change
# %load_ext autoreload

#Own Library modules
import src
# OPTIONAL: always reload modules so that as you change code in src, it gets loaded
# %autoreload 2

from src.algorithms import eda as eda_eq

# +
# para leer el archivo como local
agua = pd.read_csv('../data/consumo-agua.csv')

# Para leer el archivo directo desde la url:
#CSV_URL = 'https://datos.cdmx.gob.mx/explore/dataset/consumo-agua/download/?format=csv&timezone=America/Mexico_City&lang=es&use_labels_for_header=true&csv_separator=%2C'
#agua_liga = pd.read_csv(CSV_URL)
#agua = agua_liga

# Otra forma de extraerlo desde la liga, pero menos eficiente
# CSV_URL = 'https://datos.cdmx.gob.mx/explore/dataset/consumo-agua/download/?format=csv&timezone=America/Mexico_City&lang=es&use_labels_for_header=true&csv_separator=%2C'
# r = requests.get(CSV_URL)
# with requests.Session() as s:
#     download = s.get(CSV_URL)

#     decoded_content = download.content.decode('utf-8')

#     cr = csv.reader(decoded_content.splitlines(), delimiter=',')
#     my_list = list(cr)

# agua = pd.DataFrame(my_list[1:len(my_list)], columns = my_list[0])
# -

agua.head()

# ### ¿Cuántas variables tenemos?

len(agua.columns)

# ### ¿Cuántas observaciones tenemos?

len(agua)

# ### ¿Cuántas observaciones únicas tenemos por variable?

tabla_nunicos = pd.DataFrame(agua.nunique())
tabla_nunicos.columns = ['número de elementos únicos']
tabla_nunicos['número de elementos únicos'] = tabla_nunicos['número de elementos únicos'].map('{:,}'.format)
tabla_nunicos

# #### Transformaciones que deberán realizarse más adelantte:
# Geo Point                object --> split y dos float  
# Geo Shape                object   
# consumo_total_mixto     float64  
# anio                      int64 --> Eliminar    
# nomgeo                   object --> Categorica    
# consumo_prom_dom        float64  
# consumo_total_dom       float64  
# alcaldia                 object --> Categorica   
# colonia                  object --> Categorica  
# consumo_prom_mixto      float64  
# consumo_total           float64  
# consumo_prom            float64  
# consumo_prom_no_dom     float64  
# bimestre                  int64 --> Categorica  
# consumo_total_no_dom    float64  
# gid                       int64 --> Categorica    
# indice_des               object --> Categorica   

# ### ¿Cuántas variables numéricas tenemos?

eda_eq.cuenta_tipo_de_dato(agua,'numerico')

# ### ¿Cuántas variables de fecha tenemos?

eda_eq.cuenta_tipo_de_dato(agua,'Date')

# ### ¿Cuántas variables categóricas tenemos?

eda_eq.cuenta_tipo_de_dato(agua,'category')

# ### ¿Cuántas variables de texto tenemos?

eda_eq.cuenta_tipo_de_dato(agua,'object')

# ### Genera el profiling de cada variable (propio)

# #### Perfilamiento general

profiling_general = eda_eq.genera_profiling_general(agua)
#profiling_general['Resultado'] = profiling_general['Resultado'].map('{:,}'.format)
profiling_general['Resultado'][1] = '{:,.0f}'.format(profiling_general['Resultado'][1])
profiling_general['Resultado'][2] = '{:,.0f}'.format(profiling_general['Resultado'][2])
profiling_general['Resultado'][7] = '{:,.0f}'.format(profiling_general['Resultado'][7])
profiling_general                                                                    

eda_eq.cuenta_nulos_por_columnas(agua)

eda_eq.cuenta_nulos_por_renglones(agua)

eda_eq.cuenta_nulos_por_renglones_tabla(agua)

# ## Perfilamiento por variable

profiling_numerico,profiling_categoricas,profiling_texto = eda_eq.genera_profiling_por_variable(agua)

#pd.set_option('display.max_colwidth', -1)
profiling_numerico

profiling_categoricas

profiling_texto

# ### Genera el profiling de cada variable (Pandas profiling)

# +
#profile = ProfileReport(agua, title='Pandas Profiling Report')

# +
#profile

# +
#profile.to_file(output_file = '../results/pandas_profiling_inicial.html')
# -

# ### ¿Cuántas alcadías tienes? ¿Cuántos nomgeo tienes? ¿Identificas algún error?

alcaldias = pd.unique(agua['alcaldia']) 
alcaldias_n = agua['alcaldia'].nunique()

print(alcaldias)
print(alcaldias_n)

nomgeos = pd.unique(agua['nomgeo']) 
nomgeos_n = agua['nomgeo'].nunique()

print(nomgeos)
print(nomgeos_n)

agua.loc[agua["nomgeo"].str.contains('Talpan', case = False, na = None), "nomgeo"] = 'Tlalpan'

nomgeos = pd.unique(agua['nomgeo']) 
nomgeos_n = agua['nomgeo'].nunique() 
print(nomgeos)
print(nomgeos_n)

# ### ¿Qué conocemos ahora de este set de datos por variable?

# * El dataframe tiene 17 columnas.
# * La variable `anio` tiene solamente un valor en toda la base de datos, por lo que no tendría sentido conservarla.
# * La variable `nomgeo` contenía un error, poniendo como otra categoría la palabra `talpan`, dicho error fue corregido.
# * `gid`indica el ID de cada observación.
# * En realidad, deberían ser 8 variables numéricas
# * Hay 26,318 valores faltantes en toda la base de datos, que corresponden al 2.2% de los datos.
# * No hay valores duplicados
# * Hay 5 columnas que tienen NA's --> `consumo_total_mixto, consumo_prom_mixto, consumo_prom_dom, consumo_total_dom y Geo Shape`
# * En la variable consumo_total_no_dom y consumo_prom_no_dom los faltantes están expresados con ceros. --> **pregunta para cliente para ver si es correcto**
# * La observación que tiene más valores faltantes es el 54,555, con 5 valores nulos.
#
# -------------------------------------
# ##### Profiling numérico:
# * La variable `consumo_total_mixto` tiene una desviación estándar muy alta (casi el doble de la media), hay una gran cantidad de ceros (incluso, llegan a ser por lo menos el 25% de los datos) --> son 17,715 ceros
# * El cuartil 75% de la variable `consumo_total_mixto`está en un valor de 233, mientras que el máximo está en 23,404 --> podría indicar valores atípicos.
# * La variable `consumo_prom_dom` tiene 9,861 ceros.
# * El cuartil 75% de la variable `consumo_prom_dom`está en un valor de 36.25, mientras que el máximo está en 7,796 --> podría indicar valores atípicos.
# * El cuartil 75% de la variable `consumo_total_dom`está en un valor de 1,261, mientras que el máximo está en 95,060 --> podría indicar valores atípicos.
# * `consumo_total_mixto`y `consumo_prom_mixto` tienen la misma cantidad de ceros
# * El cuartil 75% de la variable `consumo_prom_mixto`está en un valor de 61, mientras que el máximo está en 11,702 --> podría indicar valores atípicos.
# * El cuartil 75% de la variable `consumo_total`está en un valor de 1,808, mientras que el máximo está en 119,727 --> podría indicar valores atípicos.
# * La variable `consumo_prom`tiene una desviación estándar de 1,069 y una media de 111. Adicionalmente, el cuartil 75% está en un valor de 45, mientras el máximo está en 89,691.
# * `consumo_prom_no_dom`y `consumo_total_no_dom`tienen ambas una media no tan alta, pero una desviación estándar muy grande y un máximo muy elevado.
#
# -------------------------------------
# ##### Profiling categórico:
# * `nomgeo`y `alcaldia`son variables redundantes, no es necesario mantener ambas.
# * Iztapalapa, <NAME> y Cuauhtémoc son las alcaldías con más registros. --> verificar con cliente si es correcto.
# * Hay muchísimas colonias
# * Solo se tiene información de los primeros 3 bimestres del año
# * `gid`indica el ID de cada observación.
# * En cuanto al índice de desarrollo (`indice_des`), las tres clasificaciones con mayor cantidad de registros son:
#  - bajo --> incluye el 41% de las observaciones
#  - popular
#  - alto
#  
#  Entre estas últimas tres, forman poco más del 80% de los registros, por lo que la categoría "medio" no está tan lejos de ellas.
#
#
# -------------------------------------
# ##### Profiling fecha:
#
# No hay variables de tipo fecha.
#
#
# -------------------------------------
# ##### Profiling texto:
#
# No hay variables que debieran estar como de tipo texto, sino más bien categóricas.
#
# -------------------------------------
#
# ##### Comentarios para FE 
# * Geo Point debe ser separada y dividida en `latitud` y `longitud`.
# * Geo Shape indica información para gráficas geoespaciales.
# * `nomgeo, alcaldía, colonia, bimestre, gid e indice_des`  deberían estar en formato categórico
# * anio debería ser eliminada por tener un solo valor
# * Solo se tiene información de los primeros 3 bimestres del año
#

# ### Transformar las variables a formato estándar: minúsculas, sin espacios en blanco, sin signos de puntuación.

agua = eda_eq.EstandarizaFormato(agua)
agua.head()

agua = agua.astype({"bimestre":'category', "indice_des":'category', "nomgeo":'category', "alcaldia":'category',"colonia":'category', "gid":'category'}) 

agua.head()

# ### Agregar la variable latitud y longitud.

new = agua['geo_point'].str.split(",", n = 1, expand = True) 
agua["latitud"]= new[0] 
agua["longitud"]= new[1] 

# ### Pasar la variable latitud y longitud a numérica -si no la tomó como numérica-.

# Revisamos si está como tipo numérico
agua.dtypes

agua = agua.astype({"latitud":'float64', "longitud":'float64'}) 
agua.dtypes

# ### Eliminar la columna geo_point -una vez que creaste la variable latitud y longitud.

agua.drop(columns =["geo_point"], inplace = True) 
agua.head()

# ### Eliminar la columna geo_shape.

agua.drop(columns =["geo_shape"], inplace = True) 
agua.head()

# ### Cambiar a minúsculas las columnas alcaldía, colonia e indice_des.

# hecho con las funciones anteriores

# ### Volver a correr el proceso de identificación de variables numéricas, categóricas, texto y fechas.

# #### Variables numéricas

eda_eq.cuenta_tipo_de_dato(agua,'numerico')

# #### Variables categóricas

eda_eq.cuenta_tipo_de_dato(agua,'category')

# #### Variables de texto

eda_eq.cuenta_tipo_de_dato(agua,'object')

# #### Variables de tipo fecha

eda_eq.cuenta_tipo_de_dato(agua,'Date')

# ### Genera el data profiling por variable (puedes ocupar el paquete pandas-profiling de pandas)

# #### Perfilamiento propio --> General

profiling_general = eda_eq.genera_profiling_general(agua)
#profiling_general['Resultado'] = profiling_general['Resultado'].map('{:,}'.format)
profiling_general['Resultado'][1] = '{:,.0f}'.format(profiling_general['Resultado'][1])
profiling_general['Resultado'][2] = '{:,.0f}'.format(profiling_general['Resultado'][2])
profiling_general['Resultado'][7] = '{:,.0f}'.format(profiling_general['Resultado'][7])
profiling_general        

eda_eq.cuenta_nulos_por_columnas(agua)

eda_eq.cuenta_nulos_por_renglones(agua)

eda_eq.cuenta_nulos_por_renglones_tabla(agua)

# #### Perfilamiento propio --> Por variable

profiling_numerico,profiling_categoricas,profiling_texto = eda_eq.genera_profiling_por_variable(agua)

profiling_numerico

profiling_categoricas

profiling_texto

# ### Genera el profiling de cada variable (Pandas profiling)

# +
#profile = ProfileReport(agua, title='Pandas Profiling Report')

# +
#profile

# +
#profile.to_file(output_file = '../results/pandas_profiling_final.html')
# -

# Queremos generar el data profiling de estos datos.
#
# * ¿Cuántas variables tenemos?
# * ¿Cuántas observaciones tenemos?
# * ¿Cuántas observaciones únicas tenemos por variable?
# * ¿Cuántas variables numéricas tenemos?
# * ¿Cuántas variables de fecha tenemos?
# * ¿Cuántas variables categóricas tenemos?
# * ¿Cuántas variables de texto tenemos?
# * Generea el profiling de cada variable
# * ¿Qué conocemos ahora de este set de datos por variable?
# * ¿Cuántas alcadías tienes? ¿Cuántos nomgeo tienes? ¿Identificas algún error?
#
# * Transformar las variables a formato estándar: minúsculas, sin espacios en blanco, sin signos de puntuación.
# * Agregar la variable latitud y longitud.
# * Pasar la variable latitud y longitud a numérica -si no la tomó como numérica-.
# * Eliminar la columna geo_point -una vez que creaste la variable latitud y longitud.
# * Eliminar la columna geo_shape.
# * Cambiar a minúsculas las columnas alcaldía, colonia e indice_des.
# * Volver a correr el proceso de identificación de variables numéricas, categóricas, texto y fechas.
# * Genera el data profiling por variable (puedes ocupar el paquete pandas-profiling de pandas)

agua.head()

agua.drop(columns =["nomgeo", "anio", "colonia"], inplace = True) 

agua.head()

# +
#agua['indice_des'].unique()
# -

indices_des_que_hay = list(agua['indice_des'].unique())
indices_des_que_hay

indice_alto = agua.loc[agua["indice_des"] == 'alto']
indice_medio = agua.loc[agua["indice_des"] == 'medio']
indice_popular = agua.loc[agua["indice_des"] == 'popular']
indice_bajo = agua.loc[agua["indice_des"] == 'bajo']

print(len(indice_alto))
tabla_alto = eda_eq.cuenta_nulos_por_columnas(indice_alto)
tabla_alto['indice'] = 'alto'
tabla_alto

print(len(indice_medio))
tabla_medio = eda_eq.cuenta_nulos_por_columnas(indice_medio)
tabla_medio['indice'] = 'medio'
tabla_medio

print(len(indice_popular))
tabla_popular = eda_eq.cuenta_nulos_por_columnas(indice_popular)
tabla_popular['indice'] = 'popular'
tabla_popular

print(len(indice_bajo))
tabla_bajo = eda_eq.cuenta_nulos_por_columnas(indice_bajo)
tabla_bajo['indice'] = 'bajo'
tabla_bajo

eda_eq.CreaTablaConteoPorcentaje(agua,'indice_des',True)

tabla_todos_NA = tabla_alto.append(tabla_medio)
tabla_todos_NA = tabla_todos_NA.append(tabla_popular)
tabla_todos_NA = tabla_todos_NA.append(tabla_bajo)
tabla_todos_NA['categoria']= tabla_todos_NA.index
tabla_todos_NA

# +
import seaborn as sns
import matplotlib.pyplot as plt


chart = sns.barplot(x = 'categoria', y = 'Missing Values', hue = 'indice', data = tabla_todos_NA )

#Poner el título a la gráfica
chart.set_title("Cantidad absoluta de NA's por variable y su distinción por indice de desarrollo")

# Rotar labels del eje x
chart.set_xticklabels(chart.get_xticklabels(), rotation=10)

#Mover cuadro de leyendas afuera de la gráfica
plt.legend(bbox_to_anchor=(1.01, 1),borderaxespad=0)
plt.tight_layout()

# +
chart = sns.barplot(x = 'categoria', y = '% del Total', hue = 'indice', data = tabla_todos_NA )

#Poner el título a la gráfica
chart.set_title("Cantidad relativa de NA's por variable y su distinción por indice de desarrollo")

# Rotar labels del eje x
chart.set_xticklabels(chart.get_xticklabels(), rotation=10)

#Mover cuadro de leyendas afuera de la gráfica
plt.legend(bbox_to_anchor=(1.01, 1),borderaxespad=0)
plt.tight_layout()
# -

# ## GEDA

# ### Análisis de ceros

# +
### Libreria necesaria
#imports del notebook anterior
import re
import pandas as pd
import numpy as np
from pandas_profiling import ProfileReport
import csv
import requests

#disable warnings messages
import warnings
warnings.simplefilter("ignore")

#imports para graficar
import matplotlib.pyplot as plt
import seaborn as sns

#importando librería propia
import sys
sys.path.append('./../')

# OPTIONAL: Load the "autoreload" extension so that code can change
# %load_ext autoreload

#Own Library modules
import src
# OPTIONAL: always reload modules so that as you change code in src, it gets loaded
# %autoreload 2

from src.algorithms import eda as eda_eq
# -

agua = eda_eq.prepara_dataset(agua)

variables_numericas = np.array(["consumo_total_mixto", "consumo_prom_dom", "consumo_total_dom", 
                                "consumo_prom_mixto", "consumo_total", "consumo_prom", "consumo_prom_no_dom",
                               "consumo_total_no_dom"])
print(variables_numericas)

# Los siguientes histogramas muestran la **gran densidad en niveles bajos** (ceros y/o cercanos a dicho valor), además de que se logra apreciar que todas las variables poseen colas pesadas, pues hay valores muy altos para todas las variables.

for column in variables_numericas:
    plt.figure()
    sns.distplot(agua[column])

# En la siguiente sección se realizará un análisis detallado en aquellas observaciones donde no hubo medición en el consumo de agua, es decir, en donde el **consumo fue cero** para el periodo en cuestión.

# Filtrar por suma por renglones igual a cero
agua["suma"] = agua[variables_numericas].sum(axis=1)
agua_no_zero = agua[agua.suma == 0]
agua_no_zero = agua_no_zero[agua.columns.difference(np.append(variables_numericas, 'suma'))]

n_zero = agua_no_zero.shape[0]
n_agua = agua.shape[0]
print(n_zero/n_agua)

# Los valores que contienen puros ceros en las variables numéricas corresponden aproximadamente al $3.4\%$ de los datos, se preguntará a cliente porqué tenemos observaciones sin consumo de agua para dichos periodos y lugares:
# * ¿Esto se debe a un error de medición o captura de los datos?
# * ¿Son establecimientos que no son habitados o fueron deshabitados a lo largo del periodo en cuestión? 
# * Alguna otra opción.

variables_categoricas = np.array(['alcaldia', 'bimestre', 'colonia', 'gid', 'indice_des', 'nomgeo'])
for column in variables_categoricas:
    aux = pd.crosstab(index = agua_no_zero[column], columns='count')
    aux = aux.sort_values('count', ascending = False)
    print(column)
    print(aux.head())

# * La alcaldía de Iztapalapa tiene una cantidad de ceros "fuera de lo común", en el sentido de que se encuentra muy por encima de los demás valores.
# * El índice de desarrollo popular tiene una cantidad de ceros "fuera de lo común".
# * Entre las demás variables, la cantidad de ceros no es fuera de lo común (los rangos de dispersión no son muy altos).

cross_col_alc = pd.crosstab(index = agua_no_zero['colonia'], columns =  agua_no_zero['alcaldia'])
sns.heatmap(cross_col_alc, 
            cmap = sns.diverging_palette(20, 220, n = 200), 
            center = 0).set_title('Distribución de ceros por alcaldías y colonias')

# Cabe mencionar que no todas las colonias se encuentran en cada una de las alcaldías, lo cual puede confundir al lector, esta tabla muestra la cantidad de ceros que cada una de las colonias de distintas alcaldías. Nótese que las alcaldías de Iztapalapa y Tlalpan contienen una mayor cantidad de ceros, esto se debe a que son las que contienen más colonias. La siguiente tabla muestra la cantidad de colonias por alcaldía.

n_by_alcaldia = agua_no_zero.groupby("alcaldia")["colonia"].count()
n_by_alcaldia = n_by_alcaldia.sort_values(ascending = False)
n_by_alcaldia

cross_col_alc = pd.crosstab(index = agua_no_zero['alcaldia'], columns =  agua_no_zero['indice_des'])
sns.heatmap(cross_col_alc, 
            cmap = sns.diverging_palette(20, 220, n = 200), 
            center = 0).set_title('Distribución de ceros por índice de darrrollo y colonias')

# Se observa que hay un patrón: independientemente de la alcaldía, si hay ceros en el nivel popular, entonces hay ceros en el nivel bajo y viceversa, por lo que son estos $2$ ídices de desarrollo que son más afectados por la cantidad de ceros, lo cual no es para sorprenderse, debido a que son los que presentan mayor densidad. Sin embargo, para el caso de Tlalpan, se incluye el índice medio, donde se concluye que los 3 niveles son afectados, sin importar la densidad.

n_by_alcaldia = agua_no_zero.groupby("indice_des")["indice_des"].count()
n_by_alcaldia = n_by_alcaldia.sort_values(ascending = False)
n_by_alcaldia

cross_col_alc = pd.crosstab(index = agua_no_zero['alcaldia'], columns =  agua_no_zero['bimestre'])
sns.heatmap(cross_col_alc, 
            cmap = sns.diverging_palette(20, 220, n = 200), 
            center = 0).set_title('Distribución de ceros por índice de darrrollo y colonias')

# Las colonias presentan la misma cantidad de ceros a lo largo del semestre, de lo que concluímos que la presencia de ceros en las observaciones no se debe a un tema temporal, sino que esto se presenta de manera recurrente cada periodo.

agua_no_zero = agua[agua.suma == 0]
sns.scatterplot(x = agua_no_zero['longitud'], 
                y = agua_no_zero['latitud'], 
                hue = agua_no_zero['indice_des']).set_title('Longitud y latitud (ceros)')

# Observamos que se pueden observar ciertas zonas en donde hay una mayor agrupación de datos, esto se debe a la densidad en las zonas, no a que haya un fallo en la medición.

# Conclusión:
# * Las alcaldías con mayor cantidad de ceros son Iztapalapa, seguida de Tlalpan, este punto no es para alarmarse, pues se debe a que son las que presentan una mayor densidad.
# * A lo largo del tiempo, las distintas alcaldías han tenido el mismo comportamiento en relación a los ceros.
# * Los principales índices de desarrollo afectados por este "suceso" son popular, seguido del bajo, lo cual se debe a la densidad en dichos índices.
# * Por último, concluimos que la presencia de ceros no se debe a que en una zona haya fallos en la medición o errores de captura, pues es un fenómeno que se presenta de manera similar a lo largo de los tiempos en la misma cantidad para los distintos lugares.

StarcoderdataJupyterScriptsDedupFiltered