<jupyter_start><jupyter_text>Importación de Librerías<jupyter_code>import pyarrow
import fastparquet
import pandas as pd
import numpy as np
import missingno as msno
import matplotlib.pyplot as plt
import seaborn as sns
import hashlib as hl
import re

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import SimpleImputer
from sklearn.impute import IterativeImputer
from sklearn.decomposition import PCA
from sklearn.preprocessing import KBinsDiscretizer

from sklearn import (base, decomposition, feature_extraction, impute,
                     neighbors, preprocessing)
from sklearn.preprocessing import MinMaxScaler
from unidecode import unidecode

from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer<jupyter_output><empty_output><jupyter_text>Lectura del Dataset<jupyter_code>df_orig = pd.read_parquet("cupones_ds_test.parquet")
df = df_orig.copy()
df
df['monto_compra_movimiento'].describe().round(2)
df_orig.shape
df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 3769 entries, 0 to 3768
Data columns (total 41 columns):
 #   Column                               Non-Null Count  Dtype  
---  ------                               --------------  -----  
 0   dni_titular_movimiento               3769 non-null   object 
 1   moneda_movimiento                    3769 non-null   int64  
 2   id_comercio_movimiento               3769 non-null   object 
 3   nombre_comercio_histo                3769 non-null   object 
 4   numero_cupon_movimiento              3769 non-null   int64  
 5   debito_credito_movimiento            3769 non-null   int64  
 6   producto_naranja_movimiento          3769 non-null   object 
 7   codigo_empresa_movimiento            3769 non-null   int64  
 8   tipo_producto_tarjeta_movimiento     3769 non-null   int64  
 9   plan_movimiento                      3769 non-null   int64  
 10  fecha_vto_cupon_movimiento           3769 non-null   object 
 11  fecha_presentacion_movimiento [...]<jupyter_text>1. Curación  Eliminar filas duplicadas<jupyter_code>df = df[~df.duplicated()]
df['monto_compra_movimiento'].describe().round(2)<jupyter_output><empty_output><jupyter_text>Ajuste por Inflación<jupyter_code>df_infl = pd.read_csv('https://raw.githubusercontent.com/JIBarrionuevoGaltier/DiploDatos_2021_Mentoria_Grupo_2/main/notebooks_tp2/Indices%20de%20inflaci%C3%B3n.csv', 
                      parse_dates=['mes'])
df_infl
# Procedimiento para ajustar la variable monto a la inflacion en el periodo muestreado

df['anio_mes_cupon'] = pd.DatetimeIndex(df['fecha_cupon_movimiento']).year * 100 + pd.DatetimeIndex(df['fecha_cupon_movimiento']).month
df_infl['anio_mes_infl'] = pd.DatetimeIndex(df_infl['mes']).year * 100 + pd.DatetimeIndex(df_infl['mes']).month

df = df.merge(df_infl[['anio_mes_infl', 'inflacion_acumulada']]
         ,left_on='anio_mes_cupon'
         ,right_on='anio_mes_infl'
         ,how='inner').drop(columns=['anio_mes_infl'])
df.loc[df.moneda_movimiento != 1,'monto_ajustado'] = \
                df[df.moneda_movimiento != 1]['monto_compra_movimiento'] / (1 + df['inflacion_acumulada'])
# Visualizamos montos ajustados en pesos luego del ajuste
df.loc[df['moneda_movimiento'] != 1, ['monto_compra_movimiento','monto_ajustado']].describe().round(2)<jupyter_output><empty_output><jupyter_text>Ajuste por Tipo de Cambio<jupyter_code># Obtención de la cotizacion del dolar para los meses en nuestro dataset
df_tdc = pd.read_csv('https://raw.githubusercontent.com/JIBarrionuevoGaltier/DiploDatos_2021_Mentoria_Grupo_2/main/notebooks_tp2/Tipos%20de%20cambio.csv',
                     parse_dates=['fecha_cotizacion'], dayfirst=True, delimiter=';')

df_tdc = df_tdc.sort_values(by=['fecha_cotizacion']).reset_index(level=None).drop(columns=['index'])
df_tdc['anio_mes'] = (df_tdc.fecha_cotizacion.dt.year * 100) + df_tdc.fecha_cotizacion.dt.month
df_tdc.head(10)
# Procedimiento para calcular la fecha de cotizacion 
df_tdc['day'] = df_tdc.fecha_cotizacion.dt.day

meses_cdiez = df_tdc[df_tdc['day'].isin([10])]['anio_mes'].unique()
meses_sdiez = df_tdc[(~df_tdc['day'].isin([10])) & (~df_tdc['anio_mes'].isin(meses_cdiez))]['anio_mes'].unique()

