Data Visualization con Flask
Le storie raccontate fino ad oggi, come ad esempio I Cambiamenti Climatici, descrivono la realtà per mezzo di analisi statiche dei dati a disposizione. L’articolo di oggi ha l’obiettivo di rappresentare un approccio dinamico alla realizzazione di storie basate su dati. La soluzione descritta si basa sull’esperimento effettuato con www.monitoraggioelezioni.it e mostra come realizzare delle visualizzazioni web interattive, a partire dai dati grezzi, con python e javascript.
Monitoraggio elezioni fornisce un servizio di monitoraggio della visibilità social e web dei principali candidati politici alle elezioni italiane del 2018. Dopo la creazione di una serie di articoli sull’utilizzo statico dei dati social (es. Un giorno con #trump, La “speculazione sociale” su Facebook) è stato realizzato il sito www.monitoraggioelezioni.it che sfrutta tali dati in modo dinamico.
Approccio alla Data Visualization in Flask
La figura di seguito mostra l’architettura di massima utilizzata per la realizzazione delle visualizzazioni dinamiche:
L’architettura si compone di 2 macro mondi:
La componente di data integration si occupa di: leggere le fonti, elaborare il dato e storicizzarlo. I principali strumenti utilizzati in questa fase sono:
- Python, linguaggio di programmazione
- Jupyter, ambiente per la parte di data integration
- PostgreSQL, database
La componente di data visualization si occupa di: leggere il dato elaborato al punto precedente e visualizzarlo. I principali strumenti utilizzati sono:
- Python, linguaggio di programmazione
- Flask, framework web
- PostgreSQL, database
- Javascript e HTML per la presentazione dei dati
In molti casi le due componenti, data integration e visualization, possono essere unite per realizzare applicazioni real-time. La scelta di disaccoppiare le due componenti è stata effettuata principalmente dopo aver valutato 3 fattori:
- Il dato real-time non è strettamente necessario, il dato si aggiorna infatti ogni ora.
- Costi infrastrutturali nulli: è stato utilizzato un raspberry pi 3 come server.
- Focus sulla parte di data visualization rispetto alla parte di data integration.
Data Integration
Il codice di www.monitoraggioelezioni.it relativo alla componente di data integration è pubblicato sul repository GitHub od_elezioni. Per comprendere meglio la fase di data integration, analizzeremo in dettaglio il codice per il calcolo dei fan di una pagina Facebook: Facebook_Fans.ipynb
Librerie
La gestione delle librerie python è effettuata tramite il gestore virtualenev. In particolare, vengono utilizzate le librerie Facebook per la lettura dei dati del social network e la libreria Pandas per il trattamento dei dati.
# import librerie import os import facebook import requests import datetime import pandas as pd import numpy as np from sqlalchemy import create_engine import json
File di configurazione
Il file di configurazione è esterno alla cartella di GitHub e permette il caricamento delle password del database e delle API Facebook.
# configuration file config = {} config_path = os.path.join(os.path.abspath('../../')) config_name = 'config.py' config_file = os.path.join(config_path,config_name) exec(open(config_file).read(),config) fb_app_id=config['FB_APP_ID'] fb_app_secret=config['FB_APP_SECRET'] fb_app_userid=config['FB_APP_USERID']
Lettura dei dati
La fase di lettura dei dati si occupa di creare la connessione con Facebook tramite le API.
# get access token payload = {'grant_type': 'client_credentials', 'client_id': fb_app_id, 'client_secret': fb_app_secret} response = requests.post('https://graph.facebook.com/oauth/access_token?', params = payload) json_data = json.loads(response.text) access_token = json_data['access_token'] # get graph Facebook graph = facebook.GraphAPI(access_token=access_token, version='2.7')
Elaborazione
Il cuore della fase di data integration è l’elaborazione dei dati, in questa fase è stato realizzato un metodo che sfrutta le API Facebook e, per ogni candidato politico fornito in input, restituisce il numero di fan della rispettiva pagina Facebook, identificata tramite un apposito id.
# Candidati Elezioni users = [ {'user':'Renzi','user_id':'113335124914'}, {'user':'Di Maio','user_id':'522391027797448'}, {'user':'Salvini','user_id':'252306033154'}, {'user':'Pisapia','user_id':'112352038802143'}, {'user':'Meloni','user_id':'38919827644'}, {'user':'Berlusconi','user_id':'116716651695782'} ] # get today's date todays_date = datetime.datetime.now() # get fans def get_fans(fb_user, fb_id, graph, date): info = graph.get_object(id=fb_id, fields='name,fan_count,posts') fan = info['fan_count'] fan_d = {'dt_rif':todays_date,'user':fb_user,'fb_fans':fan} return fan_d fb_fans = [] for user in users: fb_fans.append(get_fans(user['user'],user['user_id'],graph,todays_date)) df = pd.DataFrame(fb_fans)
Scrittura
Il dato ottenuto viene salvato e storicizzato in una tabella postgresql chiamata fb_fans.
# get database connection db=config['DATABASE_ELE'] schema=config['SCHEMA_ELE'] engine = create_engine(db) # write on db df.to_sql('fb_fans', engine, schema=schema, if_exists='append')
Come ultimo step della fase di data integration, il tutto viene schedulato, ogni ora, con crontab.
Data Visualization
Per comprendere meglio la fase di data visualization analizzeremo la struttura delle cartelle del repository mon_elezioni e i passi che portano alla visualizzazione del numero di facebook fans.
Struttura delle cartelle
La struttura delle cartelle dipende molto dal framework utilizzato, in questo caso Flask.
- Cartella Padre
\elezioni
- runserver.py
Il file runserver.py contiene il codice per avviare l’applicazione ed è lo script che viene lanciato da linea di comando per avviare l’applicazione.
- Cartella Elezioni
\static
\templates
\views
__init__.py
Il modello utilizzato è il classico MVC, semplificato della componente Model. Una volta avviato lo script runserver.py, viene richiamato lo script __init__.py che si occupa di avviare le configurazioni di base (es. avvio del database) e di richiamare il Controller.
La cartella delle views corrisponde alla classe Controller del modello MVC e si occupa di leggere i dati dal database e fornirli ai templates.
La cartella dei templates corrisponde alla classe View del modello MVC e contiene le pagine .html che si occupano di visualizzare il dato.
Infine, la cartella static contiene tutta la componente statica delle pagine web, ovvero: css, javascript e immagini.
Visualizzazione di un Dato – Numero di Facebook Fans
Per visualizzare il numero di facebook fans sulla pagina principale di www.monitoraggioelezioni.it, gli step sono i seguenti:
- Controller -> /views/osservatorio.py
Le librerie sono sempre gestite all’interno di un virtualenv dedicato. Il tag @app.route indica le modalità di routing dell’applicazione. In questo caso la chiamata alla pagina home di www.monitoraggioelezioni.it richiama il metodo osservatorio().
Il metodo osservatorio() si occupa di leggere l’ultimo valore di facebook fans presente in tabella e di fornirlo al template osservatorio.html.
# coding=utf-8 from __future__ import division from elezioni import app, get_db, render_template, url_for from flask import request import json @app.route('/') def osservatorio(): engine = get_db() cur = engine.execute( ''' select fb_fans from ''' + app.config['SCHEMA_ELE'] + '''."fb_fans" where "user"=''' "'" + app.config['USER1'] + "'" ''' order by dt_rif desc limit 1; ''') fb_fans = cur.fetchall() return render_template('osservatorio.html', fb_fans=fb_fans)
- View -> /templates/osservatorio.html
Per comodità e, per velocizzare le struttura delle pagine html, è stato acquistato un template di base, in questo caso Limitless che fornisce una base di html statico sulla quale realizzare la “dinamicità”.
I templates sono strutturati in modo che tutte le pagine ereditano una pagina padre, il layout.html, che si occupa della struttura generica e della gestione di header e footer. I singoli templates, invece, si occupano di gestire il contenuto di ogni pagina. Ad esempio, il template osservatorio.html è responsabile della pagina di home di www.monitoraggioelezioni.it.
All’interno dei templates, oltre al codice html, è presente il codice Jinja che si occupa di realizzare la “dinamicità” delle pagine html, leggendo i valori forniti dalle views. In questo caso viene esposto all’interno della pagina html, il valore fb_fans.
<!-- Simple statistics Facebook --> <div class="panel panel-body panel-body-accent"> <div class="media no-margin"> <div class="media-left media-middle"> {% if u=='Renzi' %} <a href="https://www.facebook.com/{{id_user1_fb}}" target='_blank'><i class="icon-facebook2 icon-2x text-primary"></i></a> {% elif u=='Di Maio' %} <a href="https://www.facebook.com/{{id_user2_fb}}" target='_blank'><i class="icon-facebook2 icon-2x text-primary"></i></a> {% elif u=='Berlusconi' %} <a href="https://www.facebook.com/{{id_user3_fb}}" target='_blank'><i class="icon-facebook2 icon-2x text-primary"></i></a> {% endif %} </div> <div class="media-body text-right"> {% if u=='Renzi' %} <h3 class="no-margin text-semibold">{{"{:,}".format(fb_fans[0].fb_fans)}}</h3> {% elif u=='Di Maio' %} <h3 class="no-margin text-semibold">{{"{:,}".format(fb_fans2[0].fb_fans)}}</h3> {% elif u=='Berlusconi' %} <h3 class="no-margin text-semibold">{{"{:,}".format(fb_fans3[0].fb_fans)}}</h3> {% endif %} <span class="text-uppercase text-size-mini text-muted">Facebook Fans</span> </div> </div> </div>
Il risultato ottenuto è il seguente, ovvero la rappresentazione in alto a destra del numero di Facebook fans.
Una volta compresi gli strumenti e il metodo che dal dato grezzo porta alla sua visualizzazione è possibile aggiungere ulteriori e nuove visualizzazioni replicando i passi indicati.
Visualizzazione di un Grafico – Trend dei Facebook Fans
La realizzazione di grafici, ovvero la componente core della data visualization, merita invece un discorso a parte. Nell’immagine precedente troviamo ad esempio il grafico corrispondente al Trend dei Facebook Fans.
La realizzazione dei grafici segue un procedimento leggermente diverso rispetto ai semplici numeri. Il dato viene recuperato sempre dal controller ma, al contrario di un semplice numero, è consigliabile restituirlo in output sotto forma di json. Il dato json viene successivamente letto tramite javascript ed elaborato con una delle tante librerie presenti (es. d3.js o chart.js per citarne qualcuna).
- Controller -> /views/facebook.py
In questo caso il metodo non viene richiamato nella homepage ma è separato e sarà richiamato ad hoc, via javascript, da una chiamata ajax necessaria per recuperare il json.
# json trend fb fans user 1 @app.route('/trend_fb_fans1') def trend_fb_fans1(): engine = get_db() cur = engine.execute( ''' select to_char(dt_rif,'YYYY-MM-DD') as date, fb_fans as value from ''' + app.config['SCHEMA_ELE'] + '''."fb_fans" where "user"=''' "'" + app.config['USER1'] + "'" ''' order by dt_rif asc ''') result = cur.fetchall() return json.dumps([dict(r) for r in result])
- View -> /templates/osservatorio.html
All’interno del template, una volta posizionato il grafico, è necessario definire un identificativo del grafico che verrà poi richiamato da uno script javascript.
<!-- Basic area chart --> </br> <div class="text-muted text-size-small">Trend Facebook Fans</div> {% if u=='Renzi' %} <div id="monthly-sales-stats1"></div> {% elif u=='Di Maio' %} <div id="monthly-sales-stats2"></div> {% elif u=='Berlusconi' %} <div id="monthly-sales-stats3"></div> {% endif %} <!-- /basic area chart -->
- Grafico -> /static/assets/js/elezioni_chart.js
Lo script che si occupa di richiamare il json precedente e disegnare il grafico relativo al trend dei facebook fans è presente all’interno della cartella static.
I grafici cosi come tutti gli altri componenti possono essere realizzati da zero oppure presi dai diversi template già pronti presenti sulla rete. In questo caso è stato utilizzato un grafico già presente nel template Limitless e opportunamente modificato per adattarlo ai nuovi dati.
Conclusioni
In generale, gli approcci alla Data Visualization possono essere molteplici. Il metodo qui presentato richiede la conoscenza di molte tecnologie, che vanno dal back-end al front-end. L’articolo fornisce un’overview completa di come realizzare delle visualizzazioni dinamiche con python e javascript, grazie al framework flask. Inoltre, fornisce tutte le indicazioni ed i link utili per approfondire i singoli argomenti e creare la propria visualizzazione.
Per quanto riguarda altri approcci, si segnala l’ottimo lavoro che sta svolgendo il team digitale, con la realizzazione del Data & Analytics Framework. Presto saranno disponibili degli strumenti pronti all’uso per la realizzazione di visualizzazioni web basate su dati, quali: Superset, Metabase e Jupyter, con la possibilità di combinare le visualizzazioni realizzate in vere e proprie storie.