Introducción

En este laboratorio veremos un ejemplo de como podemos trabajar con dos de los formatos de codificación de datos más extendidos. Por un lado, el formato CSV/TSV ofrece un soporte casi universal para trasvase rápido de datos entre diferentes sistemas (incluso desde o hacia programas de hoja de cálculo). Por otro lado, JSON es un formato muy popular en aplicaciones y APIs web.

En ambos casos, se encuentran soportados en Python mediante los módulos de la biblioteca standard csv y json. En el caso de JSON, veremos también bibliotecas alternativas para mejorar el rendimiento de carga y escritura.

El formato CSV

El formato CSV es uno de los más sencillos para trasvasar datos entre diferentes programas, repositorios o bases de datos. Algunas de sus ventajas son:

  • Es un formato relativamente sencillo de utilizar, aunque no existe un estándar bien definido. Por ello pueden existir pequeñas diferencias entre formatos de distintos programas (dialectos).
  • No contiene metadatos, por lo que ocupa menos espacio.
  • Si no surgen problemas de formato, suele ser muy rápido para carga o volcado de datos.

Los problemas derivados de la ausencia de un estándar común bien definido pueden entorpecer la recuperación de datos. Por ejemplo, no existe un consenso sobre la representación de números con cifras decimales.

Sin embargo, el principal inconveniente del formato CSV es que necesitamos conocer de antemano el número de campos que contiene cada fila, así como el tipo de datos de cada campo para su correcta recuperación. Por ello, es muy importante contar con una descripción por separado del contenido de los archivos, o al menos con algunas líneas de ejemplo.

Trabajando con CSV

La biblioteca estándar de Python proporciona el módulo csv para facilitar la lectura y escritura de datos en este formato. Uno de los mejores tutoriales para aprender más sobre el tratamiento de datos CSV es csv – Comma-separated value files, que forma parte de la serie PyMOTW.

Dicho tutorial incluye:

  • Una introducción a la lectura y escritura de archivos en formato CSV.
  • Una descripción de los detalles de formato, incluyendo los diferentes dialectos soportados y la definición de nuevos dialectos.
  • Ejemplos de uso de la clase Sniffer() (incluida en el módulo csv) para detección automática del formato.

Para continuar nuestra práctica, vamos a utilizar el archivo sample_new_users.csv (disponible en el repositorio del curso). Este archivo contiene una muestra del log de creación de nuevos usuarios de la Wikipedia en Escocés (lengua germánica).

Los campos son:

  • Identificador unívoco (entero).
  • Identificador de usuario (entero).
  • Nombre de usuario (cadena de caracteres Unicode, UTF-8, libre).
  • Fecha de registro (formato ISO 8601).
  • Herramienta de registro (cadena de caracteres Unicode, UTF-8, variable cateǵorica).

Ejercicio 1

Escriba un programa en Python que cargue en memoria todos los datos del fichero sample_new_users.csv y muestre el primer y el último registro contenido en el fichero, según su orden cronológico.


In [1]:
## ESPACIO PARA LA RESOLUCION DEL EJERCICIO

Ejercicio 2

Cree un programa en Python que escriba en un fichero de texto plano (TSV) los datos leídos del fichero sample_new_users.csv pero solo incluya, por este orden, el identificador de usuario, la herramienta de registro y la fecha de registro.


In [2]:
## ESPACIO PARA LA RESOLUCION DEL EJERCICIO

El formato JSON

Como ya hemos comentado, JSON se creo como un estándar para intercambio de datos que fuese ligero, fácil de implementar y flexible. Su sintaxis es muy parecida a la de los diccionarios en Python, pues está basado en objetos, representados como parejas clave: valor que van encerradas entre llaves. El documento JSON puede tener una estructura jerárquica, con objetos anidados en varios niveles.

Las claves son siempre de tipo string. Los valores puede ser individuales (strings, números, objetos serializados, etc.) o bien arrays de valores. Estos últimos son similares a las listas de Python, bues se encierran entre corchetes y permiten crear listas ordenadas de valores. Normalmente, los datos de tipo string se interpretan como cadenas de caracteres Unicode.

Trabajando con JSON