ind_fechas = []
for i in meses_cdiez:
    ind_fechas.append(min((df_tdc[(df_tdc['anio_mes'] == i) & (df_tdc['day'] == 10)]['fecha_cotizacion'].index)))
for i in meses_sdiez:
    ind_fechas.append(min((df_tdc[(df_tdc['anio_mes'] == i) & (df_tdc['day'] > 10)]['fecha_cotizacion'].index)))
# Cotizacion del dia y el dia de vencimiento
df_tdc_cot = df_tdc.iloc[ind_fechas,:].sort_values(by=['fecha_cotizacion']).reset_index(level=None).drop(columns=['index'])
df_tdc_cot
# Procesamos la variable anio_mes_cupon considerando la fecha de corte
df.fecha_cupon_movimiento = pd.to_datetime(df.fecha_cupon_movimiento)

def func_var(row):
    if row.fecha_cupon_movimiento.day > 24:
        if row.fecha_cupon_movimiento.month == 12:
            return 202101
        else:
            return row['anio_mes_cupon'] + 1
    else:
        return row['anio_mes_cupon']

df.loc[df.moneda_movimiento == 1,'anio_mes_cupon'] = df[df.moneda_movimiento == 1].apply(func_var, axis=1)<jupyter_output><empty_output><jupyter_text>Mostramos un ejemplo donde se puede visualizar que anio_mes_cupon cambia para antes y despues de la fecha de corte<jupyter_code># Mergeamos con df original
df = df.merge(df_tdc_cot[['anio_mes', 'venta']]
                 ,left_on='anio_mes_cupon'
                 ,right_on='anio_mes'
                 ,how='inner').drop(columns=['anio_mes'])

# Aplicamos el tipo de cambio
df.loc[df.moneda_movimiento == 1,'monto_ajustado'] = \
                df[df.moneda_movimiento == 1]['monto_compra_movimiento'] * df['venta']

# Se descartan las columnas usadas para los calculos
df = df.drop(columns=['inflacion_acumulada','venta'])

# Redondeamos
df['monto_ajustado'] = df['monto_ajustado'].round(2)
# Visualizamos montos ajustados en dolares luego de aplicar el tipo de cambio
df.loc[df['moneda_movimiento'] == 1, ['monto_compra_movimiento','monto_ajustado']].describe().round(2)<jupyter_output><empty_output><jupyter_text>------<jupyter_code>df['moneda_movimiento'].value_counts()<jupyter_output><empty_output><jupyter_text>1.1 Asegurar IDs únicos<jupyter_code># Reagrupar los comercios que tienen mas de un nombre y concatenar todos sus nombres
df.nombre_comercio_histo = df.nombre_comercio_histo.str.strip()

df_unicos = (
    df[["id_comercio_movimiento", "nombre_comercio_histo"]].drop_duplicates()\
        .groupby("id_comercio_movimiento").agg({"nombre_comercio_histo": 'sum'})
).reset_index()

df_unicos = df_unicos.rename(columns={'nombre_comercio_histo':'nombre_comercio_concat'})<jupyter_output><empty_output><jupyter_text>Reagrupamos los comercios que tienen mas de un nombre y concatenamos todos sus nombres en una nueva columna **nombre_comercio_concat**<jupyter_code># Unimos con dataframe original agregando la columna nueva con relacion 1:1 con el id
df = df.merge(df_unicos.drop_duplicates(subset=['id_comercio_movimiento'], keep='first'), on='id_comercio_movimiento', how='inner')

# Relacion 1:1 entre id_comercio_movimiento y la columna nueva con los nombres concatenados
df[['id_comercio_movimiento', 'nombre_comercio_concat']].drop_duplicates() \
.groupby(['id_comercio_movimiento']).count() # Deberia dar 3833 que es la cantidad de ids unicos<jupyter_output><empty_output><jupyter_text>1.2. Asegurar el tipo de dato de codigo postal<jupyter_code>df.domicilio_codigo_postal = df[df['domicilio_codigo_postal'].isna()!=True]['domicilio_codigo_postal'].apply(int).apply(str)
df.domicilio_codigo_postal = df.domicilio_codigo_postal.str.strip()<jupyter_output><empty_output><jupyter_text>1.4. Reemplazar caracteres especiales<jupyter_code># Columnas que contienen strings
columnas_str = [
    'nombre_comercio_histo',
    'producto_naranja_movimiento',
    'nombre_local_histo',
    'estado_civil_descripcion',
    'sexo_descripcion',
    'pais',
    'provincia',
    'ciudad',
    'domicilio_barrio',
    'cargo_descripcion_histo',
    'nivel_estudio_descripcion_histo',
    'rel_vivienda_descripcion_histo',
    'nombre_comercio_concat'
]
# Expresion regular que incluye solo caracteres de a-z mayúscula o minúscula y 0-9
regex = r'[^a-zA-Z0-9 ]'
for col in columnas_str:
    df[col] = df[df[col].isnull()==False][col].apply(unidecode)
