Millón de Monos

Weblog de Manuel Aristarán

¿Quién fabrica lo que compramos?

Los códigos de barras de los productos que compramos contienen información sobre la empresa que los distribuye, empaca o fabrica. En este breve ejercicio, vamos a intentar combinar una lista de productos tomada del sitio público Precios Claros con una lista de compañías compilada por el proyecto Product Open Data.

Los identificadores de productos en preciosclaros.gob.ar son, en la mayoría de los casos, el código de barras del producto. Para extraer el campo GCP (Global Company Prefix) del identificador, vamos a usar la librería gtin.

import pandas as pd
from gtin import GTIN

Leemos la lista de productos, y agradecemos a nuestro anónimo amigo que se tomó el trabajo de scrapear Precios Claros.

df = pd.read_csv('productos_precios_claros.csv')
df.loc[:, 'uuid'] = df.uuid.apply(lambda s: s.split('producto-')[1])

Agregamos una columna que contendrá, si es posible, el campo GCP.

def gg(gtin):
    try:
        g = GTIN(gtin)
    except:
        return None

    try:
        return g.get_gcp()
    except:
        return None

df.loc[:, 'gcp'] = df.uuid.apply(lambda u: gg(u))

Calculamos una agregación de la tabla anterior, calculando dos variables adicionales:

  • marca: un conjunto de marcas asociadas a un GCP
  • product_count: cantidad de productos asociados a un GCP
products_by_gcp = df \
                   .groupby(['gcp'], as_index=False) \
                   .agg({
                       'marca': lambda m: set(m),
                       'uuid': 'count'
                    }) \
                   .rename(columns={'uuid': 'product_count'}) \
                   .sort_values(by='product_count', ascending=False)

Leemos la base de datos de GCPs obtenida de Product Open Data. Lamentablemente, la última versión es de 2013 y su cobertura para fabricantes argentinos es bastante mala.

gepir = pd.read_csv('/Users/manuel/Downloads/gs1_gcp.csv',
                    dtype={'GCP_CD': str, 'GLN_CD': str},
                    low_memory=False)

…y la combinamos con nuestra lista de GCPs (products_by_gcp).

merged = products_by_gcp.merge(gepir, how='inner', left_on='gcp', right_on='GCP_CD')

Verificamos qué porcentaje de GCPs pudimos encontrar en la base de datos:

n_matched_gcps = len(merged[~merged.GLN_NM.isnull()])
total_gcps = len(products_by_gcp)
print((n_matched_gcps / total_gcps) * 100)
66.34615384615384

Filtramos la lista para mostrar solamente las compañías que pudimos encontrar

out = merged[~merged.GLN_NM.isnull()][['gcp', 'product_count', 'marca', 'GLN_NM', 'GLN_COUNTRY_ISO_CD']]

Le agregamos la bandera del país para que quede más cheto.

OFFSET = ord('🇦') - ord('A')
def flag(code):
    return chr(ord(code[0]) + OFFSET) + chr(ord(code[1]) + OFFSET)

