Setup Menus in Admin Panel

School.Dataninja.it

Visualizzazioni dati con d3js: gestire scale e proporzioni

Quando hai visto come modificare attributi e stili degli elementi della pagina ti sei limitato a usarne i dati associati per impostare per esempio la dimensione del font di un testo. Spesso però l’ordine di grandezza dei dati a disposizione, che sono sempre una misura di un fenomeno reale, è radicalmente diverso da quello tipi di una pagina web. Prendi per esempio le dimensioni di un div. Su un classico schermo con risoluzione full-hd, visualizzando la finestra del browser a tutto schermo, la pagina ha a disposizione al massimo 1920 pixel in larghezza e 1080 in altezza. Rinunciando allo schermo intero, tra bordi della finestra, menù, barre varie, l’altezza è anche minore. E ormai solo una parte del traffico su web avviene da desktop su schermi così grandi, in molti casi ormai si naviga da tablet e smartphone, con schermi molto più piccoli.

Prendi ora in considerazione gli ordini di grandezza di alcuni dati molto usati. L’età delle persone è un numero intero tra zero e circa cento. Il PIL è un numero attorno alle migliaia di miliardi. Le popolazioni di un paese sono nell’ordine delle decine, se non centinaia di milioni. I limiti delle polveri sottili nelle città sono attorno alle decine di microgrammi per metro cubo. Numeri molto diversi tra loro che in una visualizzazione vuoi codificare negli attributi degli elementi geometrici o testuali di una pagina: larghezze, altezze, superfici, spessore dei bordi, colori, dimensione del font, ecc. Guarda questo esempio (illeggibile) della word cloud delle parole di questa pagina, in cui ogni parola ha una dimensione in px pari al numero delle sue occorrenze nel testo.

Word cloud…

Ecco il codice che genera quella visualizzazione.

In generale devi distinguere tra dimensioni quantitative e qualitative. Le prime sono ben rappresentate da numeri o date, le seconde da categorie. Le prime sono continue (da 1 a 100, oppure i giorni di quest’anno), le seconde discrete (“blu”, “rosso”, “giallo”, oppure “ottimo”, “buono”, “sufficiente”, “insufficiente”).

Le dimensioni associate ai tuoi dati rappresentano generalmente il dominio (domain, in inglese) della tua visualizzazione. Quelle associate invece agli attributi degli elementi della pagina sono il codominio, o range in inglese. Molto del lavoro progettuale di una visualizzazione sta proprio nel decidere come trasformare i valori del dominio in valori del codominio, eventualmente passando da dimensioni continue a discrete. Queste trasformazioni si chiamano funzioni di scala e in d3js sono proprio funzioni a tutti gli effetti, definite da un domain e un range, che prendono un valore del domain e ritornano il relativo valore nel range quando invocate.

Scale quantitative

Le scale quantitative sono quelle più intuitive, prendono valori da un dominio continuo e restituiscono valori da un codominio che può essere anch’esso continuo oppure anche discreto, dopo un processo di quantizzazione. Vediamole separatamente.

Dominio continuo, range continuo

La scala più semplice è quella lineare ed è implementata dal metodo d3.scale.linear(). Ecco l’esempio precedente, ma con la trasformazione del dominio delle occorrenze (numeri interi da 1 a 6) nel codominio delle dimensioni standard dei font di una pagina web (da 12px a 32px) con una funzione lineare: d3.scale.linear().domain([1,6]).range([12,32]) .

Word cloud…

Ed ecco il codice.

In generale il dominio, in questo caso un array con il valore minimo e quello massimo, viene calcolato direttamente dai dati, per esempio con il metodo d3.extent(array). Idem per il codominio, partendo dai vincoli delle dimensioni della pagina (per esempio la larghezza massima dell’elemento contenitore). Ecco un altro esempio in cui le occorrenze delle parole non sono codificate nelle dimensioni del font, ma nella larghezza di una barra colorata. La scala usata è sempre lineare, ma questa volta il codominio è la lunghezza della barra orizzontale in percentuale alla larghezza del contenitore: d3.scale.linear().domain([1,6]).range([5,100])  (un minimo di 5% per far vedere che comunque una barra c’è).

Word bar…

Ed ecco il codice.

D3js mette a disposizione altre due utili scale oltre a quella lineare: quella a potenza (d3.scale.pow(), con esponente qualsiasi, quindi anche la radice quadrata, d3.scale.sqrt()) e quella logaritmica, d3.scale.log(). La prima è molto utile quando si vogliono associare i dati a delle superfici invece che a dimensioni lineari. Nei grafici a bolle, per esempio, vuoi che sia la superficie del cerchio a essere proporzionale ai dati, non il suo raggio. Ma di fatto agisci sul raggio per disegnare un cerchio, per cui se vuoi che la trasformazione dato -> area sia lineare, quella dato -> raggio non deve esserlo, ma coinvolgere una radice quadrata (perché l’area del cerchio è proporzionale al quadrato del raggio, per cui il raggio è proporzionale alla radice quadrata dell’area e quindi al tuo dato). La scala logaritmica invece è utile quando hai domini molti estesi, con molti valori piccoli e pochi grandi che in una scala lineare schiaccerebbero tutti gli altri verso zero. È la scala più usata proprio per le word cloud, ecco l’esempio di prima con una scala logaritmica.