for col in columnas_str:
    df.loc[:, col] = df[df[col].isnull()==False][col].str.replace(regex, '')<jupyter_output><ipython-input-26-6865719e0677>:2: FutureWarning: The default value of regex will change from True to False in a future version.
  df.loc[:, col] = df[df[col].isnull()==False][col].str.replace(regex, '')<jupyter_text>1.5. Asegurar formato de fecha<jupyter_code># Columnas con fecha

col_fechas = [
    'fecha_vto_cupon_movimiento',
    'fecha_presentacion_movimiento',
    'fecha_cupon_movimiento',
    'fecha_carga_sistema_movimiento',
    'fecha_nacimiento',
    'fecha_extraccion_movimiento',
    'fecha_de_ingreso_histo'
]
df[col_fechas] = df[col_fechas].apply(pd.to_datetime)
df["fecha_cupon_movimiento"].max()<jupyter_output><empty_output><jupyter_text>1.6. Agrupar valores de cargo_descripcion_histo<jupyter_code>df = df[~(df['cargo_descripcion_histo'] == 'GERENTE')].copy()
df = df[~(df['cargo_descripcion_histo'] == 'JUEZ')].copy()
sec_salud = ['ENFERMERO','MEDICO']
sec_edu = ['DOCENTE','PROFESTECNICO']
sec_seg = ['EMPSEGPUBLICA','PORTEROCONSERJ','VIGILADOR']
sec_fin = ['PRESGARANTIA','PRESTCREDITO']
sec_Emp_Com = ['ADMINISTRATIVO','CAJERO','EMPDE COMERCIO','MAESTRANZA','JEFE','ENCARGADO','VENDEDORPROMOT', 'COCINERO','MOZO']
sec_ope = ['CHOFER','INDEPENDIENTE','OPERARIO','PEONEMBARCADOS','PROPIETARIO']
sec_no_ope = ['JUBILADO']
sec_sd = ['SIN DATOS']
#sec_pers_jer = ['GERENTE']
#sec_jud = ['JUEZ']

df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_salud)),'cargo_sector_desc_hist'] = 'Sector_Salud'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_edu)),'cargo_sector_desc_hist'] = 'Sector_Educativo'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_seg)),'cargo_sector_desc_hist'] = 'Sector_Seguridad'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_fin)),'cargo_sector_desc_hist'] = 'Sector_Financiero'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_Emp_Com)),'cargo_sector_desc_hist'] = 'Sector_Empleado_Comercio'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_ope)),'cargo_sector_desc_hist'] = 'Sector_Operativo'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_no_ope)),'cargo_sector_desc_hist'] = 'Sector_No_Operativo'
#df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_pers_jer)),'cargo_sector_desc_hist'] = 'Sector_Personal_Jerarquico'
#df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_jud)),'cargo_sector_desc_hist'] = 'Sector_Judicial'
df.loc[df['cargo_descripcion_histo'].str.contains('|'.join(sec_sd)),'cargo_sector_desc_hist'] = 'Sector_Sin_Datos'
df[df['cargo_sector_desc_hist'].isna()]
df = df.reset_index().drop(columns=['index'])<jupyter_output><empty_output><jupyter_text>-----  2 Data Enrichment  2.1. Cálculo de la edad de la persona<jupyter_code>df['edad_cliente'] = ((df['fecha_cupon_movimiento'].max() - df['fecha_nacimiento'])/np.timedelta64(1, 'Y')).round(0)
df.edad_cliente = df.edad_cliente.astype('Int64', errors='ignore')<jupyter_output><empty_output><jupyter_text>2.2 Cálculo de la antigüedad de la persona<jupyter_code>df['antig_cliente']= ((df['fecha_cupon_movimiento'] - df['fecha_de_ingreso_histo'])/np.timedelta64(1, 'M'))
df['antig_cliente']= df.antig_cliente.apply(np.round).apply(int)
df[['fecha_cupon_movimiento', 'fecha_de_ingreso_histo', 'antig_cliente']]<jupyter_output><empty_output><jupyter_text>2.3 Reagrupamiento de **nombre_comercio_histo**<jupyter_code>df.id_comercio_movimiento.astype(str)
df.id_comercio_movimiento = df.id_comercio_movimiento.astype(str)
df['id_comercio_movimiento'] = df['id_comercio_movimiento'].str.zfill(9) # Se agrega el 0 a la izquierda

df['id_comercio_movimiento'].str.len().value_counts()
# Categorización
def func(row):
    return row.id_comercio_movimiento[0:1]

