datosgobar / data-cleaner

Librería en python para para limpieza de datos, según estándares del Equipo de Datos Argentina.
http://data-cleaner.readthedocs.io/
MIT License
30 stars 12 forks source link

data-cleaner

Coverage Status Build Status PyPI Stories in Ready Documentation Status

Paquete para limpieza de datos, según los estándares de limpieza de la SSIPyGA - Gobierno Abierto Argentina

Nota: Este paquete aún se encuentra en etapa temprana de desarrollo y la interface podría sufrir modificaciones significativas.

Table of Contents generated with DocToc

Instalación

Para uso simple:

pip install data_cleaner

Para desarrollo:

cd package_directory
pip install -e .

Dependencias

Si se va a instalar python desde cero, se recomienda instalar la distribución de Anaconda con Python 2.7, ya que viene con varias librerías preinstaladas.

Tests

nosetests en el root del repositorio clonado (sólo para desarrollo)

Uso de data-cleaner

Lista de reglas

Se puede limpiar un CSV a través de una lista de reglas. El siguiente ejemplo toma un csv, capitaliza todos los strings en la columna "dependencia" y convierte todas las fechas de la columna "fecha_completa_audiencia" que sigan el formato 19-02-2016 09:11 al estándar ISO 8601 2016-02-19T09:11:00-03:00

from data_cleaner import DataCleaner

input_path = "samples/example.csv"
output_path = "samples/clean_example.csv"

rules = [
    {
        "nombre_propio": [
            {"field": "dependencia"}
        ]
    },
    {
        "fecha_completa": [
            {"field": "fecha_completa_audiencia",
             "time_format": "DD-MM-YYYY HH:mm"}
        ]
    }
]

dc = DataCleaner(input_path)
dc.clean_file(rules, output_path)

También se pueden limpiar los datos sin guardar el csv, para analizarlos en memoria.

dc.clean(rules)
dc.df  # accede al DataFrame donde están los datos

Métodos de limpieza