out.loc[:, 'country_flag'] = out['GLN_COUNTRY_ISO_CD'].apply(flag)
pd.set_option('display.max_rows', len(out))
out
gcp product_count marca GLN_NM GLN_COUNTRY_ISO_CD country_flag
0 8480017 1009 {GROLS, CUQUE, NAN, CARO AMICI, S.OND, CROCK, ... DIA ES 🇪🇸
2 4005808 29 {NIVEA} Beiersdorf AG DE 🇩🇪
3 0070330 18 {BIC , BIC} BIC USA Inc. US 🇺🇸
5 4052899 10 {OSRAM, DULUX} OSRAM GmbH CRM&S MDS P-W DE 🇩🇪
6 301426 10 {ZOOTH, GILLETTE, ORAL B} PROCTER ET GAMBLE FRANCE SAS FR 🇫🇷
9 75010074 7 {KOLESTON, ALWAYS, PANTENE} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
10 4008321 7 {OSRAM, DULUX} OSRAM GmbH DE 🇩🇪
12 75010067 6 {PAMPERS, PANTENE} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
13 7509546 6 {PROTEX, COLGATE, PALMOLIVE} COLGATE PALMOLIVE, S.A. DE C.V. COLGATE PALMOLIVE MX 🇲🇽
14 0038000 6 {PRINGLES} Kellogg Company US 🇺🇸
15 8413600 5 {VEET} RECKITT BENCKISER ES 🇪🇸
16 0047400 5 {GILLETTE} Procter & Gamble Company US 🇺🇸
17 75010092 5 {GILLETTE, PRESTOBARBA} NEWELL RUBBERMAID DE MEXICO, S. DE R.L. DE C.V... MX 🇲🇽
18 75010563 5 {PONDS, SEDAL} UNILEVER DE MEXICO, S. DE R.L. DE C.V. UNILEVER MX 🇲🇽
19 4005900 5 {NIVEA} Beiersdorf AG DE 🇩🇪
20 0041789 5 {MARUCHAN} Maruchan, Inc. US 🇺🇸
21 75010592 4 {COFFEE MATE, NESCAFE} NESTLE MEXICO, S.A. DE C.V. NESTLE MEXICO, S.A... MX 🇲🇽
22 0000075 4 {CORONA, DOVE, REXONA} ASOCIACION MEXICANA DE ESTANDARES PARA EL COME... MX 🇲🇽
23 0041333 4 {DURAC} Procter & Gamble Company US 🇺🇸
24 0084773 4 {ROBINSON CRUSOE} Trans Antartic Trading Co Ltda CL 🇨🇱
25 8718291 3 {PHILI} Philips Electronics Nederland B.V. NL 🇳🇱
26 75010011 3 {HEAD & SHOULDERS, PANTENE} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
27 0040000 3 {M & M, SKITTLES} Mars Chocolate North America LLC US 🇺🇸
29 8710163 3 {PHILI} Philips Electronics Nederland B.V. NL 🇳🇱
30 8710103 3 {PHILI} Philips Electronics Nederland B.V. NL 🇳🇱
31 0000080 3 {KINDER} Indicod-Ecr - GS1 Italy IT 🇮🇹
32 75010086 2 {BACARDI} BACARDI Y COMPAÑIA, S.A. DE C.V. BACARDI MX 🇲🇽
33 75010012 2 {HEAD & SHOULDERS} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
34 75010013 2 {OLD SPICE, PANTENE} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
35 9044400 2 {PEZ} PEZ International GmbH AT 🇦🇹
36 8712581 2 {PHILI} Philips Electronics Nederland B.V. NL 🇳🇱
37 75010864 2 {PRO} CORPORATIVO PROCTER & GAMBLE, S. DE R.L. DE C.... MX 🇲🇽
38 7591083 2 {COLGATE} COLGATE-PALMOLIVE C.A VE 🇻🇪
39 0021200 2 {SCOTCH BRITE} 3M Company US 🇺🇸
41 0071603 2 {TRIM} Pacific World Corporation US 🇺🇸
42 08452180 2 {VULCA, SIN MARCA} GS1 China CN 🇨🇳
43 0070501 2 {NEUTROGENA} Neutrogena Corporation US 🇺🇸
44 0022000 2 {WRIGLEYS} Wm. Wrigley Jr. Company US 🇺🇸
45 4015400 2 {PAMPERS} Procter & Gamble GmbH Wasch- u. Reinigungsmittel DE 🇩🇪
46 0079400 2 {REXONA} Unilever Home and Personal Care USA US 🇺🇸
47 5000329 2 {BEEFEATER} Chivas Brothers Limited GB 🇬🇧
48 0012800 1 {RAYOV} Spectrum Brands, Inc. US 🇺🇸
49 9002490 1 {RED BULL} Red Bull GmbH AT 🇦🇹
50 0079200 1 {NERDS} Sunmark US 🇺🇸
51 0000040 1 {KINDER} Nestlé Deutschland AG DE 🇩🇪
52 0070942 1 {GUM} Sunstar Americas, Inc. US 🇺🇸
53 8412300 1 {NIVEA} BEIERSDORF MANUFACTURING TRES CANTO ES 🇪🇸
54 8715200 1 {NIVEA} Beiersdorf N.V. NL 🇳🇱
55 0051000 1 {sin marca} Campbell Soup Company US 🇺🇸
57 0080432 1 {CHIVAS REGAL} Pernod Ricard USA LLC US 🇺🇸
58 0016304 1 {SIN MARCA} Unofficial Guides Ltd. US 🇺🇸
59 0013000 1 {HEINZ} Heinz USA US 🇺🇸
60 0099176 1 {COLGATE} Colgate Palmolive (Central America) S.A. GT 🇬🇹
61 0090159 1 {MAIST} May Cheong Toy Products Factory Limited HK 🇭🇰
62 75010587 1 {sin marca} RECKITT BENCKISER MEXICO, S.A. DE C.V. RECKITT... MX 🇲🇽
63 5900273 1 {COLGATE} Colgate-Palmolive (Poland) Sp. z o.o. PL 🇵🇱
64 5410316 1 {SMIRNOFF} DIAGEO SCOTLAND LTD GB 🇬🇧
65 5011013 1 {BAILEYS} GS1 Ireland IE 🇮🇪
66 5010103 1 {J&B} Diageo PLC GB 🇬🇧
67 75010080 1 {CHOCO KRISPIS } KELLOGG COMPANY MEXICO, S. DE R.L.DE C.V. KELLOGG MX 🇲🇽
68 75010152 1 {PELIK} PELIKAN MEXICO, S.A. DE C.V. PELIKAN MX 🇲🇽
69 75010329 1 {GLADE} S.C. JOHNSON AND SON S.A. DE C.V. S.C. JOHNSON... MX 🇲🇽
70 4006584 1 {L.B.L} OSRAM GmbH CRM&S MDS P-W DE 🇩🇪
71 75010641 1 {CORONA} CERVECERIA MODELO, S. DE R.L. DE C.V. CERVECER... MX 🇲🇽
72 4005800 1 {NIVEA} Beiersdorf AG DE 🇩🇪
73 303371 1 {NESCAFE} NESTLE FRANCE SAS FR 🇫🇷
74 0742832 1 {AOC} Tufftek US 🇺🇸
76 0681326 1 {SIN MARCA} Jazwares US 🇺🇸
77 7590002 1 {ALWAYS} PROCTER & GAMBLE DE VENEZUELA, S.C.A. VE 🇻🇪

El notebook completo está disponible acá: https://gist.github.com/jazzido/35990ebf8c236d3df953eb1f0af9e39c


Procesando los microdatos de la Encuesta Permanente de Hogares

Cada vez que el INDEC publica datos de la Encuesta Permanente de Hogares (EPH), hay repercusiones mediáticas de algunas de las variables que mide esa encuesta. El ingreso suele ser de particular interés para la cobertura periodística, por ejemplo en esta nota de La Nación.

Esos artículos, en general, usan como fuente a los “cuadros” que publica el INDEC. Esos cuadros contienen estadísticas calculadas a partir de los registros individuales de la EPH. Es decir, las repuestas de cada individuo que fue encuestado. A esas tablas, en la jerga, se las llama microdatos.

Inspirado por el post de Pablo Fernández en el que analizó los cuadros de población según escala de ingreso individual para el tercer trimestre de 2016, voy a intentar reproducirlos a partir de los microdatos de la EPH. Vamos a usar el lenguaje de programación Python, y la librería de análisis y manipulación de datos pandas.

import pandas as pd

A diferencia de otros organismos de estadística pública, como las APIs del Census Bureau de Estados Unidos, el INDEC no ofrece demasiada sistematización para obtener las bases de datos. El sistema de distribución de esa información es un rudimentario download de un archivo ZIP, que contiene las tablas en formato TXT o Excel. Vamos a trabajar con el último dataset disponible a la fecha, que corresponde al segundo trimestre de 2016.

Una vez abierto el archivo ZIP, leemos la tabla de respuestas de individuos.

eph = pd.read_excel('usu_individual_T216.xls')

Queremos reproducir un cuadro de población total según escala de ingreso individual. Consultamos el documento de Diseño de Registro y Estructura para las bases preliminares Hogar y Personas y vemos que la variable que contiene el monto de ingreso total individual se llama P47T. Filtramos la tabla y obtenemos los registros que declaran algún ingreso. Esto es, aquellos en los que P47T > 0.

with_income = eph[eph['P47T'] > 0]

Dado que las encuestas como la EPH contienen una muestra de la población, las variables suelen estar ponderadas (weighted) por un factor de ajuste que debemos usar para el cálculo de estadísticas. Para tratar el ingreso total individual, la EPH provee la variable PONDII, documentada en el Anexo I del documento de diseño.

El decil o grupo decílico para el ingreso total individual, ya está provisto por la EPH en la variable DECINDR, pero no contiene los límites de cada uno. Los calculamos con la ayuda de la librería weightedcalcs.

import weightedcalcs
upper = []
lower = []

wc = weightedcalcs.Calculator('PONDII')
lowerb = with_income['P47T'].min()
for q in range(1,11):
    upperb = wc.quantile(with_income, 'P47T', q/10.0)
    lower.append(lowerb)
    upper.append(upperb)
    lowerb = upperb
deciles = pd.DataFrame({'lbound': lower, 'ubound': upper}).reset_index().rename(columns={'index': 'decile'})
deciles['decile'] = deciles['decile'] + 1

Verificamos que las cotas de los deciles son iguales a los del cuadro que estamos replicando:

decile lbound ubound
0 1 22.0 2470.0
1 2 2470.0 4000.0
2 3 4000.0 4800.0
3 4 4800.0 6000.0
4 5 6000.0 7200.0
5 6 7200.0 9000.0
6 7 9000.0 10500.0
7 8 10500.0 14000.0
8 9 14000.0 20000.0
9 10 20000.0 715000.0

Usamos el grupo decílico provisto en la tabla para agregarle los intervalos de cada uno que acabamos de calcular.

with_income = with_income.merge(deciles, left_on='DECINDR', right_on='decile')

Sumando el ponderador (PONDII) para cada grupo decílico, obtenemos la población de cada uno. Creamos un nuevo DataFrame en el que almacenaremos las variables del cuadro que estamos replicando.

cuadro = pd.DataFrame(with_income.groupby('DECINDR')['PONDII'].sum()).rename(columns={'PONDII': 'decile_population'})

Agregamos una variable con el ingreso total de cada decil.

with_income.loc[:, 'weighted_income'] = with_income['P47T'] * with_income['PONDII']
cuadro['decile_total_income'] = pd.DataFrame(with_income.groupby('DECINDR')['weighted_income'].sum() / 1000)

Agregamos otra con el porcentaje del ingreso de cada decil sobre el ingreso total

cuadro['decile_percentage_income'] = (cuadro['decile_total_income'] / cuadro['decile_total_income'].sum()) * 100

Calculamos el ingreso medio por decil

cuadro['decile_mean_income'] = cuadro['decile_total_income'] / cuadro['decile_population']

Finalmente, agregamos las cotas de cada decil

cuadro = cuadro.reset_index().merge(deciles, left_on='DECINDR', right_on='decile').rename(columns={'lbound': 'decile_lower_bound', 'ubound': 'decile_upper_bound'})
del cuadro['decile']

Como decía la revista Anteojito, acá el modelo terminado:

DECINDR decile_population decile_total_income decile_percentage_income decile_mean_income decile_lower_bound decile_upper_bound
0 1 1618265 2.121558e+06 1.350246 1.311008 22.0 2470.0
1 2 1616771 5.337631e+06 3.397086 3.301414 2470.0 4000.0
2 3 1617945 7.243509e+06 4.610064 4.476981 4000.0 4800.0
3 4 1615707 8.257147e+06 5.255185 5.110547 4800.0 6000.0
4 5 1617192 1.046035e+07 6.657395 6.468220 6000.0 7200.0
5 6 1617391 1.318178e+07 8.389421 8.150026 7200.0 9000.0
6 7 1617847 1.577172e+07 10.037767 9.748588 9000.0 10500.0
7 8 1616376 1.957398e+07 12.457680 12.109795 10500.0 14000.0
8 9 1617180 2.605983e+07 16.585536 16.114364 14000.0 20000.0
9 10 1617037 4.911631e+07 31.259620 30.374264 20000.0 715000.0

Por supuesto, este ejercicio es apenas un ejemplo de lo que se puede hacer con los microdatos de la Encuesta Permanente de Hogares. El procesamiento de otras variables queda como actividad para el lector entusiasta.

El notebook completo está disponible acá: https://gist.github.com/jazzido/d067c834d0990fe3cf932cf6e7ec1881


Notplastic — intentando vender archivos

Hace unos años, cuando los músicos todavía tenían esperanzas de poder cobrar por su música grabada, mi amigo Milton Amadeo había grabado su disco Como dos Barcos y quería venderlo en los shows en vivo. Como no tenía sentido hacer una tirada de CDs (además de ser carísimos, ¿quién tiene una compactera a esta altura?), se nos ocurrió intentar vender códigos de descarga. Es decir, una tarjetita con un número que los compradores podían ingresar en un sitio web para bajar el disco en formato MP3. Programé una aplicación web muy simple para hacer eso, se vendieron varios códigos de descarga, todos contentos.

A otros amigos músicos les entusiasmó el sistema, pero querían que los downloads se puedan pagar con tarjeta, además de los códigos impresos. Pero el sistema que había hecho para el disco de Milton era una porquería hecha en una tarde, y no había manera de agregarle features.

En las semanas de desempleo que tuve justo antes de mudarme a EE.UU. para estudiar, para sacarme las ganas de programar, rehice la aplicación original y le agregué algunos features:

  • Integración con Mercado Pago, a través de su sistema de notificación (parecido al IPN de PayPal.
  • Precio mínimo y precio sugerido, y la posibilidad de pagar más, como donación a la causa.
  • Multi-tenancy: el sistema original funcionaba con un proyecto solo, ahora podía vender más de uno.

Luego de un par de años de estar en línea, y de haber publicado algunos proyectos de amigos y familia como El Abrigo del Viento —película documental producida por mi esposa Luisina Pozzo Ardizzi— o el disco LAS de Supernova Jazz Trío, aprendí algunas cosas de este experimento:

  • Argentina es un país muy poco bancarizado, en especial los miembros del target de proyectos artísticos independientes. La idea de usar Mercado Pago era aprovechar los medios de pago diferidos como Pago Fácil. La lección es que no funcionan para este tipo de sistemas. Salvo un único caso, las 22 transacciones iniciadas con un cupón nunca fueron completadas. Es decir, los usuarios comenzaban el proceso, y nunca iban al Farmacity a pagar el cupón. El medio de pago más usado fue, por lejos, las tarjetas de crédito.
  • La enorme mayoría de los que compraron algo, pagaron el mínimo, pese a ser precios irrisorios (más barato que un kilo de milanesas, o una coca grande).
  • A pesar de la complejidad de su API, y lo deficiente de su documentación, Mercado Pago funciona bastante bien.

Este proyecto nunca tuvo desarrollo comercial, ni de producto (así lo demuestra su horrible diseño gráfico). Pero esta breve y superficial experiencia me sugiere que este modelo no funciona. Ya no bajamos música, ni ningún tipo de bien digital: los consumimos online, en plataformas como YouTube.

De todas maneras, fue un experimento interesante, y me mantuvo entretenido los días previos a mudarnos a EE.UU.

El código fuente de notplastic está disponible bajo licencia AGPL3 en http://github.com/jazzido/notplastic


Presentación en CONDATOS (Bogotá, Noviembre 2016)

A continuación, algunas notas que usé para la presentación que di en la Conferencia CONDATOS 2016, realizada en Bogotá el 3 de Noviembre de 2016

Presentación

Espero que los 20 minutos me alcancen para lo que quiero hablar:

  • Una breve historia de mi primer proyecto de Open Data: Gasto Público Bahiense
  • Luego de ¿8? años el movimiento de Datos y Gobierno Abierto aprendió muchas cosas, pero seguimos manteniendo algunos mitos. Me gustaría hacer un ejercicio de refutación.
  • ¿Cómo podemos construir recursos de información pública más eficientes?

Historia de Gasto Público Bahiense

Inicios

Bahía Blanca, la ciudad del sur de Argentina donde nací, publica información detallada acerca de sus compras desde el año 2001, mucho antes de que se inventara el concepto de datos abiertos. El sistema donde se publica esa información, con los sucesivos alcaldes, se achicó progresivamente; cada vez publicaba menos información.

Así publicaba las compras el Municipio de Bahía Blanca en 2010

En Julio de 2010, gracias a la procrastinación (estaba trabajando en un proyecto un poco aburrido). Noté que esos datos (un listado de órdenes de compra) estaban disponibles en el sitio del Municipio y construí un sitio web muy simple que facilitaba su consulta y navegación.

Así era gastopublicobahiense.org en 2010

Lo puse online, y me explotó en la cara. De repente, empecé a atender llamados de periodistas que me preguntaban “quién financiaba el proyecto”, y cuáles eran mis intenciones.

Ignorado por las autoridades

Pese a que el proyecto fue declarado de interés municipal por un legislador local, y tuvo un gran repercusión mediática y política, el gobierno municipal de entonces no acusó recibo de la herramienta durante un año.

CAPTCHA y repercusión

Hasta que justo 1 año después de haber lanzado GPB, el gobierno rediseño su sitio web y puso un CAPTCHA para evitar que “los robots se roben la información y hagan más lento al sistema”.

Al contrario de lo que el Secretario de Haciendo de entonces —supongo— esperaba lograr con la introducción de un CAPTCHA en el sitio web donde publicaban las órdenes de compra, su decisión ayudó a la difusión del proyecto. Por primera vez, Gasto Público Bahiense alcanzó la prensa nacional:

Modificamos el sistema de extracción de datos (saltar ese CAPTCHA era muy fácil) e hicimos que GPB siguiera funcionando. Y ya que estábamos, con la colaboración de amigos y colegas, rediseñamos el sitio.

Así quedó GPB después del rediseño (mucho más lindo, ¿no?)

Creación de la Secretaría de Innovación y Gobierno Abierto

En 2012 —GPB ya llevaba 2 años funcionando— cambió el alcalde de la ciudad. El nuevo gobierno creó una Secretaría de Innovación y Gobierno Abierto, inspirado por la experiencia de nuestro proyecto. Fue la primera vez que tuve contacto con un representante del Municipio. La nueva Secretaría, en lugar de entorpecer el acceso a la información, nos lo facilitó dándonos acceso a un web service.

Demanda de los datos por parte del sector público

La visibilidad de estos recursos de información producida y publicada por el estado rindió sus frutos. Hoy, en Bahía Blanca, los políticos, periodistas y los ciudadanos interesados, consideran el acceso a los datos como un derecho adquirido. En los últimos días, esta problemática apareció en la agenda política y mediática.

Cierre y apoyo de la Municipalidad

Hace alrededor de 1 mes, decidí dar de baja al proyecto Gasto Público Bahiense. Fueron más de 6 años de mantener el sistema funcionando, y de pagar el servidor donde estaba alojado. Apenas anuncié el cierre, me contactaron políticos de la oposición y del gobierno actual para proponerme soluciones. Finalmente, la Secretaría de Modernización y Gobierno Abierto de la Municipalidad de Bahía Blanca ofreció proporcionarnos un servidor en el que alojar la aplicación, para que pueda seguir funcionando. En los próximos días, GPB volverá a estar en línea.

Mitos de Open Data

“Publicaremos datos y los desarrolladores construirán aplicaciones, y florecerán cientos de nuevas compañías”

La idea de “Open Data para el desarrollo de emprendimientos privados”, frecuentemente se ilustra con proyectos de gran escala como el Sistema de Posicionamiento Global (GPS) o el servicio meteorológico de EE.UU. Semejantes obras de infraestructura digital no pueden ser comparadas con liberar un pocos datasets y pretender que los emprendedores privados los tomen para producir valor.

Además, en mi opinión, en este mito subyace una cierta idea de voluntarismo: ciudadanos cumpliendo el rol del estado. No hay nada de mal en eso, pero —tomando prestado un concepto de ingeniería— no escala. Los proyectos sustentables necesitan de la infraestructura y profesionalismo que sólo puede aportar una organización.

Los “ciudadanos comunes” se involucrarán en el control y auditoría de la gestión pública

La idea de ciudadano común no está clara, la experiencia muestra que no existen —en el contexto de usuarios ideales de un recurso de información pública. Todo aquel que se embarca en la tarea de interpretar cuentas públicas, o procesar indicadores socio-económicos, tiene un propósito. Si no, ¿por qué va a ponerse a trabajar en eso, que es tan complicado?

Cualquier “data” es buena: PDFs, planillas Excel mal hechas, APIs.

Señal/ruido: Lo que es señal para usted, puede ser ruido para mí, o viceversa. El diseño de un dataset (medidas de un fenómeno), implica necesariamente una perspectiva u opinión sobre este.

¿Cómo mejorar los portales de datos abiertos?

La presencia en la web de las iniciativas gubernamentales de Open Data, en general, se materializa en la forma de un portal de Datos Abiertos. Hay productos, abiertos y comerciales, que permiten a una administración pública implementar este tipo de sitios con relativo poco esfuerzo. Algunos de ellos:

Por supuesto, un repositorio de información es un requisito necesario para una iniciativa seria de publicación de datos generados por el estado, pero no es suficiente.

Tomo prestada una metáfora de mi supervisor de tesis en MIT César Hidalgo:

Imagínense ir a hacer las compras a un supermercado donde todos los productos están en cajas idénticas. Pasta, shampoo, aceite: todos en la misma caja. La experiencia de comprar en ese supermercado es parecida a la de buscar una base de datos en casi cualquier portal de datos públicos. Para poder saber qué contienen, tengo que descargarlo y abrirlo con una aplicación, o interactuar con una API; un procedimiento que requiere de conocimientos relativamente avanzados de programación (What’s Wrong with Open-Data Sites–and How We Can Fix Them, Scientific American)

El paradigma de diseño de los portales de datos, por otro lado, establece un sesgo hacia abrir más datasets y no hacia abrirlos mejor. Notemos, por ejemplo, que el número de datasets disponibles se enfatiza en todos los portales de datos. No obstante, es fácil fabricar datasets. Por ejemplo, es frecuente ver en muchos portales de datos, una tabla de presupuesto “partida” en varios archivos. Con ese viejo truco, podemos fabricar varios datasets a partir de uno.

La descubribilidad de la información publicada en los portales de datos también es un problema. Google —todavía— no sabe indexar tablas llenas de números. Entonces, así como generamos visualizaciones a partir de los datos, también podemos generar texto (lo único que Google sabe indexar).

También es crucial facilitar el sharing en redes sociales, que es el origen de una porción sustancial del tráfico de cualquier sitio web.

El paradigma clásico del dashboard (tablero de control), no es suficiente para satisfacer el objetivo de hacer nuestra información más accesible, transparente y compensible. Los dashboards, tales como el panel de control de un vehículo, están diseñados para obtener —en un golpe de vista– la información necesaria para tener control de un sistema o proceso. Este diseño no se traslada muy bien a la web. En principio, un dashboard no aprovecha la interacción más natural que tenemos en cualquier dispositivo: el scroll. De hecho, mucha gente está “descubriendo” que a los usuarios nos gusta scrollear, más que apretar botones [1].

Algunos ejemplos salidos de Datawheel y Macroconnections:

SpendView

Mi proyecto de tesis de Maestría en el grupo Macroconnections de MIT Media Lab: https://spendview.media.mit.edu

DataUSA

Plataforma de visualización de datos públicos publicados por el gobierno de EE.UU: http://datausa.io

(¡Ayer ganó medalla de oro en los premios Kantar Information is Beautiful!)

DataChile

Actualmente estamos desarrollando un proyecto similar a DataUSA para información generada por el Gobierno de Chile.

Observatory of Economic Complexity

Visualización de 60 años de datos de comercio internacional: http://atlas.media.mit.edu

Data Viva

Plataforma de visualización de indicadores socioeconómicos de Brasil: http://legacy.dataviva.info

Conclusión

Es posible construir recursos de información útiles. Pero, en mi opinión, es crucial trabajar en la experiencia de uso de esos sistemas. Los servicios masivos de Internet nos acostumbraron a interactuar con sistemas fluídos, y siempre disponibles. Esto aumenta las expectativas de los usuarios que usarán nuestros sistemas que —por supuesto— no cuentan con los recursos de los que disponen las grandes empresas de tecnología . Si implementamos una función de búsqueda en nuestro sitio, debe funcionar bien. Nuestro sistema debe poder accederse más o menos bien desde un dispositivo móvil [2].

Entonces, ¿cómo hace un departamento de tecnología de un gobierno para encarar esos desafíos técnicos y políticos?

Podemos encontrar una posible respuesta a esta pregunta en uno de los principios del así llamado “Gobierno Abierto”: colaboración. Sin diálogo y participación de las comunidades de desarrolladores de software, periodistas, académicos, miembros de la sociedad civil y de sector privado, es poco probable que una oficina pública pueda llevar a cabo una iniciativa exitosa de datos abiertos.

En Argentina, gracias al empuje de organizaciones como Hacks-Hackers, La Nación Data y muchos otros, se establecieron puentes de diálogo y colaboración con el sector público. Por supuesto, habrá tensión: en última instancia estamos haciendo política y es natural que no estemos todos tomados de la mano cantando canciones de amor. Los periodistas, por ejemplo, están naturalmente en oposición a los gobiernos (de otra manera estarían haciendo propaganda). Las organizaciones de la sociedad civil también: existen para llenar un espacio que no ocupa el estado. Aún así, es posible establecer diálogos productivos y maduros.

Desarrollar un software, un portal de datos o una visualización es relativamente fácil. Al fin de cuentas, no estamos resolviendo problemas técnicos demasiado desafiantes: las bases de datos tal como las conocemos hoy existen hace más de 40 años. La comunidad de software libre produce una cantidad ENORME de herramientas que podemos usar gratuitamente [3]. Armar software no es el problema principal. Pero cambiar una cultura de gobierno es un proceso largo y tortuoso: en Argentina costó muchos años lograr una Ley Nacional de Acceso a la Información Pública.

Entonces, los desafíos a los que nos enfrentamos son mucho más viejos que las computadoras, los gráficos estadísticos y los datos abiertos. Son retos políticos, de planeamiento y de gestión. Los hackatones y las conferencias son una gran oportunidad que tenemos las comunidades que estamos interesadas en estos temas para conocernos y para compartir conocimiento y experiencias. Pero las iniciativas de datos abiertos e información pública no deben terminar ahí. Para lograr impacto real e “innovación” (ese concepto tan de moda pero que no sabemos realmente qué significa), necesitamos pensar en el largo plazo y atraer a profesionales competentes [4].

Honestamente, no se cómo se construye un ecosistema de innovación vibrante y productivo [5]. Pero he sido partícipe de algunos. En todos, he visto apertura, diálogo, financiamiento, incentivos para todos sus participantes e impacto en el mundo real. Creo que el Estado, como garante de derechos y oportunidades para todos, está en una posición inmejorable para generar esos espacios.

¡Muchas gracias!

Footnotes

[1] “Why are we doing fewer interactives”, presentación en Malofiej 2016 de Archie Tse, Deputy Graphics Director en el New York Times

[2] El tráfico desde móviles sigue aumentando y es una tendencia que no va a cambiar.

[3] De hecho, hay tantas opciones que es difícil elegir bien. Mi consejo: no elijas lo que está de moda, sino lo que tus programadores sepan usar.

[4] La demanda de profesionales TIC capacitados presenta un problema interesante para el sector público. “It’s a seller’s market” dicen los gringos 😀

[5] Hay mucha gente que dice que sabe cómo.


Extrayendo datos reutilizables del Sitio del Ciudadano

Los presupuestos públicos son conjuntos de datos multidimensionlales, que reflejan las estructuras burocráticas, contables y económicas de los gastos e ingresos de la administración del estado. Su complejidad presenta desafíos interesantes a la hora de construir herramientas que permitan explorarlos.

Aunque los presupuestos de cualquier nivel del estado son muy similares en estructura, casi todos los países publican en la web sus presupuestos a través de herramientas ad-hoc. Este panorama fue relevado en detalle por Jonathan Stray en el reporte Open Budget Data: Mapping the Landscape. La fundación Open Knowledge International, por su parte, impulsa el proyecto Fiscal Data Package, que aprovecha esa similaridad estructural para estandarizar el formato en que se publican los datos públicos fiscales.

Argentina hace su parte desde hace algunos años con el Sitio del Ciudadano, dependiente del Ministerio de Hacienda y Finanzas Públicas. Pese a su título, nos cuesta imaginar a un ciudadano común navegando con éxito esta herramienta, debido a su mal diseño, complejidad y bajísima performance.

Durante mi tesis de maestría trabajé en SpendView, un prototipo de herrramienta para visualizar información presupuestaria. Uno de las premisas de su diseño es ser lo suficientemente flexible para almacenar y permitir explorar cualquier presupuesto público. Naturalmente, me interesaba mostrar el presupuesto argentino. SpendView requiere que los datos estén representados de manera desagregada. Es decir, cada línea del presupuesto debe contener información sobre todas las dimensiones en que se clasifica. En el caso del presupuesto asignado al presupuesto del CONICET, tomamos una línea del presupuesto bastante desagregada (actualizada a marzo de 2016):

  • Clasificación Administrativa (¿quién gasta?)
  • Clasificación según Objeto del Gasto (¿en qué se gasta?)
    • Gastos en Personal (nivel Inciso)
    • Personal Permanente (nivel Partida Principal)
  • Clasificación Funcional (¿para qué se gasta?)
    • Servicios Sociales (nivel Finalidad)
    • Ciencia y Técnica (nivel Función)
  • Clasificación según Fuente de financiamiento
    • Tesoro Nacional
  • Medidas
    • Crédito Vigente: 4.65 miles de millones de pesos argentinos
    • Devengado: 1.21 miles de millones de pesos argentinos.

Desafortunadamente, el Sitio del Ciudadano sólo ofrece cuadros pre-agregados. Es decir, no es posible obtener líneas completas del presupuesto, que refieran a todos los criterios de clasificación. Pero la data está, y se puede extraer.

Hurgando en el Sitio del Ciudadano (SiCi)

El SiCi está implementado en una versión antigua de Oracle Business Intelligence, un sistema muy poco apto para construir sitios web públicos. Por ejemplo, para obtener una tabla de gastos por jurisdicción, el browser hace 388 pedidos al servidor (!), transfiere 4.2 MB y (en mi computadora y con mi conexión a internet) tarda 27 segundos en mostrar el contenido.

Lentísimo

Pero entremezclada en su verborragia, el SiCi emite información que nos permitirá obtener los datos que necesitamos. El primer indicio es un request que ocurre cuando se interactúa (hover, click, etc) sobre una tabla. El browser pide un recurso llamado /saw.dll?getReportXmlFromSearchID, que contiene la definición del reporte solicitado:

Confundidos dentro de semejante aberración, hay elementos interesantes que contienen las fórmulas para cada columna del reporte:

<saw:columnFormula>
  <sawx:expr xsi:type="sawx:sqlExpression">Institucion."Cod. y Desc. Jurisdiccion"</sawx:expr>
</saw:columnFormula>
<!-- ... -->
<saw:columnFormula>
  <sawx:expr xsi:type="sawx:sqlExpression">CAST(Tiempo.Mes as VARCHAR(2))</sawx:expr>
</saw:columnFormula>
<!-- ... -->
<saw:columnFormula>
  <sawx:expr xsi:type="sawx:sqlExpression">"Indicadores Credito"."$ Cred. Vigente"</sawx:expr>
</saw:columnFormula>

Como uno de los elementos principales, al principio del archivo, aparece <saw:criteria subjectArea="&quot;SITIO DEL CIUDADANO&quot;" >, que no es otra cosa que la tabla/cubo sobre la que opera el reporte.

Luego de un exhaustivo proceso de investigación (busqué en Google), vi que el recurso HTTP /saw.dll es el punto de entrada a casi todas las operaciones que ofrece este sistema de business intelligence. Grande fue mi felicidad cuando vi en la documentación que saw.dll acepta un parámetro llamado SQL. Resulta que ese supuesto SQL es una extensión de Oracle, diseñada para hacer consultas OLAP (analíticas). En pocas palabras, es un SQL donde el SUM() sobre las medidas y el GROUP BY sobre las dimensiones están implícitos (me gustó la idea, ojalá hubiera una implementación open source).

Probamos el endpoint saw.dll con una consulta simple (averigüé los nombres de las columnas mirando la definición del reporte):

SELECT "Ejercicio Presupuestario"."Cod. Ejercicio Presupuestario",
       "Sector Institucional"."Desc. Caracter",
       "Indicadores Credito"."$ Comprometido",
       "Indicadores Credito"."$ Devengado",
       "Indicadores Credito"."$ Pagado",
       "Indicadores Credito"."$ Cred. Vigente"
FROM "SITIO DEL CIUDADANO"

El URL completo es el siguiente (el nombre de usuario y el password están visibles en el HTML del SiCi)

http://sitiodelciudadano.mecon.gov.ar/analytics/saw.dll?Go
  &NQUser=usrsici_c
  &NQPassword=usrsici_c
  &SQL=SELECT%20%22Ejercicio%20Presupuestario%22.%22Cod.%20Ejercicio%20Presupuestario%22,%20%22Sector%20Institucional%22.%22Desc.%20Caracter%22,%20%22Indicadores%20Credito%22.%22$%20Comprometido%22,%20%22Indicadores%20Credito%22.%22$%20Devengado%22,%20%22Indicadores%20Credito%22.%22$%20Pagado%22,%20%22Indicadores%20Credito%22.%22$%20Cred.%20Vigente%22%20FROM%20%22SITIO%20DEL%20CIUDADANO%22

Boom. La historia de la ejecución presupuestaria desde 1998, desagregada por “Sector Institucional”. Para mi sorpresa, funcionó a la perfección, dibujando una tabla que parece diseñada en 1999, y cuyo HTML parece escrito con Microsoft FrontPage ‘98:

Consulta simple

Pero queremos CSV, no una tabla horrible. Volvemos a la documentación, y vemos un parámetro Format. Agregamos Format=CSV al URL, que nos devuelve un hermoso archivo separado por comas.

Vamos a omitir muchos pasos intermedios, para pasar directamente al modelo terminado. La siguiente consulta obtiene la ejecución presupuestaria a la fecha en 2016, a una resolución más alta de la que podíamos esperar:

SELECT "Ejercicio Presupuestario"."Cod. Ejercicio Presupuestario",
       "Sector Institucional"."Desc. Caracter",
       "Institucion"."Cod. Jurisdiccion",
       "Institucion"."Desc. Jurisdiccion",
       "Institucion"."Cod. Subjurisdiccion",
       "Institucion"."Desc. Subjurisdiccion",
       "Institucion"."Cod. Entidad",
       "Institucion"."Desc. Entidad",
       "Servicio"."Cod. Servicio",
       "Servicio"."Desc. Larga Servicio",
       "Apertura Programatica"."Cod. Programa",
       "Apertura Programatica"."Desc. Programa",
       "Finalidad Funcion"."Cod. Finalidad",
       "Finalidad Funcion"."Desc. Finalidad",
       "Finalidad Funcion"."Cod. Funcion",
       "Finalidad Funcion"."Desc. Funcion",
       "Objeto Gasto"."Cod. Inciso",
       "Objeto Gasto"."Desc. Inciso",
       "Objeto Gasto"."Cod. Principal",
       "Objeto Gasto"."Desc. Principal",
       "Objeto Gasto"."Cod. Parcial",
       "Objeto Gasto"."Desc. Parcial",
       "Objeto Gasto"."Cod. Subparcial",
       "Objeto Gasto"."Desc. Subparcial",
       "Clasificador Economico"."Cod. 2 Digitos",
       "Clasificador Economico"."Desc. 2 Digitos",
       "Clasificador Economico"."Cod. 3 Digitos",
       "Clasificador Economico"."Desc. 3 Digitos",
       "Fuente Financiamiento"."Cod Codigos",
       "Fuente Financiamiento"."Cod y Desc Codigos",
       "Indicadores Credito"."$ Comprometido",
       "Indicadores Credito"."$ Devengado",
       "Indicadores Credito"."$ Pagado",
       "Indicadores Credito"."$ Cred. Vigente"
FROM "SITIO DEL CIUDADANO"
WHERE "Ejercicio Presupuestario"."Cod. Ejercicio Presupuestario"=2016
  AND ("Objeto Gasto"."Cod. Inciso" BETWEEN 1 AND 8)
  AND "Clasificador Economico"."Cod. 2 Digitos" IN (21, 22)

(Los filtros que aparecen en la cláusula WHERE fueron tomados de los reportes)

La tabla/cubo SITIO DEL CIUDADANO contiene más dimensiones, que nos permitirían obtener la ejecución presupuestaria a una resolución de días, para cualquier fecha desde 1998.

Obtener esa información, y construir reportes o herramientas interesantes, queda como ejercicio para el lector entusiasta.