df['comercio_cat'] = df.apply(func, axis=1)
# Visualizar la columna de comercios recategorizada, segun cuantos id_comercios y cuantas transacciones se abarcan.
df_comercios = df['comercio_cat'].value_counts().to_frame().reset_index().rename(
                                    {'index':'comercio_cat',
                                     'comercio_cat':'frecuencia_ts'}, axis=1)

df_com_freq = pd.DataFrame(df.groupby(['comercio_cat'])['id_comercio_movimiento'].nunique().sort_values(ascending=True)) \
                            .reset_index().rename({
                                'id_comercio_movimiento':'frecuencia_idcom'
                            }, axis=1)
df_com_freq = df_com_freq.merge(df_comercios, on='comercio_cat', how='inner')
df_com_freq

with pd.option_context('display.max_rows', None, 'display.max_columns', None):  # more options can be specified also
    print(df_com_freq.sort_values(by='frecuencia_idcom'))<jupyter_output>comercio_cat  frecuencia_idcom  frecuencia_ts
0            0               249           3760<jupyter_text>3.1 Análisis e imputación de Datos Nulos Se puede observar cierta sistematicidad en la pérdida de valores relacionados a datos personales de los clientes. A continuación procedemos a analizar cada columna con valores faltantes.  Fecha carga sistema movimiento<jupyter_code>df.drop('fecha_carga_sistema_movimiento', axis=1, inplace=True)<jupyter_output><empty_output><jupyter_text>Tipo_prestamo_movimiento, Nombre_local_histo y Fecha_extraccion_movimiento<jupyter_code>df.drop(columns = ['tipo_prestamo_movimiento','nombre_local_histo','fecha_extraccion_movimiento'], inplace=True)<jupyter_output><empty_output><jupyter_text>Datos Faltantes Personales de los clientes<jupyter_code>df[df['sexo_descripcion'] == '']
columna = ['sexo_descripcion']
df['sexo_descripcion'] = df['sexo_descripcion'].replace('', 'Sin Datos')
df['sexo_descripcion'] = df['sexo_descripcion'].replace(np.nan, 'Sin Datos')
#df['sexo_descripcion']= df.sexo_descripcion.fillna(value=np.nan)
#const_imputer = SimpleImputer(missing_values= np.nan, strategy='constant',fill_value="Sin Datos") 
#df.loc[ : , columna] = pd.DataFrame(const_imputer.fit_transform(df.loc[:][columna]) , columns = columna)
#df['sexo_descripcion'] = df['sexo_descripcion'].astype(str)
df['sexo_descripcion'].value_counts(dropna=False)<jupyter_output><empty_output><jupyter_text>Datos faltantes Nivel de Estudio<jupyter_code>df.nivel_estudio_descripcion_histo.value_counts()
columna = ['nivel_estudio_descripcion_histo']
df_faltante_estud = pd.DataFrame(df[df['nivel_estudio_descripcion_histo'].isna()])
df_faltante_estud[columna].isna().sum()
dnis_faltantes = df_faltante_estud.dni_titular_movimiento.values
df[(df['dni_titular_movimiento'].isin(dnis_faltantes)) & (~df['nivel_estudio_descripcion_histo'].isnull())]<jupyter_output><empty_output><jupyter_text>Observamos que la pérdida de datos de nivel de estudio también es sistemática, no pudiendose recuperar este dato, procedemos a imputar dichas filas con Sin datos.<jupyter_code>columna = ['nivel_estudio_descripcion_histo']
#df['sexo_descripcion']= df.sexo_descripcion.fillna(value=np.nan)
const_imputer = SimpleImputer(missing_values= np.nan, strategy='constant',fill_value="Sin Datos") 
df.loc[ : , columna] = pd.DataFrame(const_imputer.fit_transform(df.loc[:][columna]) , columns = columna)
df.nivel_estudio_descripcion_histo.value_counts()<jupyter_output><empty_output><jupyter_text>Datos faltantes Vivienda<jupyter_code>df.rel_vivienda_descripcion_histo.value_counts()
columna = ['rel_vivienda_descripcion_histo']
df_faltante_vivi = pd.DataFrame(df[df['rel_vivienda_descripcion_histo'].isna()])
df_faltante_vivi[columna].isna().sum()
dnis_faltantes = df_faltante_vivi.dni_titular_movimiento.values
df[(df['dni_titular_movimiento'].isin(dnis_faltantes)) & (~df['rel_vivienda_descripcion_histo'].isnull())]
columna = ['rel_vivienda_descripcion_histo']
#df["nivel_estudio_descripcion_histo"]= df.nivel_estudio_descripcion_histo.fillna(value=np.nan)
const_imputer = SimpleImputer(missing_values= np.nan, strategy='constant',fill_value="Sin Datos") 
df.loc[ : , columna] = pd.DataFrame(const_imputer.fit_transform(df.loc[:][columna]) , columns = columna)
df.rel_vivienda_descripcion_histo.value_counts()<jupyter_output><empty_output><jupyter_text>Datos faltantes Edad Procedemos a imputar usando IterativeImputer (en su operación por defecto BayesianRigde) la variable edad_cliente. Para imputar usamos las columnas **edad_cliente** y **antig_cliente**.<jupyter_code>df.shape
df[df['edad_cliente'].isna()]
#df['edad_cliente'] = df['edad_cliente'].replace(np.nan, 0)
#df[df['edad_cliente'].isna()]
#, missing_values=0
imp = IterativeImputer(max_iter=10, random_state=0)
imp_columns = ['edad_cliente', 'antig_cliente']
x_train = df[imp_columns]
imp.fit(x_train)

