datosgobar / data-cleaner Goto Github PK
View Code? Open in Web Editor NEWLibrería en python para para limpieza de datos, según estándares del Equipo de Datos Argentina.
Home Page: http://data-cleaner.readthedocs.io/
License: MIT License
Librería en python para para limpieza de datos, según estándares del Equipo de Datos Argentina.
Home Page: http://data-cleaner.readthedocs.io/
License: MIT License
Data frame agrega columnas con el mismo nombre, lo que no está bien. El normalizador de fields también debería chequear esto y levantar una excepción.
El metodo DataCleaner.clean llama a getattr
para definir el metodo a ejecutar pero chequea que el mismo este expuesto. No es un eval pero casi.
Seria mejor si se agrega un decorador @is_cleaner_rule
(o algo similar) para identificar a los metodos que efectivamente implementan reglas.
La inspiracion viene de cherrypy:
https://cherrypy.readthedocs.org/en/3.2.6/concepts/exposing.html
Evalular la mejor manera de implementar esto.
Esta funcion deberia servir para definir el orden en la cual van a estar los campos del dataframe.
Más específicamente, me refiero al caso donde hay un archivo por provincia (voy a asumir que no es un caso aislado el que me estoy encontrando y que vale la pena agregar esto al data cleaner).
La idea sería consolidar todos los archivos en uno, agregando una columna "Provincia" a ser completada tal vez por el nombre del archivo o algún otro parámetro a discutir.
Actuamente data_cleaner/data_cleaner.py
superó las mil lineas de código. Creo que podria dividirse en varios archivos para mejorar el mantenimiento del repo y la incorporación de nuevas reglas.
La clase DataCleaner
tiene metodos helpers que pueden extraerse a archivos independientes. Ademas, creo que lo ideal seria tener alguna carpeta de rules con archivos individuales por cada regla (algo onda data_cleaner/rules/*.py
)
Creo que tambien ayudaria a #12 si la separación se encara en conjunto.
El visualizador de CKAN no funciona bien con polígonos complejos o pesados. Existen otros casos de uso en los que al transformar de un formato geográfico a otro puede ser útil o necesario simplificar polígonos.
Para esto crear una nueva regla de limpieza/transformación que permita simplificar polígonos, siguiendo la interfaz de todas las otras reglas de limpieza.
Esto se nota particularmente con el formato de los floats.
keep_original=True, inplace=False
El comportamiento que esperaba es que el datacleaner no modifique los valores de las celdas. Esto no es lo que sucede con floats. Ver ejemplo a continuación
# Setup del environment
$ mkdir test
$ cd test
$ virtualenv venv
$ . venv/bin/activate
$ pip install data_cleaner
Una vez que está setupeado el environment, crear los siguientes archivos
example_input.csv
lat,long
-34.6091725340289,-58.3842972567038
script.py
from data_cleaner import DataCleaner
input_path = "example_input.csv"
output_path = "example_output.csv"
dc = DataCleaner(input_path)
dc.clean_file([], output_path)
Ejecutar el script via python script.py
y comparar los csv de input y output
$ diff example_input.csv example_output.csv
2c2
< -34.6091725340289,-58.3842972567038
---
> -34.609172534,-58.384297256703796
$ uname -a # version del sistema operativo (mac os)
Darwin ARAR013089 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.15
$ pip --version
pip 18.1 from /Users/iheredia/personal/test/venv/lib/python2.7/site-packages/pip (python 2.7)
$ pip freeze
arrow==0.13.0
attrs==18.2.0
backports.functools-lru-cache==1.5
certifi==2018.11.29
chardet==3.0.4
Click==7.0
click-plugins==1.0.4
cligj==0.5.0
coverage==4.5.2
cycler==0.10.0
data-cleaner==0.1.20
descartes==1.1.0
enum34==1.1.6
Fiona==1.8.4
funcsigs==1.0.2
geopandas==0.2.1
idna==2.8
kiwisolver==1.0.1
matplotlib==2.2.3
mock==2.0.0
munch==2.3.2
nose==1.3.7
numpy==1.16.0
pandas==0.23.4
Parsley==1.3
pbr==5.1.1
PyCRS==0.1.3
pyparsing==2.3.1
pyproj==1.9.6
python-dateutil==2.7.5
pytz==2018.9
requests==2.21.0
Shapely==1.6.4.post2
six==1.12.0
subprocess32==3.5.3
unicodecsv==0.14.1
Unidecode==1.0.23
urllib3==1.24.1
xlrd==1.2.0
Contexto
Una función común en la limpieza y transformación de tablas con columnas que tienen unidades territoriales es normalizar textos no estandarizados por su nomenclatura oficial y añadir códigos, u otro tipo de datos adicionales relacionados a la unidad.
Propuesta
Implementar una regla de normalización de unidades territoriales que explote la API de Georreferenciación del Min. Modernización para esto.
normalizar_unidad_territorial(self, field, entity_level, filters=None, add_code=False, add_centroid=False, add_parents=None, keep_original=False, inplace=False)
filters = {
"field_name": "entity_level",
"provincia_nombre": "provincia",
"departamento_nombre": "departamento"
}
add_parents = ["provincia", "departamento"]
entity_level
declarado (ie. no se puede filtrar la normalización de departamentos por localidades o de provincias por departamentos.add_code
usa el nombre de columna estándar "{entity_level}_id"add_centroid
usa "{entity_level}_centroid_lat" y "{entity_level}_centroid_lon"Notas
Estrategia de iteraciones para la implementación de esta regla:
Implementar interfaz mínima viable: normalizar_unidad_territorial(self, field, entity_level, keep_original=False, inplace=False)
Agregar códigos: normalizar_unidad_territorial(self, field, entity_level, add_code=False, keep_original=False, inplace=False)
Agregar centroide: normalizar_unidad_territorial(self, field, entity_level, add_code=False, add_centroid=False, keep_original=False, inplace=False)
Agregar padres: normalizar_unidad_territorial(self, field, entity_level, add_code=False, add_centroid=False, add_parents=None, keep_original=False, inplace=False)
Agregar filtros: normalizar_unidad_territorial(self, field, entity_level, filters=None, add_code=False, add_centroid=False, add_parents=None, keep_original=False, inplace=False)
Después de cada iteración hay que
--- (1) modificar documentación
--- (2) agregar tests
---- (3) submitir PR a fede primero y a beni después
Actualmente el csv debe ser "utf-8" o el usuario debe saber y consignar qué encoding tiene el archivo. Debería haber una librería en python que permita adivinar el encoding de un csv de forma segura para que el usuario no tenga que saberlo.
Dependencias: este issue requiere la implementación previa de #43
Una vez que DataCleaner
es capaz de entender internamente un SHP y guardarlo en CSV (geojson), se puede plantear fácilmente la capacidad de transformarlo a otros formatos:
La experiencia buscada descansa en que para el usuario esta transformación sea transparente, manejada por DataCleaner
sólo con la lectura de las extensiones de entrada y salida:
from data_cleaner import DataCleaner
input_path = "samples/provincias/provincias.shp"
output_path = "samples/provincias.geojson"
output_path = "samples/provincias.kml"
dc = DataCleaner(input_path)
dc.save(output_path)
please, someone who works with the datacleaner tool that can tell me the difficulties that arise when working with this tool for an investigation that I am doing
El nombre del campo "fecha_audiencia" no coincide entre el README y example.csv, lo cual produce error al tratar de correr el ejemplo del readme.
Hoy el data-cleaner
sólo admite la interpretación de archivos CSV que internamente se convierten a DataFrame de pandas para ofrecer una serie de reglas de limpieza y transformación.
El archivo limpio y transformado se puede guardar nuevamente en CSV mediante el método DataCleaner.save().
Se busca agregar la capacidad del objeto DataCleaner de leer/entender un SHP para poder aplicar las mismas reglas de transformación y limpieza disponible para usar en un CSV, y luego transformar el formato guardando los datos geográficos en un CSV.
Convertir un SHP en CSV implica mayormente trabajar en la creación de una columna geom en el CSV que contenga las figuras geométricas que en el caso del SHP se guardan en un formato no tabular específico, en forma de un string geojson.
Research previa: El uso de GeoPandas como alternativa a Pandas para que el objeto DataCleaner
lea SHP es probablemente la mejor posibilidad a explorar. Permite mantener la interfaz interna construida sobre la base de Pandas para las reglas de transformación y limpieza, y permite usar funcionalidades geográficas para leer un SHP y convertirlo a GEOJSON.
La experiencia de uso de la librería buscada sería:
from data_cleaner import DataCleaner
input_path = "samples/provincias/provincias.shp"
output_path = "samples/provincias.csv"
dc = DataCleaner(input_path)
dc.save(output_path)
Otros requerimientos: revisar la interfaz y el estilo de implementación actual de DataCleaner
para que el desarrollo vaya en línea con la idea de la librería en general. Crear tests para la nueva funcionalidad siguiendo el estilo de los tests ya implementados para otras funcionalidades.
Quiza lo mejor es que tire cuantos registros (no vacios) no se pudieron parsear como fecha para cada columna.
Similar al metodo para substituciones con regex pero sin regex y de manera trivial
El decorador deberia reemplazar todos los segmentos de codigo de data_cleaner.py como el que sigue:
if inplace:
self._update_series(field=field, sufix=sufix,
keep_original=keep_original,
new_series=capitalized)
`
(a) repensar el nombre de la regla de clustering, ya que hemos hablado de bajarle su nivel de enforcement -ya no aplicaría utilizarlo para la mayoría de los strings, necesariamente, así que su nombre le queda grande
(b) repensar el algoritmo de fingerprint, puede haber formas más inocentes y menos inocentes de usarlo, por ejemplo añadiendo o no el orden de los tokens como criterio de clusterización
Contexto
Data-cleaner va a empezar a usar la API de Georreferenciación para incorporar reglas de limpieza.
Propuesta
Armar una lista de reglas útiles que podrían explotar la API de Georref.
Agregar una nueva regla para forzar la interpretación o transformación de un campo hacia determinado tipo de datos.
Sphinx toma todos los docstrings y los métodos públicos del repo y genera una documentación prolija automáticamente. Estaría bueno hacerlo como caso de prueba cuando tengamos tiempo.
¿Cuál es el comportamiento que esperabas y cuál fue el comportamiento recibido?
Poder utilizar el data-cleaner con python3
¿Cuáles son los pasos para reproducir el bug?
Teniendo python 3 y virtualenv instalado:
$ virtualenv -p python3 venv
$ . venv/bin/activate
$ pip install data-cleaner
Detalles del contexto: ¿qué sistema operativo, qué versión de navegador estás usando, por ejemplo?
$ uname -a # version del sistema operativo (mac os)
Darwin ARAR013089 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
$ python --version
Python 3.6.3
$ pip --version
pip 18.1 from /Users/iheredia/personal/test_data-cleaner/venv/lib/python3.6/site-packages/pip (python 3.6)
¿Tenés algún stack trace o captura de pantalla? Adjuntalo.
$ pip install data_cleaner
Collecting data_cleaner
Using cached https://files.pythonhosted.org/packages/87/41/e4fe93373f2897bde6d5c8fe8036b08b96ad7e6307be29d8da22a63c7030/data-cleaner-0.1.18.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/private/var/folders/hs/cx3bmdc53ng9m4vgwp9yqcyw35lmdf/T/pip-install-bxbhc_10/data-cleaner/setup.py", line 21, in <module>
shutil.copy("README.md", os.path.join("docs", "README.md"))
File "/Users/iheredia/personal/test_data-cleaner/venv/lib/python3.6/shutil.py", line 241, in copy
copyfile(src, dst, follow_symlinks=follow_symlinks)
File "/Users/iheredia/personal/test_data-cleaner/venv/lib/python3.6/shutil.py", line 121, in copyfile
with open(dst, 'wb') as fdst:
FileNotFoundError: [Errno 2] No such file or directory: 'docs/README.md'
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/hs/cx3bmdc53ng9m4vgwp9yqcyw35lmdf/T/pip-install-bxbhc_10/data-cleaner/
La normalización se hace automáticamente en 3 ocasiones: (a) cuando se carga el archivo, (b) cuando se lee cualquier regla -el field que pone el usuario se normaliza- y (c) cuando se utiliza la regla "renombrar_columnas", que no permite se renombren columnas violando la normalización de campos decidida.
El punto a discutir e implementar es que el paquete emita un warning cada vez que tenga que hacer una normalización, en todos los casos (a, b y c) o en algunos de ellos. Adicionalmente discutir si además de emitir el warning debería (1) proceder con la normalización dando el aviso, (2) permitir al usuario realizar una acción que viola la normalización de fields decidida o (3) levantar una excepción parando todo el proceso hasta que el usuario corrija.
Debería investigarse alguna forma elegante de implementar esto con un decorador.
Contexto
Se desea definir algunos criterios básicos para generar mapas y dejarlos plasmados como defaults en el código de DataCleaner.
Propuesta
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
ax = world.plot(color='lightgrey', linewidth=0.5, edgecolor='white', figsize=(15,5))
ax.set_xlim(-75, -50)
ax.set_ylim(-40, -25)
gdf.plot(ax=ax)
El comportamiento que esperaba es que el datacleaner solo dispare warnings de nombres de columnas cuando corresponde, lo cual no parece estar sucediendo por dos temas independientes.
Por un lado, se disparan warnings para todos los nombres de columnas que se encuentren en el input al momento de crear la instancia del data cleaner. Por otro lado, se disparan de vuelta warnings al momento de aplicar las reglas. Esto molesta en dos sentidos: primero porque hay warnings duplciados, y segundo porque hay columnas que ya se que hay que renombrar y su nuevo nombre se encuentra dentro de las reglas que le paso al datacleaner, con lo cual esperaria que en este caso no dispare warnings.
Ver el ejemplo a continuación. Al ejecutar el ejemplo esperaria que solo se genere un warning por la columna llamda TIRAME WARNINGS
, pero obtengo dos warnings para NO ME TIRES WARNINGS
y uno para TIRAME WARNINGS
.
# Setup del environment
$ mkdir test
$ cd test
$ virtualenv venv
$ . venv/bin/activate
$ pip install data_cleaner
Una vez que está setupeado el environment, crear los siguientes archivos
example_input.csv
NO ME TIRES WARNINGS,TIRAME WARNINGS
1,2
script.py
from data_cleaner import DataCleaner
input_path = "example_input.csv"
output_path = "example_output.csv"
rules = [
{
"renombrar_columnas": [
{"field": "NO ME TIRES WARNINGS", "new_field": "ya_se_que_tengo_que_renombrar_esta_columna"},
]
}
]
DataCleaner(input_path).clean_file(rules, output_path)
Al ejecutar el script via python script.py
se obtienen los siguientes warnings:
/Users/iheredia/personal/test/venv/lib/python2.7/site-packages/data_cleaner/data_cleaner.py:196: UserWarning:
El campo "NO ME TIRES WARNINGS" no sigue las convenciones para escribir
campos (sólo se admiten caracteres alfanuméricos ASCII en
minúsculas, con palabras separadas por "_"). DataCleaner
normaliza automáticamente los campos en estos casos, lo
que puede llevar a resultados inesperados.
El nuevo nombre del campo normalizado es: "no_me_tires_warnings".
Método que llamó al normalizador de campos: __init__
warnings.warn(msg)
/Users/iheredia/personal/test/venv/lib/python2.7/site-packages/data_cleaner/data_cleaner.py:196: UserWarning:
El campo "TIRAME WARNINGS" no sigue las convenciones para escribir
campos (sólo se admiten caracteres alfanuméricos ASCII en
minúsculas, con palabras separadas por "_"). DataCleaner
normaliza automáticamente los campos en estos casos, lo
que puede llevar a resultados inesperados.
El nuevo nombre del campo normalizado es: "tirame_warnings".
Método que llamó al normalizador de campos: __init__
warnings.warn(msg)
/Users/iheredia/personal/test/venv/lib/python2.7/site-packages/unidecode/__init__.py:46: RuntimeWarning: Argument <type 'str'> is not an unicode object. Passing an encoded string will likely have unexpected results.
_warn_if_not_unicode(string)
/Users/iheredia/personal/test/venv/lib/python2.7/site-packages/data_cleaner/data_cleaner.py:196: UserWarning:
El campo "NO ME TIRES WARNINGS" no sigue las convenciones para escribir
campos (sólo se admiten caracteres alfanuméricos ASCII en
minúsculas, con palabras separadas por "_"). DataCleaner
normaliza automáticamente los campos en estos casos, lo
que puede llevar a resultados inesperados.
El nuevo nombre del campo normalizado es: "no_me_tires_warnings".
Método que llamó al normalizador de campos: renombrar_columnas
warnings.warn(msg)
$ uname -a # version del sistema operativo (mac os)
Darwin ARAR013089 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.15
$ pip --version
pip 18.1 from /Users/iheredia/personal/test/venv/lib/python2.7/site-packages/pip (python 2.7)
$ pip freeze
arrow==0.13.0
attrs==18.2.0
backports.functools-lru-cache==1.5
certifi==2018.11.29
chardet==3.0.4
Click==7.0
click-plugins==1.0.4
cligj==0.5.0
coverage==4.5.2
cycler==0.10.0
data-cleaner==0.1.20
descartes==1.1.0
enum34==1.1.6
Fiona==1.8.4
funcsigs==1.0.2
geopandas==0.2.1
idna==2.8
kiwisolver==1.0.1
matplotlib==2.2.3
mock==2.0.0
munch==2.3.2
nose==1.3.7
numpy==1.16.0
pandas==0.23.4
Parsley==1.3
pbr==5.1.1
PyCRS==0.1.3
pyparsing==2.3.1
pyproj==1.9.6
python-dateutil==2.7.5
pytz==2018.9
requests==2.21.0
Shapely==1.6.4.post2
six==1.12.0
subprocess32==3.5.3
unicodecsv==0.14.1
Unidecode==1.0.23
urllib3==1.24.1
xlrd==1.2.0
Contexto
Se desea poder leer fácilmente CSVs que tienen columnas a partir de las cuales se podría construir una geometría, directamente en un objeto que permita funcionalidad geográfica.
Propuesta
Código neceesario para traducir un CSV con puntos en un GeoDataframe:
geometry = [Point(xy) for xy in zip(df.lon, df.lat)]
df = df.drop(['lon', 'lat'], axis=1)
crs = {'init': 'epsg:4326'}
gdf = GeoDataFrame(df, crs=crs, geometry=geometry)
Interfaz posible 1:
dc = DataCleaner("geo.csv", geom=["lat", "lon"])
dc = DataCleaner("geo.csv", geom="geojson")
Dependencias: este issue requiere la implementación previa de #45
Una vez resuelta la capacidad de DataCleaner
para leer y guardar archivos de distintos formatos, debería poder lidiar con leer/escribir archivos que vienen comprimidos en ZIP.
Esto es especialmente necesario para el caso de los SHP (que suelen venir en una carpeta ZIP) pero también es muy útil para hacer transparente la lectura de cualquier otro archivo CSV, etc que venga comprimido.
Al igual que en los issues #43, #44 y #45 se busca que la experiencia para el usuario sea transparente, pero esto es más difícil en el caso de escritura de ZIPs (¿Cómo debería indicarle el usuario que quiere guardar los datos en formato CSV, GEOJSON, KML o SHP pero también ZIPeados?).
Todas estas combinaciones de opciones deberían ser posibles para el usuario:
from data_cleaner import DataCleaner
input_path = "samples/provincias.zip"
input_path = "samples/provincias/provincias.shp"
input_path = "samples/provincias.geojson"
input_path = "samples/provincias.kml"
input_path = "samples/provincias.csv"
output_path = "samples/provincias.zip"
output_path = "samples/provincias/provincias.shp"
output_path = "samples/provincias.geojson"
output_path = "samples/provincias.kml"
output_path = "samples/provincias.csv"
dc = DataCleaner(input_path)
dc.save(output_path)
Hace falta pensar cuál es la mejor opción de interfaz para guardar el archivo en determinado formato, comprimido dentro de un ZIP:
dc.save("samples/provincias.zip", fmt="SHP")
dc.save("samples/provincias.zip", fmt="CSV")
dc.save("samples/provincias.zip", fmt="GEOJSON")
ó
dc.save("samples/provincias/provincias.shp", compress=True)
dc.save("samples/provincias.csv", compress=True)
dc.save("samples/provincias.geojson", compress=True)
para esto habría que hacer un poco de research de patrones ya usados en Pandas
, GeoPandas
y otras librerías de python + datos para ver cuál resulta mejor, o si hay un tercero mejor que estos dos.
El repo actualmente tiene 2 licencias diferentes.
Hay un archivo LICENSE con una licencia GNU GPL y un archivo LICENSE.md con licencia MIT
What are the key differences between the GNU General Public license and the MIT License?
Deberia haber una manera estandar para realizar esto de manera que resulte sencillo implementar reglas nuevas para casos de filtrados particulares a la vez que tambien sea facil incorporarlas a la clase original.
tests/integration
tests/unit
Dependencias: este issue requiere la implementación previa de #44
Una vez que DataCleaner es capaz de entender internamente un SHP y guardarlo en diversos formatos geográficos abiertos (CSV (geojson), GEOJSON y KML), se puede plantear fácilmente la capacidad de:
Al igual que en los issues #43 y #44 la experiencia buscada descansa en que para el usuario esta transformación sea transparente, manejada por DataCleaner sólo con la lectura de las extensiones de entrada y salida:
from data_cleaner import DataCleaner
input_path = "samples/provincias.geojson"
input_path = "samples/provincias.kml"
input_path = "samples/provincias.csv"
output_path = "samples/provincias/provincias.shp"
dc = DataCleaner(input_path)
dc.save(output_path)
Hay dos lugares donde creo que la mezcla de español e ingles resalta y puede llegar a confundir:
DataCleaner
están en español. Esto impacta en el siguiente punto:{"remover_columnas": [{"field": "col1"} ]}
Atacar el primer punto no deberia ser complejo, pero si queremos atacar ambos y tener tanto código como config de reglas todo en ingles entonces se puede meter algun helper para tener backwards compatibility
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.