El formato JSON se ha convertido en un estándar muy frecuente para el trasvase de datos, especialmente en entornos web. Inicialmente asociado al lenguaje JavaScript, su relativa simplicidad y su capacidad para integrar fácilmente estructuras de datos anidadas con distintos formatos le convierten en un formato muy versátil en todo tipo de situaciones.

Consultar datos de APIs

Un caso típico en el que se suele trabajar con datos en formato JSON es cuando consumimos información de una API, por ejemplo a través de una interfaz REST. El siguiente ejemplo muestra una consulta para recuperar datos de la API de Wikipedia en español. La consulta obtiene la revisión más reciente del artículo enciclopédico Tetramorium caespitum, incluyendo su contenido y metadatos adicionales como el autor del último cambio, la fecha en la que se editó esa revisión, y el comentario con el resumen de cambios que incluyó el autor:

http://es.wikipedia.org/w/api.php?action=query&prop=revisions&titles=Tetramorium_caespitum&rvprop=timestamp|user|comment|content&continue=&format=json

A continuación, presentamos un programa muy simple que ilustra cómo podemos consumir dicha información en formato JSON en Python, utilizando para ello del módulo ujson. También hacemos uso del módulo json, incluido en la biblioteca estándar de Python, para imprimir los datos de forma más legible para un humano (pretty print):


In [7]:
import ujson
import json
import urllib

query = "".join(["http://es.wikipedia.org/w/api.php?action=query&prop=revisions",
                 "&titles=Tetramorium_caespitum&rvprop=timestamp|user|comment|content",
                 "&continue=&format=json"])
socket = urllib.urlopen(query)
data = socket.read()
# print data
json_data = ujson.loads(data)
print json.dumps(json_data, sort_keys=True, indent=4,
                 separators=(',', ': '))


{
    "batchcomplete": "",
    "query": {
        "normalized": [
            {
                "from": "Tetramorium_caespitum",
                "to": "Tetramorium caespitum"
            }
        ],
        "pages": {
            "4802751": {
                "ns": 0,
                "pageid": 4802751,
                "revisions": [
                    {
                        "*": "{{Ficha de tax\u00f3n\n| name = Hormiga de pavimento\n| image = Tetramorium caespitum casent0005827 profile 1.jpg\n| image_width = 200px\n| image_caption = \n| regnum = [[Animalia]]\n| phylum = [[Arthropoda]]\n| classis = [[Insecta]]\n| ordo = [[Hymenoptera]]\n| familia = [[Ant|Formicidae]]\n| subfamilia = [[Myrmicinae]]\n| genus = ''[[Tetramorium]]''\n| species = '''''T. caespitum'''''\n| binomial = ''Tetramorium caespitum''\n| binomial_authority = ([[Carolus Linnaeus|Linnaeus]], [[Systema Naturae|1758]])\n}}\n\nLa '''hormiga de pavimento''', '''''Tetramorium caespitum''''', es una plaga casera com\u00fan, cuyos individuos suelen construir sus colonias bajo el pavimento. Se distingue por una espina en la espalda, dos n\u00f3dulos en su pec\u00edolo y granuras en su cabeza y t\u00f3rax.<ref>{{cita web|url=http://www.ipm.ucdavis.edu/TOOLS/ANTKEY/pavement.html |t\u00edtulo=Pavement ant \u2014 ''Tetramorium caespitum'' |editorial=[[University of California, Davis]] |fecha=26-01-2004}}</ref> La especie es nativa de Europa, pero fue introducida en Am\u00e9rica del Norte en el [[siglo XVIII]].\n\n== Fuentes ==\n{{listaref}}\n\n[[Categor\u00eda:Myrmicinae]]",
                        "comment": "",
                        "contentformat": "text/x-wiki",
                        "contentmodel": "wikitext",
                        "timestamp": "2013-10-29T22:56:50Z",
                        "user": "Canyq"
                    }
                ],
                "title": "Tetramorium caespitum"
            }
        }
    }
}

Ejercicio 3

¿Podríamos obtener la misma información en un formato de representación distinto, como XML? ¿De qué forma?

Obtener datos de archivos