x_test = df[imp_columns]
x_test = np.round(imp.transform(x_test))
x_test = pd.DataFrame(x_test, columns=imp_columns)
x_test
df['edad_cliente'] = x_test['edad_cliente']
df['edad_cliente'].describe().round(2)
df[df['edad_cliente'].isna()]<jupyter_output><empty_output><jupyter_text>La columna fecha_nacimiento se deja sin imputar debido a que su unica finalidad era obtener la edad del cliente. Por lo tanto, no se tiene en cuenta.<jupyter_code>df.drop('fecha_nacimiento', inplace=True, axis=1)<jupyter_output><empty_output><jupyter_text>Datos Faltantes Geograficos de los clientes  Sistematicidad en la pérdida<jupyter_code>col_geo = ['domicilio_codigo_postal', 'pais', 'provincia', 'ciudad', 'domicilio_barrio']
df[df['domicilio_codigo_postal'].isna()][col_geo].isna().sum()<jupyter_output><empty_output><jupyter_text>Tenemos un total de 24 provincias, el resto son registros NaN.<jupyter_code>df['domicilio_codigo_postal'] = df['domicilio_codigo_postal'].replace('0',np.nan)
df[df['domicilio_codigo_postal']=='0']
df['ciudad'] = df['ciudad'].str.strip()
print('Cantidad de filas con 400 en el CP:',len(df[df['ciudad'].isin(['CAMPO HERRERA'])]))
df[df['ciudad'].isin(['CAMPO HERRERA'])][['pais','provincia','ciudad','domicilio_codigo_postal']].drop_duplicates()
df.loc[df['ciudad'].isin(['CAMPO HERRERA']),'domicilio_codigo_postal'] = '4105'
print('Cantidad de filas con 400 en el CP:',len(df[df['ciudad'].isin(['CAMPO HERRERA'])]))
df[df['ciudad'].isin(['CAMPO HERRERA'])][['pais','provincia','ciudad','domicilio_codigo_postal']].drop_duplicates()
df[df['pais'].isnull()][['pais','provincia','ciudad','domicilio_codigo_postal','domicilio_barrio']].isna().sum()
cp_ciu_nan = df[(df['domicilio_codigo_postal'].isna()!=True) & (df['ciudad'].isna()==True)]['domicilio_codigo_postal'].unique()
df[df['domicilio_codigo_postal'].isin(cp_ciu_nan)][['domicilio_codigo_postal','ciudad']]\
.drop_duplicates().sort_values(by=['domicilio_codigo_postal'])
df.loc[:,'ciudad'] = df['ciudad'].str.upper()
df_dp_cd = df[(df['domicilio_codigo_postal'].isin(cp_ciu_nan)) & (df['ciudad'].isna()==False)]\
            [['domicilio_codigo_postal','ciudad']]\
            .drop_duplicates()\
            .sort_values(by=['domicilio_codigo_postal'])
df_dp_cd = df_dp_cd.rename(columns={'ciudad': 'ciudad_0', 'domicilio_codigo_postal': 'cp'})
df_dp_cd<jupyter_output><empty_output><jupyter_text>Realizamos merge sobre el dataframe creado, e imputamos los datos de ciudad.<jupyter_code>df = df.merge(df_dp_cd.drop_duplicates(subset=['cp'], keep='first'), 
              how='left',
              left_on = 'domicilio_codigo_postal', 
              right_on = 'cp')\
            .drop(columns= ['cp'])
df.head()
# Sustituimos los valores nulos de la columna provincia (provincia_x) por las provincias (en mayus) de la columna
# provincia creada en la anterior unión.
df.loc[df['ciudad'].isnull(), 'ciudad'] = df['ciudad_0'].str.upper()

df[(df['domicilio_codigo_postal'].isin(cp_ciu_nan)) & (df['ciudad'].isna()==True)]\
    [['domicilio_codigo_postal','ciudad','ciudad_0']].drop_duplicates().sort_values(by=['domicilio_codigo_postal'])
