Corso

Saper creare grafici facilmente comprensibili ma sofisticati è fondamentale per diventare un data scientist di successo. Le visualizzazioni sono un ottimo modo per raccontare la storia nascosta nei tuoi dati. Mettono in evidenza le relazioni all'interno dei dati e rivelano informazioni visibili all'occhio umano che non possono essere trasmesse solo con numeri e cifre. Ma sai cosa è ancora meglio delle visualizzazioni per l'analisi dei dati? Le visualizzazioni interattive!
Purtroppo, da principiante, può sembrare un compito scoraggiante. Python e R offrono entrambi una vasta gamma di strumenti e trucchi per aiutarti nel compito. In questo tutorial, ti presenteremo Altair. Con Altair potrai creare visualizzazioni significative, eleganti ed efficaci con poche righe di codice e in pochissimo tempo. Iniziamo!
Che cos'è Altair?
Altair è una libreria Python progettata per la visualizzazione statistica. È di natura dichiarativa (torneremo su questa definizione più avanti). Si basa su Vega e Vega-Lite, entrambe grammatiche di visualizzazione che ti permettono di descrivere l'aspetto visivo e il comportamento interattivo di una visualizzazione in formato JSON.
API imperative vs. dichiarative
Come accennato, Python ha già un elenco di ottimi strumenti e librerie a tua disposizione per la visualizzazione dei dati, e molti supportano anche le visualizzazioni interattive. Ma cosa rende Altair diverso?
La risposta sta nella sua natura dichiarativa rispetto alle API imperative.
Con le API imperative, devi concentrarti sul "come fare qualcosa" piuttosto che su "cosa vuoi fare". Dovrai pensare di più al codice necessario per creare la visualizzazione e specificare manualmente i passaggi di tracciamento: limiti degli assi, dimensioni, rotazione, legende, ecc.
Come data scientist, Altair ti permetterà di concentrare tempo ed energie più sui dati — capirli, analizzarli e visualizzarli — piuttosto che sul codice necessario per farlo. Significa che puoi definire i dati e l'output che ti aspetti di vedere (come dovrebbe apparire la visualizzazione alla fine), e Altair eseguirà automaticamente per te le manipolazioni necessarie.
Installazione
In questo tutorial useremo un dataset di esempio da Vega datasets. Per installare Altair, insieme ai Vega datasets, digita quanto segue nella console:
$ pip install altair vega_datasets
Se usi il gestore di pacchetti conda, l'equivalente è:
$ conda install -c conda-forge altair vega_datasets
Dopodiché, passa al tuo Jupyter Notebook e importa Altair per iniziare. Per saperne di più su Jupyter Notebook, dai un'occhiata alla guida definitiva a Jupyter Notebook di DataCamp.
Dati
Ora che abbiamo installato Altair insieme ai Vega dataset, è il momento di caricare alcuni dati. Parti con i seguenti import:
import pandas as pd
import altair as alt
Perché importare Pandas?
Poiché i dati in Altair sono costruiti attorno al DataFrame di Pandas, significa che puoi manipolare i dati in Altair allo stesso modo in cui lavoreresti con i DataFrame di Pandas.
E anche se internamente Altair memorizza i dati nel formato di un DataFrame di Pandas, ci sono diversi modi per fornire i dati:
- come DataFrame di Pandas
- come oggetto Data o correlato (cioè UrlData, InlineData, NamedData)
- come stringa URL che punta a un file di testo in formato JSON o CSV
- come oggetto che supporta la geo_interface (ad es., Geopandas GeoDataFrame, geometrie Shapely, oggetti GeoJSON)
Movies Dataset
In questo tutorial lavoreremo con il Movies Dataset dal dataset di Vega. Questo o dataset simili relativi ai film sono famosi nella community della data science, soprattutto nel contesto dei motori di raccomandazione. Tuttavia, noi ci limiteremo ad analizzarlo con visualizzazioni tramite Altair.
Il dataset contiene informazioni sui film come il titolo, gli incassi negli Stati Uniti e nel mondo, insieme al budget di produzione, il genere, le valutazioni da IMDB e Rotten Tomatoes. Non useremo tutte le colonne ma solo alcune, per imparare meglio come funziona Altair come strumento di visualizzazione.
Andiamo avanti, recuperiamo il dataset ed eseguiamo qualche semplice analisi per capirlo meglio.
# Importing the Vega Dataset
from vega_datasets import data as vega_data
movies_df = pd.read_json(vega_data.movies.url)
# Checking the type of data that we get
print("movies_df is of the type: ", type(movies_df))
print("movies_df: ", movies_df.shape)
movies_df is of the type: <class 'pandas.core.frame.DataFrame'>
movies_df: (3201, 16)
Come hai visto, movies_df è in realtà un DataFrame di pandas e contiene 3201 film (righe) con 16 informazioni di feature (colonne) ciascuna. Diamo un'occhiata ai dati nel dataframe.
movies_df.head(5)
| Title | US_Gross | Worldwide_Gross | US_DVD_Sales | Production_Budget | Release_Date | MPAA_Rating | Running_Time_min | Distributor | Source | Major_Genre | Creative_Type | Director | Rotten_Tomatoes_Rating | IMDB_Rating | IMDB_Votes | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | The Land Girls | 146083.0 | 146083.0 | NaN | 8000000.0 | Jun 12 1998 | R | NaN | Gramercy | None | None | None | None | NaN | 6.1 | 1071.0 |
| 1 | First Love, Last Rites | 10876.0 | 10876.0 | NaN | 300000.0 | Aug 07 1998 | R | NaN | Strand | None | Drama | None | None | NaN | 6.9 | 207.0 |
| 2 | I Married a Strange Person | 203134.0 | 203134.0 | NaN | 250000.0 | Aug 28 1998 | None | NaN | Lionsgate | None | Comedy | None | None | NaN | 6.8 | 865.0 |
| 3 | Let's Talk About Sex | 373615.0 | 373615.0 | NaN | 300000.0 | Sep 11 1998 | None | NaN | Fine Line | None | Comedy | None | None | 13.0 | NaN | NaN |
| 4 | Slam | 1009819.0 | 1087521.0 | NaN | 1000000.0 | Oct 09 1998 | R | NaN | Trimark | Original Screenplay | Drama | Contemporary Fiction | None | 62.0 | 3.4 | 165.0 |
I nomi delle colonne sono:
movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes'],
dtype='object')
Ora useremo la funzione to_datetime di pandas per convertire la colonna Release_Date in una vera data e poi estrarre da lì il valore dell'"anno". Più avanti useremo questa nuova colonna nelle nostre visualizzazioni.
def extract_year(value):
return pd.to_datetime(value, format='%b %d %Y').year
movies_df["Year"] = movies_df["Release_Date"].apply(extract_year)
Se ora controlli i nomi delle colonne, vedrai una nuova colonna chiamata Year che contiene l'anno corrispondente a ciascun film.
movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes', 'Year'],
dtype='object')
Ora passeremo a creare qualche grafico. Ma prima, esegui questo pezzo di codice:
alt.renderers.enable('notebook')
RendererRegistry.enable('notebook')
Il codice sopra consente ad Altair di renderizzare inline nel Jupyter notebook, così possiamo vedere le figure che sviluppiamo. Se incontri altri problemi, consulta la pagina di troubleshooting di Altair.
A una prima occhiata ai dati, sarebbe interessante vedere la relazione tra quanto denaro è stato speso per realizzare il film (Production_Budget) e quanto il film ha effettivamente incassato (Worldwide_Gross). Tracceremo questi valori sugli assi X e Y.
Tuttavia, il dataset è relativamente grande — dato che il nostro obiettivo è divertirci con Altair. Quindi, per il momento, lavoreremo solo con pochi punti dati. Vediamo il numero di film in un dato anno nel nostro dataset e scegliamo un anno su cui concentrarci.
movies_df["Year"].value_counts()
2006 220
2005 210
2002 208
2004 192
2000 188
...
1943 1
2015 1
2027 1
2037 1
1928 1
Name: Year, Length: 91, dtype: int64
Concentriamoci sull'anno 2000 e creiamo un nuovo dataframe movies_2000 contenente solo i dati di quest'anno. Dovrebbe contenere 188 righe (film).
movies_2000 = movies_df[movies_df["Year"] == 2000]
movies_2000.shape
(188, 17)
Ora lavoreremo alla nostra prima visualizzazione. Per farlo, approfondiamo un po' il formato di Altair.
Chart
I chart sono un oggetto fondamentale in Altair e accettano un singolo argomento: un dataframe. Definiamo un chart così:
alt.Chart(DataFrame)
Da solo, però, il chart non fa molto se non specificare i dati con cui vuoi lavorare; poi devi indicare cosa vuoi farci. Qui entrano in gioco i mark.
Mark
La proprietà mark ti permette di specificare come i dati devono essere rappresentati nel grafico. Di seguito sono riportate le proprietà di base dei mark offerte da Altair:

Per la proprietà mark, vogliamo che ogni valore nel DataFrame sia segnato come un punto (cerchio) nel grafico. Quindi, scriveremo:
alt.Chart(DataFrame).mark_point()
Encoding
Una volta stabiliti i dati e come rappresentarli, vogliamo specificare dove rappresentarli. Cioè, impostare posizioni, dimensione, colore, ecc. Qui usiamo gli encoding. Quindi avremo:
alt.Chart(DataFrame).mark_point().encode()
Mettendo tutto insieme e aggiungendo i dettagli per creare la nostra prima visualizzazione:
alt.Chart(movies_2000).mark_point().encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross')
)
<vega.vegalite.VegaLite at 0x11709a1d0>

Non è fantastico? Non abbiamo dovuto fare molto — ci siamo familiarizzati con un po' di terminologia e abbiamo messo tutto insieme. Ora abbiamo un grafico in cui vediamo l'importo speso vs. l'incasso dei film dell'anno 2000 presenti nel nostro dataset.
Rendiamo questo grafico più interessante. Invece che tutti i punti abbiano la stessa dimensione, proviamo a renderli grandi (o piccoli) quanto il relativo US_Gross. Per questo introduciamo la feature Size. Quindi, più grande è il cerchio, maggiori sono gli incassi al botteghino negli USA. Coloriamo anche i cerchi, non solo il contorno; per farlo, aggiungiamo un argomento alla proprietà mark_point().
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross')
)
<vega.vegalite.VegaLite at 0x117cce790>