Las reglas de limpieza del cleaner también se pueden utilizar como métodos individuales que devuelven una pandas.DataSeries o un `pandas.DataFrame (en el caso en que el método genere múltiples columnas nuevas).

dependencia_clean = dc.nombre_propio("dependencia")

print dependencia_clean

0    Presidencia De La Nación
1    Presidencia De La Nación
2    Presidencia De La Nación
3    Presidencia De La Nación
4    Presidencia De La Nación
Name: dependencia, dtype: object

Método de limpieza con parámetros.

fecha_audiencia_clean = dc.fecha_completa("fecha_audiencia",
                                          "DD-MM-YYYY HH:mm")

print fecha_audiencia_clean

0    2013-11-12T10:00:00-03:00
1    2014-12-13T10:50:00-03:00
2                          NaN
3                          NaN
4                          NaN
Name: fecha_audiencia, dtype: object

Si se desea que la limpieza practicada perdure en el objeto, se debe especificar el keyword argument inplace=True.

dc.nombre_propio("dependencia", inplace=True)

print dc.df.dependencia

0    Presidencia De La Nación
1    Presidencia De La Nación
2    Presidencia De La Nación
3    Presidencia De La Nación
4    Presidencia De La Nación
Name: dependencia, dtype: object

En todo momento se puede acceder al pandas.DataFrame que contiene la tabla de datos, donde se verán reflejados los cambios luego de aplicar métodos de limpieza con el parámetro inplace=True. Cuando se carga un archivo .shp al DataCleaner, éste contiene un objeto geopandas.GeoDataFrame, que extiende la funcionalidad de pandas para trabajar con geometrías.

dc.df  # Accede al pandas.DataFrame o geopandas.GeoDataFrame del cleaner.

Para guardar el pandas.DataFrame en cualquier momento, probablemente luego de probar y aplicar algunas transformaciones, usar:

dc.save(output_path)

Si se trata de un GeoDataFrame, puede guardarse el archivo en formatos CSV, GeoJSON, y KML. Para CSVs, se puede especificar el nombre para la columna de geometría con un argumento opcional. El nombre por defecto es "geojson".

dc = DataCleaner('samples/provincias/provincias.shp')
dc.save('provincias.csv', geometry_name='geojson')  # Guarda un archivo CSV con columna de geometría.
dc.save('provincias.geojson') o dc.save('provincias.json')  # Guarda un archivo GeoJSON.
dc.save('provincias.kml')  # Guarda un archivo KML.

El método DataCleaner.save() redirige al método pandas.DataFrame.to_csv(), y por lo tanto tienen los mismos argumentos.

Encoding del input, y otros

Se asume que el input es un csv codificado en utf-8, separado por comas y que usa comillas dobles para el enclosing. Si alguno de estos parámetros (especialmente el encoding) es diferente, debe especificarse.

dc = DataCleaner("ugly.csv", encoding="latin1", sep=";", quotechar="'")

Limpieza automática

Formato del archivo limpio

Luego de la limpieza, los datos se guardan siempre en un archivo CSV, codificado en utf-8 separado por "," y usando '"' como caracter de citas.

Nombres de los campos

Los nombres de los campos se normalizan automáticamente. Sólo el uso de caracteres alfanuméricos ASCII y "_" está permitido. Los campos deben nombrarse con palabras en minúsculas separadas por guión bajo. Para esto el objeto:

Saltos de línea

No se permiten saltos de línea en los valores, al momento de crear un objeto ^DataCleaner^ se reemplazan todos los saltos de línea que estén dentro del caracter de enclosing (usualmente comillas dobles '"') por un espacio " ".

Template de script de limpieza

Para realizar la limpieza de un archivo CSV de datos con data-cleaner se sugiere utilizar el template de script de limpieza. Este permite correr la limpieza desde la línea de comandos e implementar pasos de limpieza personalizados que exceden las funcionalidades del paquete.

Reglas de limpieza

Son diccionarios cuyas keys son los nombres de las reglas de limpieza y cuyos values son: (a) lista de columnas donde aplicar la regla -en el caso en que la regla no requiera otros parámetros- o (b) lista de parámetros que necesita la regla para funcionar -donde el primer parámetro es siempre el campo donde aplicar la regla-.

Renombrar columnas (renombrar_columnas)

Renombra columnas de la tabla de datos.

Especificación:

{"renombrar_columnas": [
    {"field": "columna_actual_1", "new_field": "columna_nueva_1"},
    {"field": "columna_actual_2", "new_field": "columna_nueva_2"},
    {"field": "columna_actual_3", "new_field": "columna_nueva_3"}
]}

Ejemplo:

{"renombrar_columnas": [
    {"field": "aut_dependencia", "new_field": "dependencia"},
    {"field": "sujeto_obligado_audiencia", "new_field": "sujeto_obligado"}
]}

Remover columnas (remover_columnas)

Remueve campos de la tabla de datos.

Entre otras cosas, se puede utilizar para remover los campos originales -no recomendado- que dieron origen a múltiples campos nuevos cuando se utilizó alguna regla de split.

Especificación:

{"remover_columnas": [
    {"field": "columna_a_remover_1"},
    {"field": "columna_a_remover_2"}
]}

Ejemplo:

{"remover_columnas": [
    {"field": "dependencia"},
    {"field": "fecha_completa_audiencia"}
]}

Remover filas duplicadas (remover_filas_duplicadas)

Remueve filas duplicadas de la tabla de datos.

Se puede usar para eliminar filas duplicadas basandose en comparacion de todas las columnas de la tabla o en base a un subconjunto de columnas

Especificación:

Comparación en base a todas las columnas

{"remover_filas_duplicadas": [
    {"all_fields": True},
]}

Comparación en base a un subconjunto de columnas

{"remover_filas_duplicadas": [
    {"all_fields": False, "fields": ["col1", "col2"]},
]}

Ejemplo:

{"remover_filas_duplicadas": [
    {"all_field": False, "fields": ["id"]}
]}

Capitalizar nombres propios (nombre_propio)

Normaliza todas las palabras que encuentra poniéndolas en minúsculas y capitalizando la primera letra de cada una.

Se aplica a todos aquellos campos de datos que tengan nombres de personas. En el caso de direcciones, ciudades, países, organismos e instituciones debe aplicarse con mucha cautela, existen casos donde esta regla de limpieza hace más mal que bien (ej.: las instituciones pueden tener siglas, que no corresponde capitalizar).

Argumentos opcionales:

Especificación:

{"nombre_propio": [
    {"field": "columna_1"},
    {"field": "columna_2", "lower_words": ["lower_word1", "lower_word2"]}
]}

Ejemplo:

{"nombre_propio": [
    {"field": "dependencia", "lower_words": ["en", "la"]}
    {"field": "dependencia", "lower_words": []}
    {"field": "dependencia"}
]}

Dar formato a correo electrónico (mail_format)

Analiza todas las direcciones de correo electrónico en cada fila de una campo y les da el formato estandar definido. Es decir, las pasa todas a minúsculas y las separa con comas.

Argumentos opcionales:

Especificación:

{"mail_format": [
    {"field": "columna_1"},
    {"field": "columna_2"}
]}

Ejemplo:

{"mail_format": [
    {"field": "correo_electronico"}
]}

Normalizar strings (string)

Utiliza el algoritmo Key Collision Fingerprint para clusterizar strings con el mismo contenido, normalizando capitalización, acentos, caracteres especiales, etc.

Este algoritmo busca unificar la forma de escribir strings que contienen idénticas palabras (cadenas de caracteres alfanuméricos separados por espacios) pero difieren en otros aspectos. Para más detalle ver Key Collision Methods de OpenRefine. La implementación que se utiliza es una adaptación de esta, publicada en Github por Tyler Weirick.

Argumentos opcionales:

Especificación:

{"string": [
    {"field": "columna_1"},
    {"field": "columna_2"}
]}

Ejemplo:

{"string": [
    {"field": "dependencia"},
    {"field": "lugar_audiencia"},
    {"field": "sujeto_obligado"},
    {"field": "solicitante"}
]}

Reemplazar listas de strings por valores predefinidos (reemplazar)

Reemplaza listas de strings por un valor predefinido que el usuario decide que representa a todas. Solo sirve para reemplazar valores completos

Argumentos opcionales:

Especificación:

{"reemplazar": [
    {
     "field": "columna",
     "replacements": {"Nuevo1": ["Viejo"], "Nuevo2": ["ViejoA", "ViejoB"]}
    }
]}

Ejemplo:

{"reemplazar": [
    {
    "field": "tipo",
    "replacements": {"Servicios": ["Serv"], "Otros": ["Otro", "Loc"]}
    }
]}

En este ejemplo si el campo tipo tuviese el valor "Serv de venta" no sería reemplazado, mientras que si tuviese el valor "Serv" sería reemplazado por "Servicios"

Reemplazar partes de valores (substrings) por otros (reemplazar_string)

Reemplaza listas de substrings por otro substring. A diferencia del método reemplazar que reemplaza directamente valores completos, reemplazar_string hace reemplazos parciales. Es una versión más sencilla de string_regex_substitute que no permite evaluar expresiones regulares.

Argumentos opcionales:

Especificación:

{"reemplazar_string": [
    {
     "field": "columna",
     "replacements": {"Nuevo1": ["Viejo"], "Nuevo2": ["ViejoA", "ViejoB"]}
    }
]}

Ejemplo:

{"reemplazar_string": [
    {
    "field": "tipo",
    "replacements": {"Servicios": ["Serv"], "Otros": ["Otro", "Loc"]}
    }
]}

En este ejemplo si el campo tipo tuviese el valor "Serv de venta" sería reemplazado por "Servicios de Venta".

Normalizar fecha completa (fecha_completa)

Estandariza un campo con fecha y hora a su representación en el estándar ISO 8601 (YYYY-MM-DDTHH:MM:SS[.mmmmmm][+HH:MM]).

Ej.: 05-02-2016 14:53 a 2016-02-05T14:53:00-03:00

Para el parsing de fechas se utiliza la librería arrow. En la regla debe especificarse el formato temporal en que la fecha está expresada en la tabla de datos original. El resultado siempre se convertirá a ISO 8601 cuando sea posible, ante cualquier error se dejará la celda vacía.

Argumentos opcionales:

Especificación:

{"fecha_completa": [
    {"field": "columna", "time_format": "DD-MM-YYYY HH:mm"}
]}

Ejemplo:

{"fecha_completa": [
    {"field": "fecha_completa_audiencia", "time_format": "DD-MM-YYYY HH:mm"}
]}

Normalizar fecha simple (fecha_simple)

Estandariza un campo sin hora, día o mes a su representación en el estándar ISO 8601, obviando aquella parte de la representación ISO para la que no se cuenta con datos suficientes.

Ej.: 05-02-2016 a 2016-02-05 Ej.: 02-2016 a 2016-02

Argumentos opcionales:

Especificación:

{"fecha_simple": [
    {"field": "columna1", "time_format": "DD-MM-YYYY"},
    {"field": "columna2", "time_format": "MM-YYYY"}
]}

Ejemplo:

{"fecha_simple": [
    {"field": "fecha", "time_format": "DD-MM-YYYY"},
    {"field": "mes", "time_format": "MM-YYYY"}
]}

Normalizar fecha separada en múltiples campos (fecha_separada)

Estandariza una fecha completa donde distintos componentes de la misma están separados en varios campos, a su representación en el estándar ISO 8601.

Argumentos opcionales:

Especificación:

{"fecha_separada": [
    {"fields": [["campo1", "DD-MM-YYYY"], ["campo2", "HH:mm"]],
     "new_field_name": "audiencia"}
]}

Ejemplo:

{"fecha_separada": [
    {"fields": [["fecha_audiencia", "DD-MM-YYYY"], ["hora_audiencia", "HH:mm"]], "new_field_name": "audiencia"}
]}

Separar campos mediante un separador simple (string_simple_split)

Separa strings de un campo en múltiples campos, mediante separadores simples.

Argumentos opcionales:

Especificación:

{"string_simple_split": [
    {"field": "campo",
    "separators": ["separador_A", "separador_B"],
    "new_field_names": ["sufijo_nuevo_campo_1", "sufijo_nuevo_campo_2"]}
]}

Ejemplo:

{"string_simple_split": [
    {"field": "sujeto_obligado",
    "separators": [", Cargo:", "Cargo:"],
    "new_field_names": ["nombre", "cargo"]}
]}

Separar campos mediante una expresión regular (string_regex_split)

(NO IMPLEMENTADO)

Separar campos mediante una parsing expression grammar (string_peg_split)

Utiliza parsing expression grammars para separar strings de un campo en múltiples campos.

Las PEG son una forma de utilizar expresiones regulares de más alto nivel, que facilita la creación de reglas bastante complejas. La librería que se utiliza en este paquete es parsley.

Todas las PEG que se escriban para este paquete, deben contener una regla values cuyo output sea una lista de los valores que se quiere extraer. Cuando la PEG utilizada falle, el paquete dejará un valor nulo para esa celda.

Argumentos opcionales:

Especificación:

{"string_peg_split": [
    {"field": "campo",
    "grammar": "grammar",
    "new_field_names": ["sufijo_nuevo_campo_1", "sufijo_nuevo_campo_2"]}
]}

Ejemplo:

{"string_peg_split": [
    {
    "field": "solicitante",
    "grammar": """
    allowed_char = anything:x ?(x not in '1234567890() ')
    nombre = ~('DNI') <allowed_char+>:n ws -> n.strip()
    number = <digit+>:num -> int(num)

    nom_comp = <nombre+>:nc -> nc.strip()
    cargo = '(' <nombre+>:c ')' -> c.strip()
    dni = ','? ws 'DNI' ws number:num -> num

    values = nom_comp:n ws cargo?:c ws dni?:d ws anything* -> [n, c, d]
    """,
    "new_field_names": ["nombre", "cargo", "dni"]
    }
]}

Manipular y reemplazar contenido de campos mediante una expresión regular (string_regex_substitute)

Es análogo al método sub de la libreria de python re.

Argumentos opcionales:

Especificación:

{"string_regex_substitute":[
    {"field": "campo1",
    "regex_str_match": "str_regex_match1",
    "regex_str_sub": "str_regex_replace1"},
    {"field": "campo2",
    "regex_str_match": "str_regex_match2",
    "regex_str_sub": "str_regex_replace2"}
]}

Ejemplos:

Reemplaza punto y comas por comas:
{"string_regex_substitute":[
    {"field": "norma_competencias_objetivos",
    "regex_str_match": ";",
    "regex_str_sub": ","}
]}

Cambia el orden de una cadena entre paréntesis:
{"string_regex_substitute":[
    {"field": "nombre_cargo",
    "regex_str_match": "(?P<cargo>\(.+\))(?P<nombre>.+)",
    "regex_str_sub": "\g<nombre> \g<cargo>"}
]}
"(presidente)Juan José Perez." pasaría a ser "Juan José Perez. (presidente)"

Simplificar un objeto con datos de geometría (líneas, polígonos, etc.)

Simplifica una geometría para que resulte en un objeto de menor tamaño y complejidad, que a la vez retenga sus características esenciales.

Especificación:

{"simplificar_geometria": [
    {"tolerance": nivel}
]}

Ejemplos:

{"simplificar_geometria": [
    {"tolerance": 0.5}
]}

Normalizar y enriquecer unidades territoriales

Normaliza y enriquece unidades territoriales utilizando la API del Servicio de Normalización de Datos Geográficos.

Argumentos obligatorios:

Argumentos opcionales:

Especificación

{"normalizar_unidad_territorial": [
    {
        "field": "campo",
        "entity_level": "nivel_entidad"
    }
]}

Ejemplos:

{"normalizar_unidad_territorial": [
        {
            "field": "nombre",
            "entity_level": "provincia"
        }
]}
{"normalizar_unidad_territorial": [
        {
            "field": "nombre",
            "entity_level": "localidad",
            "add_code": True,
            "add_centroid": True,
            "add_parents": ['provincia', 'departamento', 'municipio'],
            "keep_original": True,
            "filters": {
                "provincia_field": "provincia",
                "departamento_field": "departamento",
                "municipio_field": "municipio"
            }
        }
]}

Contacto

Te invitamos a [creanos un issue](https://github.com/datosgobar/data-cleaner/issues/new?title=Encontre un bug en data-cleaner) en caso de que encuentres algún bug o tengas feedback de alguna parte de data-cleaner.

Para todo lo demás, podés mandarnos tu comentario o consulta a datos@modernizacion.gob.ar.