df = df.drop(columns=['ciudad_0'])<jupyter_output><empty_output><jupyter_text>Procedemos a imputar **provincia** de la misma manera que **ciudad**.<jupyter_code>cp_prov_nan = df[(df['domicilio_codigo_postal'].isna()!=True) & (df['provincia'].isna()==True)]['domicilio_codigo_postal'].unique()
df[df['domicilio_codigo_postal'].isin(cp_prov_nan)][['domicilio_codigo_postal','provincia']]\
.drop_duplicates().sort_values(by=['domicilio_codigo_postal'])
df.loc[:,'provincia'] = df['provincia'].str.upper()
df_dp_pv = df[(df['domicilio_codigo_postal'].isin(cp_ciu_nan)) & (df['provincia'].isna()==False)]\
            [['domicilio_codigo_postal','provincia']]\
            .drop_duplicates()\
            .sort_values(by=['domicilio_codigo_postal'])
df_dp_pv = df_dp_pv.rename(columns={'provincia': 'provincia_0', 'domicilio_codigo_postal': 'cp'})
df_dp_pv
df = df.merge(df_dp_pv.drop_duplicates(subset=['cp'], keep='first'), 
              how='left',
              left_on = 'domicilio_codigo_postal', 
              right_on = 'cp')\
            .drop(columns= ['cp'])
df.head()
# Sustituimos los valores nulos de la columna provincia (provincia_0) por las provincias (en mayus) de la columna
# provincia creada en la anterior unión.

df.loc[df['provincia'].isnull(), 'provincia'] = df['provincia_0'].str.upper()

df[(df['domicilio_codigo_postal'].isin(cp_ciu_nan)) & (df['provincia'].isna()==True)]\
    [['domicilio_codigo_postal','provincia','provincia_0']].drop_duplicates().sort_values(by=['domicilio_codigo_postal'])
df = df.drop(columns=['provincia_0'])
df[col_geo].isna().sum()<jupyter_output><empty_output><jupyter_text>Imputación con dataset externo<jupyter_code>df_cp = pd.read_csv('https://raw.githubusercontent.com/JIBarrionuevoGaltier/localidades_AR/master/localidades_cp_maestro.csv')
df_cp.head()
# Realizamos una curación de datos sobre este dataset.

df_cp.cp = df_cp[df_cp['cp'].isna()!=True]['cp'].apply(int).apply(str)
df_cp.head()<jupyter_output><empty_output><jupyter_text>Observamos si todos los Códigos Postales de nuestro DataFrame original se encuentran en los objetos del merge.<jupyter_code>df_cp = df_cp[~df_cp['cp'].isnull()]
df_cp.isna().sum()
df_cp = df_cp.rename(columns={'provincia': 'provincia_0'})
df_cp.head()
df.shape<jupyter_output><empty_output><jupyter_text>Procedemos a hacer el merge de los datasets, a través de la columna cp (Codigo Postal)<jupyter_code># Unimos por cp, eliminamos duplicados, conservamos solo cps del dataframe original
df = df.merge(df_cp[['provincia_0','localidad','cp']].drop_duplicates(subset=['cp'], keep='first'), 
                           how='left',
                           left_on = 'domicilio_codigo_postal', 
                           right_on = 'cp')\
                    .drop(columns= ['cp'])
df.head()
df.shape
df.loc[df['provincia'].isnull(), 'provincia'] = df['provincia_0'].str.upper()
df.loc[df['ciudad'].isnull(), 'ciudad'] = df['localidad'].str.upper()
df = df.drop(columns=['provincia_0','localidad'])
df[col_geo].isna().sum()
df.shape
df.drop(columns=['pais','domicilio_barrio'], inplace=True)
columna = ['domicilio_codigo_postal', 'provincia', 'ciudad']
#df['sexo_descripcion']= df.sexo_descripcion.fillna(value=np.nan)
const_imputer = SimpleImputer(missing_values= np.nan, strategy='constant',fill_value="Sin Datos") 
df.loc[ : , columna] = pd.DataFrame(const_imputer.fit_transform(df.loc[:][columna]) , columns = columna)
df[['domicilio_codigo_postal', 'provincia', 'ciudad']].isna().sum()
df.shape<jupyter_output><empty_output><jupyter_text>Nueva eliminación de filas duplicadas<jupyter_code>df = df[~df.duplicated()]<jupyter_output><empty_output><jupyter_text>Variables categoricas (Reagrupación)<jupyter_code>df.shape<jupyter_output><empty_output><jupyter_text>Estado Civil<jupyter_code>df.estado_civil_descripcion.value_counts()<jupyter_output><empty_output><jupyter_text>Observamos que existe una alta frecuencia en las categorías Solteros y Casados, perdiendo representativadad en el resto de los estados civiles, por lo cual decidimos agrupar a la mismas en una misma categoría.<jupyter_code>soltero = ['Solteroa']
casado = ['Casadoa']
otros = ['Divorciadoa','Viudoa','Concubinoa','Separacion de hecho','Novioa']
sin_datos = ['Sin Datos']