Ora abbiamo un grafico che mostra i budget di ciascun film in appena quattro righe. Cosa manca? IL COLORE!
Ma non aggiungeremo un colore a caso tanto per farlo! Terremo il grafico interessante introducendo un'altra informazione presente nel dataset, Major_Genre. Daremo a ogni genere il suo colore usando la feature Color e aggiungeremo un po' di trasparenza con OpacityValue.
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7)
)
<vega.vegalite.VegaLite at 0x118e3ff50>

Già buono, ma non abbastanza! Sarebbe fantastico poter muoverci nel grafico e vedere anche i dettagli del film rappresentato da ciascun punto. Tooltip ci permette di fare proprio questo...
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title'),
alt.Tooltip('Production_Budget'),
alt.Tooltip('Worldwide_Gross'),
alt.Tooltip('US_Gross')
]
)
<vega.vegalite.VegaLite at 0x118e3ff90>

Forte, vero? Ma aspetta, facciamo di più. Ricordi perché volevamo usare Altair? Per poter creare visualizzazioni interattive. Il tooltip introduce già un po' di interazione quando passi il mouse sui punti. Tuttavia, aggiungiamo una semplice funzionalità interactive() a quanto abbiamo già...
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title'),
alt.Tooltip('Production_Budget'),
alt.Tooltip('Worldwide_Gross'),
alt.Tooltip('US_Gross')
]
).interactive()
<vega.vegalite.VegaLite at 0x118e3c750>

Aggiungere quel semplice pezzo di codice ti permette già di fare zoom su un'area specifica — fissando automaticamente i valori degli assi così puoi concentrarti sulla zona di interesse. Ma ci sono molte altre funzionalità interattive avanzate che puoi aggiungere al grafico. Vediamone una...
Abbiamo finalmente un grafico carino per mostrare i nostri dati. Inseriamo tutti i punti e mettiamoli nel grafico. Ma aspetta, non pensi che sia troppo mettere tutti i punti in un'unica visualizzazione? Inoltre, vogliamo sicuramente separarli in base al valore Year che abbiamo creato prima. Per farlo useremo una proprietà avanzata di selection.
select_year = alt.selection_single(
name='Select', fields=['Year'], init={'Year': 1928},
bind=alt.binding_range(min=1928, max=2046, step=10)
)
alt.Chart(movies_df).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title:N'),
alt.Tooltip('Production_Budget:Q'),
alt.Tooltip('Worldwide_Gross:Q'),
alt.Tooltip('US_Gross:Q')
]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x117cd4cd0>

Quindi, usiamo la proprietà add_selection per introdurre select_year come selezione singola nel grafico e poi applichiamo transform_filter() per applicare effettivamente la trasformazione al grafico.
Hai notato un'altra novità nel codice? Le lettere "Q" e "N" inserite nel Tooltip. Servono a specificare il tipo di dato: N-Nominal (dati categoriali), Q-Quantitative (dati numerici), O-Ordinal (dati ordinati) o T-Temporal (punti o intervalli temporali).
Controlla l'anno più vecchio e il più recente nel dataset.
print("The oldest year is: ", movies_df["Year"].min())
print("The latest year is: ", movies_df["Year"].max())
The oldest year is: 1928
The latest year is: 2046
Come puoi vedere, trascinando la barra 'Select_Year', tra il 1928 e il 1967 non ci sono molti film rappresentati. E non ci sono dati sui film dopo il 2018 e certamente non fino al 2046. Possiamo usare la proprietà binding_range() per specificare l'intervallo degli anni e anche il passo degli anni.
In un progetto reale, risultati del genere sono importanti e possono indicare errori nella fase di raccolta dati o da qualche parte durante il processo. Dovresti gestire gli errori in modo efficace. Questo è un passaggio importante durante il preprocessing dei dati. Dai un'occhiata a Data Preparation with pandas per un'introduzione a questi passaggi fondamentali.
Regoliamo gli anni e, con questo, abbiamo pronta la nostra visualizzazione finale...
select_year = alt.selection_single(
name='Select', fields=['Year'], init={'Year': 1968},
bind=alt.binding_range(min=1968, max=2008, step=10)
)
alt.Chart(movies_df).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title:N'),
alt.Tooltip('Production_Budget:Q'),
alt.Tooltip('Worldwide_Gross:Q'),
alt.Tooltip('US_Gross:Q')
]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x1180cda10>

In questo tutorial hai conosciuto Altair. Hai visto come, con poche righe di codice, puoi creare grafici belli e interattivi che ti aiutano a raccontare la storia nei tuoi dati in modo più efficace. Questo tutorial vuole mostrare la potenza e il potenziale di Altair. Per saperne di più su Altair, visita il loro sito ufficiale.


