In [133]:
import pandas as pd
import pandas_profiling
import numpy as np
from pandasql import *
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import string
In [134]:
df = pd.read_pickle('sueldos_contrata.pkl')
In [135]:
pandas_profiling.ProfileReport(df)
Out[135]:
Veamos cómo es la distribución de valores de esta variable
In [136]:
q = "SELECT Estamento,count(*) as Nro FROM df GROUP BY Estamento ORDER BY 2 desc"
sqldf(q)
Out[136]:
Observaciones:
Para la limpieza, vamos a eliminar todas las puntuaciones y reemplazaré algunas abreviaciones. Examinemos primero estos casos raros.
In [137]:
q = '''
SELECT
distinct Estamento,
Cargo,
Servicio
FROM
df
WHERE
Estamento like "_.%"
'''
sqldf(q)
Out[137]:
Observamos que todos pertencen al Servicio de Evaluación Ambiental. Si miramos la fuente, podemos ver que efectivamente no hay referencias del significado de los prefijos mostrados, por lo que se eliminarán.
In [138]:
# No hay stop_words a borrar. Normalmente se usaría stop_words = stopwords.words('spanish')
stop_words = []
In [139]:
# Borramos abreviaciones sin significado
palabras_a_borrar = ['d.','g.','h.','j.']
# Normalmente se usa puntuaciones = string.punctuation
puntuaciones = []
In [140]:
def clean_text(s):
# 1. Pasar todo a minúsculas y separar en arreglo de palabras
ws = s.lower().split()
# 2. Eliminar espaciones innecesarios, junto con conectores gramaticales'''
ws = [w.strip() for w in ws if w not in stop_words]
# 3. Remover puntuación
ws = [w for w in ws if w not in puntuaciones]
# 4.
ws = [w for w in ws if w not in palabras_a_borrar]
return " ".join(ws)
In [141]:
estamentos = df['Estamento'].tolist()
In [142]:
estamentos = pd.Series([clean_text(s) for s in estamentos])
In [143]:
# Uniformemos valores con un diccionario y reemplazamos
d = {
'técnico': 'tecnico',
'fiscalizadores': 'fiscalizador',
'administrativos': 'administrativo',
'profesionales': 'profesional',
'tco no profes': 'tecnico',
'no aplica': 'otro',
'auxiliares': 'auxiliar',
'técnicos': 'tecnico'
}
estamentos.replace(d,inplace=True)
In [145]:
estamentos.sort_values().value_counts()
Out[145]:
No aparecen más problemas.
In [19]:
# Primero veamos la lista de cargos con mayor frecuencia de aparición, ej. con frecuencia > 50
q = "SELECT Cargo,count(*) as Nro FROM df GROUP BY Cargo HAVING Nro>50 ORDER BY 2 desc"
sqldf(q)
Out[19]:
Podemos observar que hay fechas metidas entre medio. Veamos los casos:
In [20]:
s = 'select servicio,count(*) as nro from df where Cargo like "%/2016" group by Cargo'
sqldf(s)
Out[20]:
Nos encontramos con que los casos corresponden a la misma institución (qu.e además tiene el nombre malo). Revisando la página web, podemos ver que el problema es del origen:
Por lo que borraremos estos registros en el merge final
In [147]:
# Pasamos los cargos a una variable para limpiar
cargos = df['Cargo'].tolist()
In [148]:
# Para usar la función "clean_text" usaremos además limpieza de conectores gramaticales de la lengua española
stop_words = stopwords.words('spanish')
palabras_a_borrar = []
puntuaciones = string.punctuation
In [149]:
cargos = pd.Series([clean_text(s) for s in cargos])
In [153]:
# Muestra para ver como quedó
cargos[0:10].to_frame(name='cargo')
Out[153]:
In [154]:
# Veamos una lista general
df_cargos = cargos.to_frame(name='cargo')
q = "SELECT cargo,count(*) as nro FROM df_cargos GROUP BY cargo ORDER BY 2 desc"
sqldf(q)
Out[154]:
Para hacer la limpieza más eficiente, vamos a separar el nombre del cargo en dos componentes:
Luego procederemos a normalizar cada campo:
In [155]:
# Creamos un nuevo Data Frame expandiendo por el primer split. Luego de las correciones los uniremos
df_cargos = df_cargos['cargo'].str.split(' ', 1, expand=True)
df_cargos.columns = ['nivel','ambito']
df_cargos.head()
Out[155]:
In [156]:
# Veamos las correcciones al campo "nivel"
s='select nivel,count(*) as nro from df_cargos group by nivel having nro>1 order by 2 desc'
sqldf(s)
Out[156]:
La lista nos muestra varios tipos de reemplazos. Haremos un diccionario y aplicaremos las correcciones
In [157]:
# Cambiemos acentos
# Uniformemos valores con un diccionario y reemplazamos (sugerencia: cargar desde archivo dict.txt)
d = {
'director/a':'director',
'directora':'director',
'subdirector/a': 'subdirector',
'subdirectora': 'subdirector',
'jefa':'jefe',
'jefe/a':'jefe',
'jefe(a)':'jefe',
'jefatura': 'jefe',
'jefatura,': 'jefe',
'consultor/a,': 'consultor',
'consultora': 'consultor',
'asesora': 'asesor',
'asesor(a)': 'asesor',
'asesoria': 'asesor',
'asesora,':'asesor',
'enc.': 'encargado',
'encargada':'encargado',
'encargada,': 'encargado',
'encargada(s)':'encargado',
'encargado/a': 'encargado',
'encargado(a)': 'encargado',
'encagado': 'encargado',
'coordinador(a)': 'coordinador',
'coordinadora': 'coordinador',
'coordinador/a': 'coordinador',
'abogado/a': 'abogado',
'técnico': 'tecnico',
'tecnico(a)': 'tecnico',
'técnico(a)' : 'tecnico',
'estadístico': 'estadistico',
'fiscalizadora': 'fiscalizador',
'ejecutiva':'ejecutivo',
'operadora': 'operador',
'abogada': 'abogado',
'profesionalde': 'profesional',
'jede': 'jefe',
'tesorera': 'tesorero',
'adm.': 'administrativo',
'administrativo/a': 'administrativo',
'administrativa': 'administrativo',
'administrativo(a)': 'administrativo',
'administrtivo' : 'administrativo',
'analista,': 'analista',
'editora': 'editor',
'guía': 'guia',
'digitadora': 'digitador',
'examinadora': 'examinador',
'secretaria,': 'secretaria',
'transcriptor,': 'transcriptor',
'auditora': 'auditor',
'profesional,' : 'profesional',
'profsional' : 'profesional',
'prosefional' : 'profesional',
'reportera' : 'reportero',
'nutricionista,' : 'nutricionista',
'profecional' : 'profesional',
'restaurador,' : 'restaurador',
'psicólogo' : 'psicologo',
'trabajadora': 'trabajador',
'asistentente': 'asistente',
'supervisora': 'supervisor',
'líder' : 'lider',
'secretario' : 'secretariado',
'secretaria' : 'secretariado',
'admninistrativo' : 'administrativo',
'administración': 'administrativo',
'experta': 'experto'
}
df_cargos["nivel"].replace(d,inplace=True)
In [158]:
s = 'select nivel, count(*) as nro from df_cargos group by nivel having nro>1 order by 2 desc'
print(sqldf(s)['nivel'].tolist())
Revisaremos además:
In [159]:
s = '''
select * from df where Cargo like "asist. %" union
select * from df where Cargo like "sub %" union
select * from df where Cargo like "analisis %" union
select * from df where Cargo like "dpto. %" union
select * from df where Cargo like "gabinete %" union
select * from df where Cargo like "desarrollar %" union
select * from df where Cargo like "programa %" union
select * from df where Cargo like "secretaría %" union
select * from df where Cargo like "oficina %"
'''
sqldf(s)
Out[159]:
Hay varias situaciones:
En los primeros dos casos, reemplazaremos el cargo por el nombre del estamento
In [160]:
d = {}
d = {
'asist.': 'asistente',
'programa': 'administrativo',
'secretaría' : 'administrativo',
'analisis': 'profesional',
'dpto.': 'profesional',
'gabinete': 'profesional',
'desarrollar': 'profesional',
'coordinar': 'profesional',
'programa': 'profesional',
'sub': 'subjefe',
'oficina': 'auxiliar'
}
df_cargos["nivel"].replace(d,inplace=True)
In [161]:
# Veamos ahora los cargos de menor frecuencia
s='select nivel,count(*) as nro from df_cargos group by nivel having nro=1 order by 2 desc'
sqldf(s)
Out[161]:
In [162]:
d = {
'contralora': 'contralor',
'administrativo-chofer' : 'chofer',
'administrativo-estafeta' : 'estafeta',
'administrativo-servicios' : 'admninistrativo',
'enc.unidad' : 'encargado',
'apoyotécnico' : 'técnico',
}
df_cargos["nivel"].replace(d,inplace=True)
In [163]:
# Veamos ahora los cargos de menor frecuencia
s='select * from df where Cargo like "&nbs%"'
sqldf(s)
Out[163]:
Cambiamos por el estamento
In [164]:
d = {'&nbs': 'profesional'}
df_cargos["nivel"].replace(d,inplace=True)
In [165]:
# Hagamos una revisión
print(df_cargos['nivel'].unique().tolist())
Pareciera estar bastante mejor. Veamos ahora el ámbito
In [166]:
s = 'select distinct ambito from df_cargos order by 1 asc'
sqldf(s)
Out[166]:
In [44]:
rep
Out[44]:
In [48]:
s = 'select distinct `Grado EUS` from df'
print(sqldf(s)['Grado EUS'].tolist())
Hay varias correcciones que hacer:
In [76]:
# Limpieza de símbolos
grados = df['Grado EUS']
grados = grados.str.replace('°','')
grados = grados.str.replace('º','')
grados = grados.str.replace(' EUS','')
grados = grados.str.replace('1C','1')
In [77]:
print(grados.unique().tolist())
Examinemos los casos anómalos
In [74]:
s = 'select servicio,count(*) as nro from df where `Grado EUS` like "Reg%" group by servicio'
sqldf(s)
Out[74]:
Todos los datos pertenecen a esta institución. El error viene de la fuente:
Como tenemos la Remuneración Mensualizada Bruta, podemos imputar el grado con el valor de la mediana de asociada a cada grado, ya que la información de grados de cada página es imprecisa para hacer esta imputación
In [81]:
# Reemplazaremos los datos anómalos por Nulo e imputaremos posteriormente
d = {
'I' : '0',
'II' : '0',
'III' : '0',
'Región Metropolitana de Santiago' : '0',
'Región del Biobío' : '0',
'Atacama' : '0',
'Región Aisén del Gral. Carlos Ibáñez del Campo' : '0',
'Región del Libertador Gral. Bernardo OHiggins' : '0',
'Región de Los Lagos' : '0',
'Región de la Araucanía' : '0',
'Región de Los Ríos' : '0',
'Tarapacá' : '0'
}
grados.replace(d,inplace=True)
grados = grados.astype('int')
print(grados.unique().tolist())
In [202]:
s = 'select distinct `Fecha de inicio` from df'
print(sqldf(s)['Fecha de inicio'].tolist())
Un examen preliminar de los datos no evidencia elementos extraños en el campo. Intentaremos convertir a tipo fecha.
In [203]:
fecha_inicio = df['Fecha de inicio']
In [204]:
df.columns
Out[204]:
In [205]:
s = 'select distinct `Fecha de término` from df'
print(sqldf(s)['Fecha de término'].tolist())
En el caso de la fecha de término, observamos que la fecha de término contiene el string "Indefinido", y en ambos campos no hay valores nulos. Reemplazaremos Indefinido por nulo.
In [207]:
# Reemplazo de indefinido por nulo
fecha_termino = df['Fecha de término'].replace('Indefinido',np.nan)
No hay más correcciones. Ensamblaremos el Data Frame final.
In [259]:
# Data Frame final
datos = {
'estamento': estamentos,
'nivel': df_cargos['nivel'].tolist(),
'ambito': df_cargos['ambito'].tolist(),
'grado': grados.values,
'rbm': df['Remuneración Bruta Mensualizada'].tolist(),
'fecha_inicio': fecha_inicio.values,
'fecha_termino': fecha_termino.values,
'servicio': df['servicio'].values
}
df_final = pd.DataFrame(datos)
df_final.head()
Out[259]:
In [260]:
# Cambiamos el order
cols = ['servicio','estamento','grado','nivel','ambito','fecha_inicio','fecha_termino','rbm']
df_final = df_final[cols]
df_final.head()
Out[260]:
In [261]:
s = 'select * from df_final where nivel not like "%/2016"'
df_final = sqldf(s)
In [262]:
df_final['fecha_inicio'] = pd.to_datetime(df_final['fecha_inicio'])
df_final['fecha_termino'] = pd.to_datetime(df_final['fecha_termino'])
In [263]:
df_final.to_pickle('sueldos_contrata_clean.pkl')