df.loc[df['estado_civil_descripcion'].str.contains('|'.join(soltero)),'estado_civil_cat'] = 'Soltero'
df.loc[df['estado_civil_descripcion'].str.contains('|'.join(casado)),'estado_civil_cat'] = 'Casado'
df.loc[df['estado_civil_descripcion'].str.contains('|'.join(otros)),'estado_civil_cat'] = 'Otros'
df.loc[df['estado_civil_descripcion'].str.contains('|'.join(sin_datos)),'estado_civil_cat'] = 'Sin_datos'
df.estado_civil_cat.value_counts()<jupyter_output><empty_output><jupyter_text>Viendo las frecuencias por provincias, vamos a recategorizar sobre una nueva columna, las provincias por regiones.<jupyter_code>df[df['provincia'] == '']
dic_region = {'REGION_NOROESTE': ['JUJUY','SALTA','TUCUMAN','CATAMARCA','SGO DEL ESTERO'],
              'REGION_NORDESTE': ['CHACO','FORMOSA','CORRIENTES','MISIONES'], 
              'REGION_PAMPEANA': ['CORDOBA','BUENOS AIRES','CAPITAL FEDERAL','ENTRE RIOS','LA PAMPA','SANTA FE'], 
              'REGION_CUYO': ['SAN JUAN','SAN LUIS','LA RIOJA','MENDOZA'], 
              'REGION_PATAGONIA': ['SANTA CRUZ','TIERRA DEL FUEGO','RIO NEGRO','NEUQUEN','CHUBUT'],
              'SIN_DATOS': ['Sin Datos', '']}

df['region']= df['provincia']
for i in dic_region:
    df['region'] = df['region'].replace(dic_region[i], i)
    

df.head()
df['region'].value_counts(dropna=False)
df[df['region'].isna()]
df_prod = df['producto_naranja_movimiento'].value_counts().to_frame().reset_index().rename(
                                    {'index':'producto_naranja_movimiento',
                                     'producto_naranja_movimiento':'frecuencia_pr'}, axis=1)

df_prod_dni = pd.DataFrame(df.groupby(['producto_naranja_movimiento'])['dni_titular_movimiento'].nunique() \
                            .sort_values(ascending=True)) \
                            .reset_index()

df_prod_dni = df_prod_dni.merge(df_prod, on='producto_naranja_movimiento', how='inner')
df_prod_dni<jupyter_output><empty_output><jupyter_text>Observamos que la distribución que resulta de esta transformacion no se parece a una normal. Por lo tanto, concluimos que se pierde mucha información y las ganancias obtenidas son bajas. Decidimos entonces, conservar la distribución original de esta variable.  3.4.1 Seleccion de columnas a utilizar<jupyter_code>df_final = df[['dni_titular_movimiento', 'fecha_cupon_movimiento', 'moneda_movimiento', 'producto_naranja_movimiento', 'tipo_producto_tarjeta_movimiento',
               'anio_mes_cupon', 'sexo_descripcion', 'monto_ajustado',
               'cargo_sector_desc_hist', 'edad_cliente', 'antig_cliente', 'comercio_cat', 
               'estado_civil_cat', 'region']]
df_final.shape
df_final
df_final['moneda_movimiento'].value_counts()
df_final.isna().sum()
df_final.shape<jupyter_output><empty_output><jupyter_text>Consideraciones en el tratamiento de las variables  Codificación<jupyter_code>ordinal_ft = 'dni_titular_movimiento'
target = 'monto_ajustado'
#date_num = 'anio_mes_cupon'
num_features = ['anio_mes_cupon', 'edad_cliente', 'antig_cliente', 'fecha_cupon_movimiento']
trans_ft = ['producto_naranja_movimiento', 'tipo_producto_tarjeta_movimiento', 'moneda_movimiento', 'comercio_cat']
client_ft = ['sexo_descripcion', 'cargo_sector_desc_hist', 'estado_civil_cat', 'region']
# Cambiamos los valores para moneda y 

dic_tipo_prod = {42: 'American_express', 44: 'American_express_gold', 0: 'Clasica', 32: 'Master_internacional',
    31: 'Master_nacional', 3: 'Naranja', 4: 'Naranja_virtual', 24: 'Naranja_visa_mini_int', 23: 'Naranja_visa_mini_nac',
    12: 'Naranja_visa_internac.', 11: 'Naranja_visa_nacional', -1: 'Sin_Datos', 22: 'Visa_internacional', 21: 'Visa_nacional'}
