Ya hacía un par de semanas que no publicábamos nada, y es que con la Semana Santa hemos preferido dar un descanso a todos nuestros seguidores (y también a nosotros mismos…) 😉

Hoy volvemos a la carga con nuestro blog, y curiosamente lo hacemos en un día no habitual, como es un sábado. ¿Por qué?
Pues porque este fin de semana LocaliData  participa en las II Jornadas de Periodismo de Datos y Open Data (#jpd14), organizadas por el capítulo español de la Open Knowledge Foundation. Y dentro del denso programa de las jornadas hemos decidido ayudar un poco con un taller en el que iremos a las bases del tratamiento con ficheros de datos: la línea de comandos.
Así que allá vamos… Esperamos que os resulte de interés también a los que ya conocéis bastante de lo que se puede hacer simplemente con una terminal abierta y unos pocos comandos en Linux.

La línea de comandos

Comencemos emulando a Lope de Vega…

Esta sesión me manda hacer Adolfo
que en mi vida me he visto en tal aprieto;
la línea de comandos aquí os presento;
rumba rumbando van los primeros comandos.
Esta sesión comienza con la explicación de un conjunto muy básico de comandos con los que nos moveremos por el sistema de ficheros. Progresivamente iremos viendo cómo se pueden usar otros comandos para hacer el tratamiento de los datos que podemos tener en nuestro sistema de ficheros, y como podemos automatizar muchas de estas tareas, tan necesarias cuando uno tiene que hacer preprocesamiento de datos.
Atención: vamos a utilizar la terminal de sistema operativo Linux o de MacOs para todas estas tareas. En Windows existen otros comandos alternativos que se pueden utilizar, pero para facilitar esta tarea recomendamos instalar Cygwin, que nos permite emular el shell de Linux directamente. El instalador para Windows está disponible aquí, y basta con la instalación básica, siempre que se añada, para los últimos pasos del curso, el paquete curl durante la instalación.
Si alguno de los comandos que se presentan en la sección 1, especialente ls, no funciona adecuadamente, puede que el problema sea por las variables de entorno. En tal caso, hay que realizar las siguientes operaciones.
  1. En Mi PC o Equipo, pulsar el botón derecho del ratón, acceder a Propiedades / Avanzadas / Variables de Entorno. Otra opción es acceder por Panel de Control / Sistema / Propiedades de Sistema.
  2. Añadir la variable de entorno CYGWIN_HOME con valor C:cygwin (si aquí es donde se ha instalado Cygwin).
  3. Añadir en la variable de entorno PATH el valor ;C:cygwinbin
  4. Reiniciar el PC

1. Moviéndose por el sistema de ficheros, copiando y borrando ficheros y directorios, etc.

Existen varios comandos que son básicos para moverse por el sistema de ficheros, crear y borrar directorios, etc. Fundamentalmente cd, ls, rm, touch, echo, mkdir y rmdir. Si ya estás acostumbrado a trabajar con la línea de comandos, puedes pasar directamente al punto 2.
Una vez tenemos una terminal abierta con la línea de comandos, podemos ver los ficheros y directorios que hay disponibles en el directorio en el que nos encontramos (en el que nos hemos descargado nuestro fichero):
ls -la
Podemos crear directorios utilizando el comando mkdir. Por ejemplo, mkdir directorio1 crear el directorio 1 en el directorio en el que nos encontramos, y podemos también crear un subdirectorio ejecutando mkdir directorio1/directorio2.
Podemos acceder al directorio comerciosMadrid usando cd comerciosMadrid. Y volver de nuevo al directorio anterior usando cd .. También podemos acceder al directorio directorio1/directorio2 usando cd directorio1/directorio2. Podemos volver al directorio inicial personal escribiendo cd. También podemos acceder al directorio raíz usando cd /. Y si queremos en algún momento saber dónde estamos en nuestra estructura de directorios, simplemente tenemos que usar el comando pwd.
Además, podemos usar el tabulador para ir completando nuestro comando según vayamos escribiendo los nombres de los ficheros.
Si queremos crear un fichero vacío, podemos usar touch pruebaFicheroVacio.txt

Si queremos incluir un texto en un fichero, podemos usar echo “hola mundo” > prueba.txt. Si queremos añadirlo a un fichero existente, podemos usar echo “hola mundo” >> prueba.txt. Si queremos ver lo que tenemos ahora mismo como contenido de este fichero, podemos ejecutar cat prueba.txt.

Podemos ahora borrar el fichero prueba.txt usando rm prueba.txt. También podemos borrar directorios usando rmdir. Por ejemplo, rmdir directorio1/directorio2, y rmdir directorio1. Si queremos borrar más rápido una estructura de directorios podemos ejecutar rm -r directorioPrueba.
Si queremos renombrar un fichero ejecutaremos:
mv pruebaFicheroVacio.txt ficheroVacio.txt.
También funciona con directorios, de la misma manera.
Si queremos copiar un fichero haremos cp ficheroVacio.txt ficheroVacio2.txt. También funciona con directorios.

¿Y cuál fue ese comando que ejecutamos hace un rato y del que ya no nos acordamos? Probad ejecutando el comando history.

2. Dividiendo nuestros ficheros en varios trozos

En muchas ocasiones tenemos que trabajar con un fichero muy grande. Tan grande que ni siquiera lo podemos abrir con nuestras herramientas habituales. Algún ejemplo podría ser la información detallada de fincas catastrales, los microdatos de la EPA, etc.
Para que la descarga del fichero original en el que tenemos nuestros ejemplos no sea muy lenta, vamos a trabajar con un fichero que sí que podemos abrir con herramientas como Microsoft Excel, R, Open Refine, etc. Se trata del fichero ComerciosMadrid.csv, obtenido del portal de datos abiertos del Ayuntamiento de Madrid. Lo hemos dejado en https://dl.dropboxusercontent.com/u/1406775/ComerciosMadrid.csv, así que lo podéis descargar de esa URL. Incluso podéis utilizar la línea de comandos para ello, ejecutando el siguiente comando:
curl https://dl.dropboxusercontent.com/u/1406775/ComerciosMadrid.csv > ComerciosMadrid.csv

Este fichero tiene algo más de 150,000 líneas (esto se puede comprobar utilizando wc -l ComerciosMadrid.csv)

Una opción es la de dividir el fichero en varios ficheros más pequeños. Por ejemplo, podemos dividir el fichero anterior en ficheros de 2000 líneas cada uno. Para ello, se puede utilizar el comando split, como mostramos a continuación:
split -l 2000 ComerciosMadrid.csv
Esto generará 76 ficheros, con nombres xaa, xab, …, xcx, cada uno de ellos con 2000 líneas (excepto el último). Podemos moverlos a un directorio que vamos a crear para ellos, mediante los dos siguientes comandos:
mkdir comerciosMadrid
mv x* comerciosMadrid
Otra opción que puede ser interesante para reducir el tamaño de un fichero, y así poderlo tratar con mayor facilidad al ser más pequeño, puede ser la de obtener sólo los valores de algunos campos del CSV. Por ejemplo, en el fichero CSV anterior, tenemos los siguientes campos:
“id_local”;”id_distrito_local”;”desc_distrito_local”;”id_barrio_local”;”desc_barrio_local”;”desc_seccion_censal_local”;”coordenada_x_local”;”coordenada_y_local”;”desc_tipo_acceso_local”;”desc_situacion_local”;”id_ndp_edificio”;”clase_vial_edificio”;”desc_vial_edificio”;”nom_edificio”;”num_edificio”;”cal_edificio”;”secuencial_local_PC”;”id_ndp_acceso”;”clase_vial_acceso”;”desc_vial_acceso”;”nom_acceso”;”num_acceso”;”cal_acceso”;”coordenada_x_agrupacion”;”coordenada_y_agrupacion”;”id_agrupacion”;”nombre_agrupacion”;”id_tipo_agrup”;”desc_tipo_agrup”;”id_planta_agrupado”;”id_local_agrupado”;”rotulo”;”id_seccion”;”desc_seccion”;”id_division”;”desc_division”;”id_epigrafe”;”desc_epigrafe”
Si estamos interesados únicamente en los campos correspondientes a coordenada_x_local y coordenada_y_local (que son los campos séptimo y octavo, y que nos pueden permitir representar nuestros datos en un mapa), podemos ejecutar el siguiente comando:
cut -f 7,8 -d ‘;’ ComerciosMadrid.csv
Y para que el resultado se almacene en un fichero, podemos usar el siguiente comando:
cut -f 7,8 -d ‘;’ ComerciosMadrid.csv > ComerciosMadridCoordenadas.csv

Nota: si este comando devuelve el siguiente error: error: illegal byte sequence, entonces se pueden ejecutar los dos siguientes comandos:

export LC_CTYPE=C 
export LANG=C
 

Para los más rápidos:

  • ¿Cómo dividirías el fichero de comercios que hemos utilizado anteriormente para que los nombres de los ficheros que se generen comiencen como ComerciosMadrid_?
    Pista: puedes utilizar man split para ver más detalles sobre este comando, así como para cualquier otro comando.
  • El comando cut también se puede utilizar para dividir un fichero en campos si tienen un tamaño predefinido (por ejemplo, los ficheros de Catastro o algunos ficheros de microdatos del INE). ¿Cuál sería el comando a utilizar para obtener del fichero de comercios de Madrid todos los códigos de los locales, que se encuentran en cada línea entre las posiciones 2 y 10? ¿Cuál es el tamaño final del fichero obtenido?

3. Buscando ficheros que contengan alguna cadena de caracteres

¿Cuántas veces habéis tenido que buscar algún fichero que contenga una cadena de caracteres concreta? Por ejemplo, imaginemos que tenemos un montón de CSVs en nuestro ordenador, con información de comercios de Madrid, y queremos encontrar los CSVs que tengan información sobre bares de la franquicia “100 Montaditos”.

Para ello, podemos usar el comando grep, que nos permite buscar una cadena de caracteres concreta, o incluso una expresión regular, dentro de un conjunto de ficheros en los directorios que especifiquemos. Vamos a buscar nuestros 100 Montaditos en el directorio comerciosMadrid. Para ello, ejecutaremos el siguiente comando:
grep ‘100 MONTADITOS’ -r .

La opción -r es para que el comando busque de manera recursiva por toda nuestra estructura de directorios, y el . es para especificar que empiece por el directorio en el que nos encontremos.

Para los más rápidos:

  • ¿Cómo buscarías los ficheros que contengan 100 MONTADITOS o 100 Montaditos? Es decir, ¿cómo harías las búsquedas independientes de si se utilizan mayúscula o minúsculas?
  • ¿Cómo guardarías en un fichero todas las líneas que contienen 100 MONTADITOS?

4. Reemplazando cadenas de caracteres dentro de nuestros ficheros

Otra actividad habitual es la de tener que modificar algún dato dentro de nuestros ficheros. Esto es algo que se puede hacer fácilmente con herramientas como Open Refine, pero en ocasiones nos puede interesar realizar este tipo de sustituciones directamente en los ficheros fuente, antes de procesarlos con este tipo de herramientas. Para ello, vamos a utilizar el comando sed.

Vamos a continuar con el ejemplo anterior, y supongamos que queremos cambiar todas las apariciones de la cadena de caracteres 100 (de 100 MONTADITOS) por la cadena CIEN. Para hacer esta sustitución, en el fichero original ComerciosMadrid.csv, ejecutaremos el siguiente comando:
sed s/100/CIEN/ ComerciosMadrid.csv

Sin embargo, con el comando anterior también podemos modificar sin querer algún trozo de texto que contenga la cadena “100”. En tal caso, vamos a sustituir todas las apariciones de la cadena de caracteres 100 MONTADITOS por la cadena CIEN MONTADITOS. Para hacer esta sustitución, en el fichero original ComerciosMadrid.csv, ejecutaremos el siguiente comando (como se puede ver, para añadir el espacio en blanco utilizamos el carácter ):
sed s/100 MONTADITOS/CIEN MONTADITOS/ ComerciosMadrid.csv

Para los más rápidos:

  • ¿Cómo reemplazarías cadenas como 100 MONTADITOS, y en general cualquier número seguido de un conjunto de caracteres, por la cadena de caracteres seguida del número (es decir, MONTADITOS 100)?

5. Descargando ficheros de APIs de datos abiertos con curl

En ocasiones tenemos a nuestra disposición URLs desde las que nos podemos descargar ficheros que nos puedan interesar. Concretamente, en el caso de los datos abiertos, estos datos pueden estar disponibles mediante APIs REST para cada uno de los datos en los que podemos estar interesados.
Uno de los ayuntamientos que lleva más tiempo trabajando en la creación de APIs para sus datos abiertos es Zaragoza con su API de datos abiertos. Así que vamos a ver qué podemos obtener si le hacemos la siguiente petición:
curl -H “Accept:text/csv” http://www.zaragoza.es/api/recurso/turismo/restaurante/14
También tiene una API el Gobierno de Aragón en su portal de datos abiertos, y concretamente en lo que se ha denominado la AragoDBpedia. Por ejemplo, si queremos obtener un CSV del municipio de Jaca, podremos realizar la siguiente petición (en este caso con una API_KEY, muy habitual para el acceso a APIs REST):
curl -H “Accept:text/csv” http://opendata.aragon.es/recurso/territorio/Municipio/Jaca.csv?api_key=e103dc13eb276ad734e680f5855f20c6&_view=ampliada
Para los más rápidos:
  • ¿Podéis acceder a la descripción de la API de datos abiertos de Zaragoza e intentar determinar cuántos restaurantes hay en Zaragoza, según esta API?
  • ¿Cómo creéis que se podría obtener información de la provincia de Huesca en la API de datos de Open Data Aragón?

6. Realizando scripts para automatizar tareas

En ocasiones necesitamos realizar varias operaciones sobre un conjunto de ficheros, u operaciones repetitivas. Esto se puede conseguir creando scripts, que luego se pueden ejecutar desde la línea de comandos.

Comenzaremos con un script muy sencillo, que vuelve a regenerar el fichero inicial de comercios de Madrid a partir de los ficheros en los que lo dividimos. Para ello, se puede crear primero el fichero unirFicheros.sh utilizando el comando touch unirFicheros.sh. A continuación, el fichero se debe hacer ejecutable, mediante el siguiente comando:
chmod u+x unirFicheros.sh
Y finalmente se debe editar el fichero con los siguientes comandos:
for f in `ls comerciosMadrid/*`; do
cat $f >> ComerciosMadridRegenerado.csv
done

A continuación, se puede ejecutar este script, ejecutando ./unirFicheros.sh
Para los más rápidos:
  • ¿Cómo podemos obtener en un único fichero los datos completos de todos los restaurantes de Zaragoza, utilizando su API y algún script para tratar los datos adecuadamente?
  • ¿Cuál sería el script a utilizar para que, dado el listado de todos los municipios de la provincia de Huesca, podamos hacer llamadas a la API de AragoDBpedia y obtener todos sus datos?