Word cloud…

Ed ecco il codice.

Dominio continuo, range discreto

La classica situazione in cui vuoi quantizzare i tuoi dati continui è quando vuoi lavorare con i colori. A parte il bianco e nero, lo spazio colore è sì continuo, ma non è lineare: nella rappresentazione RGB un colore è specificato da tre componenti, per cui è un punto in uno spazio tridimensionale. Visualizzarlo su uno schermo piatto è tutt’altro che semplice, per cui è meglio lavorare con delle palette di colori (che possono essere gradazioni dello stesso colore o colori proprio diversi) e associare i tuoi dati di volta a in volta a intervalli di colore diversi.

Lavorare con colori e scale continue è comunque possibile, se ti interessa giocare con la gradazione di un solo colore. C’è il bianco e nero, per esempio, che va appunto dal bianco rgb(255,255,255) al nero rgb(0,0,0) e di fatto è il range dei numeri da 0 a 255. Ma c’è anche l’opacità di un colore, che va da 1 (colore pieno in primo piano) a 0 (massima trasparenza, colore pieno dello sfondo) e che si può esprimere con rgba(r,g,b,alpha), con alpha valore dell’opacità che ha un range da 0 a 1.

Quantizzare dati continui richiede in generale la definizione di una serie di intervalli sul dominio da associare alle categorie discrete nel codominio. Si può fare in modo arbitrario, ma in generale ci sono alcune strategie standard che si usano nella stragrande maggioranza dei casi. D3js ha un metodo per ognuna di esse. Quella più semplice prevede la suddivisione del dominio in un certo numero di intervalli di uguale estensione ed è implementata dal metodo d3.scale.quantize(). Funziona molto bene se i tuoi dati sono distribuiti grosso modo uniformemente su tutto il dominio.

Un’altra strategia, leggermente più complessa, è quella che prevede la suddivisione del dominio in quantili ed è implementata dal metodo d3.scale.quantile(). In pratica, dato il numero di intervalli, si calcola la loro estensione in modo da rendere uniforme la popolazione dei dati in ogni intervallo. È molto efficace quando i tuoi dati non sono distribuiti in maniera uniforme, quando hai dei outliers o degli intervalli in cui i tuoi dati tendono a concentrarsi. La suddivisione del dominio in quantili è così più fitta (intervalli stretti) dove i dati sono più concentrati e più rada (intervalli larghi) dove lo è meno.

Infine il metodo d3.scale.threshold() permette di specificare degli intervalli arbitrari e quindi fare tutto a mano. Un esempio potrebbe essere la scala che trasforma i voti scolastici in decimi in voti qualitativi. La sufficienza è tra il 5 e il 6 o tra il 6 e il 7? E il 6– quanto è sufficiente?

Scale qualitative (o ordinali)

In questo caso il dominio è discreto, cioè i tuoi dati sono delle categorie. È la classica situazione quando si vuole costruire un istogramma: ci sono delle categorie discrete e un valore numerico associato a ognuna di esse, tipicamente un grafico a barre è adatto a rappresentarle. La funzione di scala di riferimento è il metodo d3.scale.ordinal() che può essere usato in due modi diversi.

Dominio discreto, range continuo

È un po’ il contrario di quanto visto prima. Ora hai un dominio discreto e vuoi quantizzare il range, che invece è continuo, in modo da associare a ogni intervallo i tuoi dati. L’applicazione più frequente riguarda il posizionamento delle barre di un istogramma: se hai dieci barre da posizionare su un asse lungo N pixel, a che distanza devi posizionarle l’una dall’altra, supponendo che tu voglia distribuirle uniformemente lungo tutto l’asse? In teoria questo problema non è del tutto banale, perché le barre hanno una larghezza e di solito vuoi prevedere una distanza minima tra barre consecutive. Il metodo d3.scale.ordinal() è estremamente flessibile da questo punto di vista: grazie ai metodi rangePoints() (per elementi senza una larghezza, come i punti) e rangeBands() (per elementi come le barre) ti risolverà molte situazioni apparentemente difficili.

Dominio discreto, range discreto

È l’ultima possibile combinazione ed è gestita sempre dal metodo d3.scale.ordinal(), ma questa volta invocato impostando un range esplicito. In questo modo gli elementi dell’array di categorie del dominio vengono associati uno a uno, in ordine, agli elementi dell’array di categorie del range. È il classico caso di quando vuoi colorare in maniera diversa delle categorie o delle serie in modo che siano facilmente individuabili e poi riconoscibili mediante una legenda: associ ogni categoria in entrata con un colore in uscita, a partire da una palette. Proprio per il fatto che molto spesso in casi come questo ti capiterà di lavorare con i colori, d3js definisce già delle scale cromatiche buone per tutti gli usi, in cui il range è già impostato. Porta con sé anche le famose palette di ColorBrewer, un progetto di gran successo orientato alla colorazione delle mappe.

Letture: 375