dic_monedas = {0: 'Pesos',1: 'Dolares',3: 'Zeta'}
df_final.tipo_producto_tarjeta_movimiento = df_final.tipo_producto_tarjeta_movimiento.replace(dic_tipo_prod)
df_final.moneda_movimiento = df_final.moneda_movimiento.replace(dic_monedas)
df_final.moneda_movimiento.value_counts()
# Ordenamos por fecha
df = df_final.sort_values(by = ['fecha_cupon_movimiento'], ascending = True).copy()
# Transformacion de fecha a numerica
df['fecha_cupon_movimiento'] = df['fecha_cupon_movimiento'].values.astype(float)/10**11
# Transformacion de tipo de moneda a string
df['moneda_movimiento'] = df['moneda_movimiento'].astype(str)

# Codificación
cat_transformer = OneHotEncoder(handle_unknown='ignore')

encoder = ColumnTransformer(
    transformers=[
        ('dni', 'drop', [ordinal_ft]),
        ('num', 'passthrough', num_features),
   #     ('date_num', discretizer, [date_num]),
        ('trans', cat_transformer, trans_ft),
        ('client', cat_transformer, client_ft),
        ('target', 'passthrough', [target])])
df_enc = encoder.fit_transform(df)
cols = encoder.get_feature_names()
df['moneda_movimiento'].value_counts()
cols_o = np.hstack([[ordinal_ft],cols])
df_stack = np.hstack([df[[ordinal_ft]],df_enc])
df_encode = pd.DataFrame(df_stack , columns=cols_o)<jupyter_output><empty_output><jupyter_text>Agrupación por Mes<jupyter_code># Renombramos
df_encode.rename({'edad_cliente':'client__edad',
                         'antig_cliente':'client__antig'}, axis=1, inplace=True)
cols = df_encode.columns

# Funciones de agregacion para cada columna
aggr = {} 
aggr.update(dict.fromkeys([x for x in cols if 'client__' in x], 'max')) # Incluye a edad y antiguedad
aggr.update(dict.fromkeys([x for x in cols if 'trans__' in x], 'sum'))
aggr.update({target:'sum'})
num_features = ['anio_mes_cupon', 'client__edad', 'client__antig', 'fecha_cupon_movimiento']

# Convertimos las columnas categoricas de la transaccion a numericas para poder sumarizarlas
df_encode[num_features + [x for x in cols if 'trans' in x]] = \
df_encode[num_features + [x for x in cols if 'trans' in x]].apply(pd.to_numeric)

# Agrupamiento
group = ['dni_titular_movimiento', 'anio_mes_cupon']

df_mes = df_encode.groupby(group).agg(aggr).reset_index() # edad y antiguedad
df_mes.shape
df_mes<jupyter_output><empty_output><jupyter_text>Guardado Decidimos usar el formato 'parquet' para guardar el dataset, por ser mas liviano y rapido que csv<jupyter_code>df_mes.to_parquet('df_supervisado_agrupado_test.parquet')
df_mes.columns
df_mes.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 1531 entries, 0 to 1530
Data columns (total 37 columns):
 #   Column                               Non-Null Count  Dtype  
---  ------                               --------------  -----  
 0   dni_titular_movimiento               1531 non-null   object 
 1   anio_mes_cupon                       1531 non-null   float64
 2   client__edad                         1531 non-null   float64
 3   client__antig                        1531 non-null   float64
 4   client__x0_Hombre                    1531 non-null   float64
 5   client__x0_Mujer                     1531 non-null   float64
 6   client__x0_Sin Datos                 1531 non-null   float64
 7   client__x1_Sector_Educativo          1531 non-null   float64
 8   client__x1_Sector_Empleado_Comercio  1531 non-null   float64
 9   client__x1_Sector_Financiero         1531 non-null   float64
 10  client__x1_Sector_No_Operativo       1531 non-null   float64
 11  client__x1_Sector_Operativo   [...]
{'fraction_non_alphanumeric': 0.14201840685496667, 'fraction_numerical': 0.012281815296731197, 'mean_word_length': 5.92854001759015, 'pattern_counts': {'":': 1, '<': 160, '<?xml version=': 0, '>': 162, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '40', 'dir': 'jupyter-structured-clean-dedup', 'id': '64943', 'license': 'MIT', 'n_tokens_mistral': 12405, 'n_tokens_neox': 12062, 'n_words': 1771, 'path': 'notebooks/grupo2/Preprocesado_test.ipynb', 'repo_name': 'MDiplo2021/AprendizajeSupervisado', 'size': '31510'}