Introducción
El Ministerio de Transportes anunció que empezaría publicar los datos recabados, por medio de teléfonos móviles, de nuestra movilidad. El ministerio reúne esos datos, según dicen, para ayudarles a tomar mejores decisiones políticas respecto a la movilidad, y lo hacen, añaden, respetando en todo momento la Ley de Protección de Datos, anonimizando los datos que publican. Más allá de que todo esto pueda ser polémico, o que no se esté de acuerdo con las intenciones del Ministerio, lo cierto es que han dado acceso a los datos y podemos descargarlos y aplicar nuestros propios análisis.
Los datos
Los datos a los que dedicaremos nuestra atención son los viajes diarios a nivel ayuntamiento. Los podemos descargar por días o por meses completos. Por ejemplo, pinchando en este enlace te descargarás los datos del mes de enero de 2022. Es un tar
de tres gigas y dentro están los ficheros diarios de ese mes comprimidos.
Esos ficheros diarios son csv
que agrupan los movimientos por columnas:
- Temporales
- Fecha
- Hora
- Situación
- Provincia de residencia
- Origen del desplazamiento
- Destino del desplazamiento
- Sociales
- Sexo de la persona que se desplaza
- Edad
- Renta
- Viajes
- Distancia recorrida en cada viaje
- Cantidad de viajes
- Kilómetros recorridos en total
- Actividad de origen
- Actividad de destino
- Posibilidad de que el origen sea por estudios
- Posibilidad de que el destino sea por estudios
En total quince columnas, que usan de separador el símbolo de la tubería (|). Cada fichero tiene además unos diez millones de filas. Esto solo para un día, hay colgados dos años completos, 2022 y 2023. Si uniéramos todos los ficheros en un único csv
que incluyera toda la información el archivo tendría un tamaño de más de 650 GB. No todos los ordenadores en la casa de uno tienen esa cantidad de RAM en el momento de escribir esto (la fecha exacta debe de estar puesta por ahí arriba).
Miller
Miller
es una herramienta que permite trabajar con ficheros csv
sin necesidad de cargar los datos en memoria. He probado otros programas con distinto éxito (qsv
, csvtk
, duckdb
), son todos buenos programas, pero al final siempre me quedo con miller
.
Supongamos que queremos cambiar el separador original (|) del fichero del 01/01/2022 por una coma. Abrimos un terminal y tecleamos:
mlr -I --csv --ifs "|" cat 20220101_Viajes_municipios.csv.gz
mlr
es el comando demiller
-I
, Esta bandera cambia los ficheros in situ, sin tener que renombrarlos o utilizar ficheros temporales. Por defectomiller
enviaría a la salida estándar, que podríamos encauzar a otro fichero con>
.--csv
.Miller
puede manejar más tipos de ficheros además decsv
, como pueden serJSON
,tsv
, etc. Así que debemos indicar qué tipo de fichero es el que queremos modificar.--ifs "|"
o--ifs pipe
, que también vale, indica amiller
que el fichero de entrada emplea el símbolo “|” como separador.- Por último el fichero. Fíjate en que no he tenido que descomprimirlo para trabajar con él,
miller
funciona con algunos tipos de compresión comogz
obzip
.
Si hacemos un bucle con for
en bash
podemos transformar todos los ficheros que guardemos en un directorio. Tardará un rato, pero cualquier ordenador, aunque tenga poca potencia y poca memoria podrá hacer la faena.
Hagamos algo más complicado
El ejemplo de arriba es sencillo, y no conlleva ningún cálculo, pero supongamos que nos hemos bajado todos los fichero de un mes y queremos saber qué día de la semana se recorrren menos kilómetros de media en cada provincia.
El comando sería este:
mlr -I --ifs "|" -c --mfrom Transformar/*.csv.gz -- \
put '$fecha = sub(string($fecha), "(\d{4})(\d{2})(\d{2})", "\1-\2-\3"); $mes = sub(string($fecha), "\d{4}-(\d{2})-\d+", "\1")' \
then put 'segundos = strptime($fecha, "%Y-%m-%d"); $dia_semana = strftime(segundos, "%u")' \
then stats1 -a sum -f viajes_km -g residencia,dia_semana \
then stats1 -a mean -f viajes_km_sum -g residencia,dia_semana \
then top --min -f viajes_km_sum_mean -a -g residencia \
then join -j cpro -r residencia -f provincias.csv \
then sort -f cpro \
then cut -f provincia,dia_semana > dia_semana_menos_viajes_por_provincia.csv
Parece mucha cosa, pero no es para tanto. En primer lugar fíjate en todas esas líneas que empiezan por then
. Miller
permite ir encadenando órdenes recogiendo el resultado de la orden anterior de manera que podemos ir añadiendo pasos hasta conseguir el resultado que deseamos. Tampoco es necesario poner todo ese comando a la vez, de hecho es fácil equivocarse en algún paso y que hayamos perdido un montón de tiempo solo para descubrir que el resultado no es válido. Como yo ya he ejecutado ese comando con anterioridad sé que está bien, pero es mejor ir por partes y guardar la salida de cada paso en un fichero csv
intermedio.
mlr -I --ifs "|" -c --mfrom Movilidad/*.csv.gz -- put '$fecha = sub(string($fecha), "(\d{4})(\d{2})(\d{2})", "\1-\2-\3"); $mes = sub(string($fecha), "\d{4}-(\d{2})-\d+", "\1")'
Este comando va a transformar todos los ficheros que hemos descargado dentro del directorio Movilidad
cambiando el formato de la columna fecha
y añadiéndoles dos columnas nuevas: mes
y dia_semana
. Además de ello ahora los ficheros ya no utilizan como separador la “tubería” sino la coma.
Comentaré solo las novedades.
-c
es una abreviatura de--csv
.--mfrom <ficheros> --
, cuando queramos quemiller
se ejecute sobre todos los ficheros que cumplan un patrón o que estén dentro de una directorio lo indicaremos de esta manera. Anteriormente dije que podíamos hacerlo con el iterador con lashell
, pero de este modo parece más práctico.put
, este subcomando indica que vamos a modificar o crear nuevas columnas. Ponemos comillas simples alrededor de lo que indiquemos a este subcomando.$fecha
. Para hacer referencia a una columna pondremos el signo “$” al principio del nombre.sub
. Es una función que sustituye un texto por otro, para ello necesita que le indiquemos tres campos:- Indicamos el lugar donde hay que buscar lo que queremos cambiar, en este caso en la columna
fecha
, que ha sido reconocida pormiller
como un campo numérico, así que con la funciónstring
indicamos que es un campo alfanumérico para quesub
la maneje correctamente. - El segundo campo es una de expresión regular1 que divide
fecha
en sus tres partes; año, mes y día. - Por último intercalamos un guión entre cada una de las partes. Con la columna
fecha
en este formato la mayoría de programas para tratarcsv
van a identificar la tipo de la columna correctamente.
- Indicamos el lugar donde hay que buscar lo que queremos cambiar, en este caso en la columna
- Separaremos la creación de una nueva columna con
;
. Creamos la columna$mes
a partir de la columnafecha
.
Con el siguiente comando añadimos la columna con el día de la semana:
mlr -I -c --mfrom Movilidad/*.csv.gz -- put 'segundos = strptime($fecha, "%Y-%m-%d"); $dia_semana = strftime(segundos, "%u")'
- Dos funciones nuevas con
miller
, hay muchas más que puedes revisar en su documentación. Primero pasamos a segundos la fecha actual constrptime
y luego convertimos esos segundos en el día de la semana.
Una vez tenemos los ficheros csv
con las columnas que vamos a necesitar podríamos reutilizarlos para hacer análisis distintos del que vamos a hacer aquí. Seguimos:
mlr -c --mfrom Movilidad/*.csv.gz -- \
stats1 -a sum -f viajes_km -g residencia,dia_semana \
then stats1 -a mean -f viajes_km_sum -g residencia,dia_semana \
then top --min -f viajes_km_sum_mean -a -g residencia \
then join -j cpro -r residencia -f provincias.csv \
then sort -f cpro \
then cut -f provincia,dia_semana > dia_semana_menos_viajes_por_provincia.csv
stats1
, subcomando para hacer algunas estadísticas con las columnas, como la media, mediana, moda, sumatorios, etc. En este caso indicamos que queremos que sume todos los kilómetros indicados en la columnaviajes_km
agrupados porresidencia
ydia_semana
. Después necesitamos hacer la media, porque puede haber días entre semana festivos con movilidad distinta a la normal.top
. Con este subcomando eleginos el día con mayor valor, con la bandera--min
todo lo contrario. Hay que indicar también en qué columna buscar el mayor valor y por cuál la agrupamos.join
. Se pueden unir otras tablas con este subcomando, en este caso con el ficheroprovincias.csv
(descárgalo aquí). Con él relacionamos el código que aparece enresidencia
con un nombre más reconocible. Por ejemplo, la provincia 28 equivale a Madrid.sort
, ordena por la columna que indiquemos. El orden podría ser inverso, claro.cut
, ahora seleccionamos las columnas con las que quedarnos, eliminando así las columnas que ya no necesitamos.- Por último redirigimos el resultado a un fichero.
Resultados
He aplicado estos comandos a los dos años de los que tenemos datos, tarda casi siete horas desde que ya hemos añadido las columnas mes
y dia_semana
. Eso sí, el consumo de miller
no sube nunca de 2 GB. Te recomiendo la aplicación pueue
para organizar las ejecuciones de programas que consumen mucho tiempo.
Por si te interesa el resultado el día que más se viaja con diferencia es el viernes, se suman los viajes relacionados con el trabajo y los estudios a los viajes de ocio de fin de semana. Respecto a qué día se viaja menos hay más variedad.
Se viaja menos el martes en:
- Álava, Barcelona, Burgos, Cáceres, Córdoba, La Coruña, Granada, Jaén, León, La Rioja, Madrid, Málaga, Navarra, Asturias, Palencia, Las Palmas, Salamanca, Sevilla, Soria, Valladolid, Zaragoza, Ceuta y Melilla
Sábado:
- Albacete, Badajoz y Valencia.
Y domingo:
- Alicante, Almería, Ávila, Baleares, Cádiz, Castellón, Ciudad Real, Cuenca, Girona, Guadalajara, Gipuzkoa, Huelva, Huesca, Lleida, Lugo, Murcia, Ourense, Pontevedra, Santa Cruz de Tenerife, Cantabria, Segovia, Tarragona, Teruel, Toledo, Bizkaia y Zamora
-
No retrases el aprender expresiones regulares, si te gusta la informática te serán necesarias más pronto que tarde. ↩︎