Para practicar la obtención y escritura de datos en archivos JSON, vamos a utilizar como ejemplo los datos del juego de cartas Magic the Gathering. En particular, emplearemos los ficheros AllCards.json y AllSets.json.

La documentación sobre el contenido de los ficheros se pueden encontrar en los siguientes enlaces:

A continuación presentamos un ejemplo de código en Python para leer el fichero con todas las cartas de los diferentes juegos, imprimir el número total de cartas y los datos de una carta elegida de forma aleatoria:


In [13]:
import ujson
import json
import random


with open('AllCards.json') as f:
    cards_dict = ujson.load(f)
    cards_ids = cards_dict.keys()
print "Número total de cartas leidas: %d" % len(cards_ids)
print "Sacamos una carta aleatoria del mazo:"
single_card = cards_dict[random.choice(cards_ids)]
print json.dumps(single_card, sort_keys=True, indent=4,
                 separators=(',', ': '))
# Imprimir la carta de ejemplo en http://mtgjson.com/#cards
print json.dumps(cards_dict["Sen Triplets"], sort_keys=True, indent=4,
                 separators=(',', ': '))


Número total de cartas leidas: 14998
Sacamos una carta aleatoria del mazo:
{
    "cmc": 2,
    "colors": [
        "Red"
    ],
    "imageName": "fiery conclusion",
    "layout": "normal",
    "manaCost": "{1}{R}",
    "name": "Fiery Conclusion",
    "text": "As an additional cost to cast Fiery Conclusion, sacrifice a creature.\nFiery Conclusion deals 5 damage to target creature.",
    "type": "Instant",
    "types": [
        "Instant"
    ]
}
{
    "cmc": 5,
    "colors": [
        "White",
        "Blue",
        "Black"
    ],
    "imageName": "sen triplets",
    "layout": "normal",
    "manaCost": "{2}{W}{U}{B}",
    "name": "Sen Triplets",
    "power": "3",
    "subtypes": [
        "Human",
        "Wizard"
    ],
    "supertypes": [
        "Legendary"
    ],
    "text": "At the beginning of your upkeep, choose target opponent. This turn, that player can't cast spells or activate abilities and plays with his or her hand revealed. You may play cards from that player's hand this turn.",
    "toughness": "3",
    "type": "Legendary Artifact Creature \u2014 Human Wizard",
    "types": [
        "Artifact",
        "Creature"
    ]
}

Ejercicio 4

Genere un programa en Python que lea el archivo AllSets.json y calcule e imprima por pantalla los siguientes datos:

  • Número de cartas de cada set.
  • Listado de nombres de todos los sets con el borde blanco.
  • Listado de nombres de todos los sets que contengan cartas creadas por el artista 'Pete Venters'.

In [3]:
## ESPACIO PARA LA RESOLUCION DEL EJERCICIO

Conclusiones

En este laboratorio hemos prácticado como podemos leer y escribir datos en dos de los formatos de representación más utilizados, CSV y JSON. Los aspectos tratados han sido:

  • Introducir los formatos de representación CSV y JSON.
  • Utilizar el módulo csv de la biblioteca estándar de Python para leer y escribir datos con ficheros CSV, considerando además las posibles peculiaridades de sintáxis (dialectos).
  • Utilizar la biblioteca estándar de Python, así como el módulo ujson para leer y escribir información en JSON de forma eficiente.

Referencias

  1. CSV. Wikipedia (inglés). (Último acceso marzo 2015).
  2. CSV, Comma Separated Values (RFC 4180). (Último acceso marzo 2015).
  3. Doug Hellmann. csv – Comma-separated value files. PyMOTW. (Último acceso marzo 2015).
  4. Introducing JSON (Último acceso marzo 2015).
  5. Russell, Matthew A. Mining the Social Web: Data Mining Facebook, Twitter, LinkedIn, Google+, GitHub, and More. 2nd Ed. O'Reilly Media, Inc., 2013.
  6. Mark Pilgrim. Dive into Python 3. Chap. 13 Serializing Python objects. http://www.diveintopython3.net/serializing.html. (último acceso Mar. 2015).

In [ ]: