#!/usr/bin/env python
# coding: utf-8

# # Introducción a la calidad en los datos

# In[2]:


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

import warnings
warnings.filterwarnings('ignore')

pd.options.display.float_format = '{:.2f}'.format #Desactivar notación científica en pandas:
np.set_printoptions(suppress=True) #Desactivar notación científica en numpy:
pd.set_option('display.max_columns', None) #comando para mostrar todas las columnas


# In[195]:


adult = pd.read_csv('./data/Adult_short_mal.csv', sep='\t')
fuego = pd.read_csv('./data/hectareasIncendiosCCAA2005-2013.csv', sep=';')
tempe = pd.read_csv('./data/medicion_temperaturas_mal.csv', sep=';')


# In[127]:


print (adult.info())


# In[128]:


print (fuego.info())


# In[129]:


print (tempe.info())


# In[131]:


adult.head()


# In[132]:


fuego.head()


# In[133]:


tempe.head()


# #### Comprobación manual de valores perdidos

# In[134]:


columna = fuego['Total Forestal'].isna()


# In[19]:


# Nos devuelve una serie lógica.  Podemos usar esta serie para filtrar datos por filas.
columna


# In[135]:


columna.sum()


# In[164]:


# Podemos obtener el total de valores perdidos en una tabla determinada
tabla = fuego.isnull()
tabla


# In[165]:


# Totales por columna
tabla.sum(axis=0)


# In[166]:


# Totales por fila
tabla.sum(axis=1)


# Existen multitud de herramientas que nos van permitir realizar el análisis exploratorio inicial de manera rápida.  Una de ellas es Sweetwiz.
# https://github.com/fbdesignpro/sweetviz

# In[10]:


#!pip install sweetwiz


# In[139]:


import sweetviz


# In[140]:


informeAdult = sweetviz.analyze(adult)
informeFuego = sweetviz.analyze(fuego)
informeTempe = sweetviz.analyze(tempe)


# In[141]:


informeAdult.show_html()
informeFuego.show_html()
informeTempe.show_html()


# #### También podemos hacer uso de mapas de calor.

# In[142]:


plt.show()

sns.heatmap(adult.isnull(), cbar=False) # Formato notebook


# In[143]:


sns.heatmap(fuego.isnull(), cbar=False)


# In[144]:


sns.heatmap(tempe.isnull(), cbar=False)


# #### Una última librería de análisis

# In[60]:


get_ipython().system('pip install missingno')


# In[145]:


import missingno as msno


# In[146]:


msno.matrix(adult)


# In[147]:


msno.matrix(fuego)


# In[148]:


msno.matrix(tempe)


# ### Esta librería también nos permite generar heatmaps

# In[149]:


msno.heatmap(fuego)


# ### Por último, podemos analizar la existencia de valores perdidos a partir de dendogramas

# In[150]:


msno.dendrogram(tempe)


# ### Sabiendo la ubicación y cantidad de valores perdidos...

# In[169]:


# Eliminamos aquellas columnas algún elementos nulos.
fuegoSinNa = fuego.copy()
fuego.isnull().sum()


# In[178]:


fuegoSinNa.dropna(axis=1, how='any').isnull().sum()


# ### En algunos casos, puede que los valores perdidos, no lo estén tanto.  Comprobamos.

# In[179]:


adult['Sex'].value_counts()


# In[196]:


# Ojo al tener que copiar un dataset.  Si no lo hacemos con copy, crea sólo un enlace al objeto original y cualquier transformación en la copia, se refleja en originial.
adultBien = adult.copy()


# In[199]:


# Podemos sustituir los ? por NAN puesto que no tenemos conocimiento a priori de su verdadero valor.
adultBien.Sex = adultBien.Sex.replace('?',np.NaN)
adultBien.Sex.value_counts()


# In[200]:


adult.Sex.value_counts()


# ### En otros casos podemos necesitar comprobar la consistencia de los datos.  Miramos el datatset adult.  Tenemos 2 columnas que parecen mostrar la misma información, education y education-num

# In[242]:


print (adult [['education','education-num']].value_counts())
tmp = adult [['education','education-num']].value_counts().to_frame().reset_index()
tmp.columns = ['education','education-num','total']


# In[230]:


tmp


# In[248]:


# Vamos a darle formato ancho a la tabla previa para verlo mejor
# Formato ancho
tmp2 = pd.pivot_table(tmp, index= ['education'], columns = ['education-num'], values = 'total', aggfunc='sum')
# Remplazo de nan's
tmp2.replace(np.nan, 0).astype('int')


# ### ¿Y entre Relationship y Sex?

# In[ ]:





# ### Hemos visto previamente que la variable Sexo tiene algunos problemas de calidad.  Los corregimos.

# In[252]:


adult.Sex.value_counts()


# In[253]:


adult.Sex = adult.Sex.replace('Mle', 'Male')
adult.Sex.value_counts()


# ### Usaremos ahora una lista de términos correctos que deberán aparecer en la columna Relationship.  De esta manera modificamos de una tacada toda la serie.  Para ello usaremos el concepto de distancia (entre palabras) de la librería Levenshtein.  La distancia Levenshtein, es el número mínimo de operaciones requeridas para transformar una cadena de caracteres en otra.

# In[283]:


#!pip install stringdist


# In[258]:


get_ipython().system('pip install python-Levenshtein')


# In[273]:


from Levenshtein import distance as lev
import operator


# In[277]:


# ¿Cómo funciona?
print (lev ('banana','Banana'))
print (lev ('banana','banan'))
print (lev ('banana','banano'))
print (lev ('banana','Bananos'))


# In[278]:


# Creamos la lista con los términos correctos que deberían aparecer en Relationship
correcto = ["Husband","Not-in-family","Own-child","Unmarried","Wife"]


# In[301]:


# A través de esta función podemos ver cuál es la palabra más similar (de una lista) a las palabras de nuestra serie de datos.
def similaridad(x):
    l = {}    
    for i in correcto:
        l[i] = ratio(x,i)
    return max(l.items(), key=operator.itemgetter(1))[0]


# In[295]:


adult2 = adult.copy()
adult2['Relationship'].map(similaridad)


# In[296]:


adult2['Relationship2'] = adult2['Relationship'].map(similaridad)
adult2[['Relationship','Relationship2']]


# ### Veremos durante el Bootcamp diferentes métodos de imputación.  Para este caso, usaremos la librería FancyImpute que tiene la librería llamada IterativeImputer (comunmente conocida como MICE)

# In[37]:


get_ipython().system('pip install fancyimpute')
get_ipython().system('pip install miceforest')


# In[38]:


from fancyimpute import IterativeImputer 
import miceforest as mf


# In[47]:


tmp = mf.MultipleImputedKernel(
    tempe,
    datasets=1,
    save_all_iterations=True,
    random_state = 123)


# In[48]:


# Ejecutamos el algoritmo MICE 3 veces
tmp.mice(6)


# In[53]:


# Extraemos el dataset con la imputación de datos realizada.
tmp.complete_data(0) # Devuelve el dataset creado con la imputación de na's realizada


# In[54]:


tempeMice.isna().sum()


# In[55]:


tempe.isna().sum()


# ### Comprobamos la existencia o no de Outliers y de existir los marcaremos.  Existen varias maneras de detectar outliers que veremos en el módulo 4.
# ### En este caso, vamos a ejecutar un análisis univariante para localizar los outliers respecto de la columna Age.
# ### El criterio para decidir si un elemento se considera Oultier o no es:  media +- 3* desviación estandar

# In[308]:


# Trabajamos con el dataset adult
media = adult.Age.mean()
desv = adult.Age.std()


# In[310]:


media, desv


# In[311]:


adult['OutlierAge'] = 'Normal'
filtroSuperior = adult['Age'] > media + 3 * desv
filtroInferior = adult['Age'] < media - 3 * desv

adult['OutlierAge'][filtroSuperior] = 'Alto'
adult['OutlierAge'][filtroInferior] = 'Bajo'


# In[313]:


adult.OutlierAge.value_counts()


# ### Ya tendríamos localizado el outlier (en cuanto a edad) en este set de datos.  Lo pintamos.

# In[315]:


sns.scatterplot(data=adult, x="Age", y="fnlwgt", hue="OutlierAge")

