L’Assistente di Visualizzazione Definitivo

Ultimate Display Assistant

Come una serata con l’IA ha trasformato il mio approccio alla visualizzazione dei dati

Foto di Simon Abrams su Unsplash

Mentre il sole iniziava a scendere e le luci della città si accendevano, inevitabilmente mi aspettavo una lunga notte in ufficio. Mi trovavo in una corsa contro il tempo. Una presentazione vendite cruciale era imminente a meno di un giorno di distanza, e il successo dipendeva da una richiesta non soddisfatta: una visualizzazione dei dati che potesse trasmettere in modo chiaro la metodologia sottostante del nostro nuovo modello di attribuzione TV.

Avevo bisogno di qualcosa di abbastanza complesso da mostrare, ma sufficientemente semplice da trasmettere la maggior parte delle informazioni senza spiegazioni. Non un compito facile con un orologio che ticchettava. Fortunatamente, un faro di intelligenza artificiale, il mio fedele nuovo assistente – ChatGPT – era pronto ad semplificare il compito a portata di mano. La sua missione principale: aiutarmi a tradurre dati che erano poco più di un concetto nella mia testa in una narrazione visiva eloquente, capace di affascinare anche le menti più avverse ai dati nella stanza.

Questo potrebbe sembrare un po’ troppo drammatico, ma ero in una situazione difficile. Per una presentazione commerciale, avevo bisogno di un esempio eccessivamente semplice che non potevo trovare facilmente nel nostro database, quindi ho utilizzato dati di esempio inventati.

Sapevo esattamente cosa volevo mostrare: una linea di base del traffico web, un picco di risposte web a seguito di una pubblicità televisiva e un modo per comunicare con poche o nessuna parola che in questo esempio avremmo attribuito la differenza tra la linea di base e il picco.

I grafici di Matplotlib sono validi in un articolo accademico, ma possono richiedere una mano abile per produrre qualcosa di complesso e davvero accattivante. Spesso mi rivolgo a Power BI per le visualizzazioni predefinite e i grafici veloci, ma non è ideale quando è necessario creare qualcosa da zero senza dati preesistenti.

Ero già da un po’ che lavoravo con Plotly, che sembrava essere una buona scelta. Tuttavia, non ero abbastanza sicuro del pacchetto da tradurre i miei pensieri in codice senza perdere preziosi minuti a cercare nella documentazione e su Stack Overflow. Quindi, con poco tempo a disposizione, ho iniziato la mia prima richiesta.

Sto creando una visualizzazione per mostrare la risposta del traffico web dopo la messa in onda di uno spot televisivo. Iniziamo rappresentando il traffico web come un conteggio per minuto delle visite al sito nel corso di 30 minuti. I primi dieci minuti devono essere un numero casuale compreso tra 0 e 4. Il secondo periodo di dieci minuti deve essere costituito da numeri casuali compresi tra 4 e 20, con i numeri distribuiti in modo simile a un istogramma asimmetrico destro. Il terzo periodo di dieci minuti è uguale al primo periodo di dieci minuti. Si prega di creare questi dati e di rappresentarli con un grafico a linea in Plotly.

La richiesta ha restituito il seguente codice, con le mie uniche modifiche nella funzione update_layout:

import numpy as npimport plotly.graph_objects as go# Imposta il seed per la riproducibilitànnp.random.seed(42)# Crea array con le distribuzioni specificatefirst_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # sposta la distribuzione gamma# Combina gli arraymeasurements = np.concatenate((first_ten, middle_ten, last_ten))# Crea i valori x x_values = np.arange(1, 31)# Crea il grafico a lineefig = go.Figure(    data=go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers')    )fig.update_layout(    title='Influenza della Pubblicità sul Traffico Web',     xaxis_title='Tempo',     yaxis_title='Visite al Minuto',    width=1000,    height=600)fig.show()
Grafico generato dal blocco di codice precedente

Ero già impressionato perché non mi aspettavo che la richiesta di un istogramma asimmetrico destro fosse gestita così bene. Forse sarei riuscito a finire prima che finisse la sessione di lavoro postorario nel palazzo vicino.

Successivamente, avevo bisogno di qualcosa che rappresentasse la linea di base del traffico web. In realtà, potrebbe essere semplicemente una mediana mobile, ma chiedere questo qui non funzionerebbe a causa della limitatezza dei dati. Invece, pensavo che la mediana dei primi e degli ultimi dieci osservazioni avrebbe probabilmente trasmesso l’idea giusta.

Calcola innanzitutto la mediana combinata dei due array first_ten e last_ten. Traccia la mediana calcolata come una linea orizzontale costante.

import numpy as npimport plotly.graph_objects as go# Imposta il seed per la riproducibilitànp.random.seed(42)# Crea gli array con le distribuzioni specificatefirst_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # sposta la distribuzione gamma# Combina gli arraymeasurements = np.concatenate((first_ten, middle_ten, last_ten))# Crea i valori x x_values = np.arange(1, 31)# Calcola la mediana combinatacombined_median = np.median(np.concatenate((first_ten, last_ten)))# Crea il grafico a lineefig = go.Figure()# Crea il grafico a lineefig = go.Figure(    data=go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers')    )# Aggiungi la linea della medianafig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='Baseline',         line=dict(color='firebrick', width=2, dash='dash')))fig.update_layout(    title='Influenza della pubblicità sul traffico web',    xaxis_title='Tempo',    yaxis_title='Visite al minuto',    width=1000,    height=600)fig.show()

Ho quindi semplicemente cambiato il nome della traccia della linea mediana da “Mediana” a “Baseline”.

Grafico dal blocco di codice sopra, che aggiunge la linea costante di base

Ora l’idea di “lift” emerge se si è familiari con i modelli di marketing, tuttavia, per un occhio non allenato, ciò richiede alcuni elementi più focalizzati; meno c’è da spiegare, più efficace è un’immagine visiva. Le principali cose mancanti in questo punto erano un elemento del grafico che implicasse la causalità dell’impennata nel traffico web, e un modo per trasmettere il volume di traffico attribuito alla pubblicità.

Ho iniziato con l’elemento di causalità.

Al minuto 10 dei dati, viene trasmesso uno spot televisivo che provoca un picco nel traffico web. Che tipo di elemento di grafico suggeriresti per trasmettere non solo che la pubblicità è stata trasmessa in quel minuto, ma che la pubblicità è il fattore causale dietro l’impennata?

La risposta suggeriva una linea verticale e un’annotazione e forniva l’aggiornamento del codice.

import numpy as npimport plotly.graph_objects as go# Imposta il seed per la riproducibilitànp.random.seed(42)# Crea gli array con le distribuzioni specificatefirst_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # sposta la distribuzione gamma# Combina gli arraymeasurements = np.concatenate((first_ten, middle_ten, last_ten))# Crea i valori x x_values = np.arange(1, 31)# Calcola la mediana combinatacombined_median = np.median(np.concatenate((first_ten, last_ten)))# Crea il grafico a lineefig = go.Figure()# Aggiungi il grafico a dispersionefig.add_trace(    go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers',         name='Traffico Web'))# Aggiungi la linea della medianafig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='Baseline',         line=dict(color='firebrick', width=2, dash='dash')))# Aggiungi la linea verticale al minuto 10fig.add_shape(    type='line',    x0=10, y0=0,    x1=10, y1=max(measurements),    line=dict(        color="Purple",        width=3,        dash="dot",    ))# Aggiungi l'annotazione per indicare lo spot televisivofig.add_annotation(    x=10,    y=max(measurements),    text="Spot TV trasmesso",    showarrow=True,    arrowhead=1,    ax=-100,    ay=-25,    font=dict(        size=16,        color="Black"    ),)fig.update_layout(    title='Influenza della pubblicità sul traffico web',    xaxis_title='Tempo',    yaxis_title='Visite al minuto',    width=1000,    height=600)fig.show()

Ho cambiato solo la dimensione del carattere dell’annotazione.

Grafico dal blocco di codice sopra, che aggiunge la linea costante e l'annotazione di trasmissione della pubblicità in TV

Ora avevamo una causa chiaramente definita e un punto di occorrenza per il picco. Il grafico aveva ancora bisogno di qualcosa per indicare il volume di traffico attribuito. Sapevo già cosa volevo per questo – ombreggiare l’area tra la linea di base e il traffico web per la durata della “finestra di incremento” (dieci minuti). Sono tornato al mio collega di intelligenza artificiale per implementare ciò.

Voglio visualizzare il volume di traffico web sopra la linea di base attribuito alla pubblicità. Aggiungi un’ombreggiatura verde tra la linea di base e la linea del traffico web per minuto. Ombreggia solo l’area compresa tra il decimo e il ventesimo minuto dei dati. Aggiungi anche una voce di legenda per l’area ombreggiata.

import numpy as npimport plotly.graph_objects as go# Imposta il seed per la riproducibilitànnp.random.seed(42)# Crea array con le distribuzioni specificatefirst_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # sposta la distribuzione gamma# Combina gli arraymeasurements = np.concatenate((first_ten, middle_ten, last_ten))# Crea i valori xx_values = np.arange(1, 31)# Calcola la mediana combinatacombined_median = np.median(np.concatenate((first_ten, last_ten)))# Crea il grafico a lineefig = go.Figure()# Aggiungi il grafico a dispersionefig.add_trace(    go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers',         name='Traffico Web'))# Aggiungi la linea della medianafig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='Linea di Base',         line=dict(color='firebrick', width=2, dash='dash')))# Aggiungi la regione ombreggiata per l'influenza della pubblicitàfig.add_trace(    go.Scatter(        x=list(x_values[9:20]) + list(x_values[9:20])[::-1],         y=list([combined_median]*11) + list(measurements[9:20])[::-1],         fill='toself',         fillcolor='rgba(0,200,0,0.2)',         line=dict(width=0),         hoverinfo="skip",         showlegend=True,         name='Impatto della Pubblicità'))# Aggiungi la linea verticale al minuto 10fig.add_shape(    type='line',    x0=10, y0=0,    x1=10, y1=max(measurements),    line=dict(        color="Purple",        width=3,        dash="dot",    ))# Aggiungi l'annotazione per indicare lo spot TVfig.add_annotation(    x=10,    y=max(measurements),    text="Pubblicità in TV trasmessa",    showarrow=True,    arrowhead=1,    ax=-100,    ay=-25,    font=dict(        size=16,        color="Black"    ),)fig.update_layout(    title='Influenza della Pubblicità sul Traffico Web',    xaxis_title='Tempo',    yaxis_title='Visite al Minuto',    width=1000,    height=600)fig.show()
Grafico dal blocco di codice sopra, che aggiunge l'ombreggiatura per rappresentare l'impatto della pubblicità

Sentivo che stavamo arrivando ad un buon punto, ma non ero ancora soddisfatto della “chiarezza” di ciò che stavo cercando di comunicare. Ho apportato alcune modifiche basate sul codice che mi era già stato dato.

Ho aggiunto una linea verticale costante e un’annotazione per indicare la fine della finestra di incremento, e ho cambiato i colori delle linee della pubblicità trasmessa e della fine della finestra in verde e rosso, rispettivamente. Ho anche cambiato la linea costante della linea di base in viola e l’ho stilizzata a tratteggio per abbinarla meglio alle altre linee costanti.

Poi volevo un ultimo elemento – l’ombreggiatura per la regione di base. Volendo sfruttare la perfomance impeccabile finora, ho dato questo compito al mio assistente.

Si prega di aggiungere una sfumatura viola all’intera regione di linea di base. Aggiungere un’annotazione che dice “Baseline” al minuto 15, sotto la linea orizzontale costante di linea di base.

Si noti qui che non ho citato alcuna coordinata o numero specifico per la posizione della sfumatura.

import numpy as npimport plotly.graph_objects as go# Imposta il seme per la riproducibilitànp.random.seed(42)# Crea array con le distribuzioni specificateprime_dieci = np.random.uniform(low=0, high=4, size=10)ultime_dieci = np.random.uniform(low=0, high=4, size=10)dieci_intermedi = np.random.gamma(shape=2, scale=2, size=10) + 4 # sposta la distribuzione gamma# Combina gli arraymisure = np.concatenate((prime_dieci, dieci_intermedi, ultime_dieci))# Crea i valori di xx_values = np.arange(1, 31)# Calcola la mediana combinatamediana_combinata = np.median(np.concatenate((prime_dieci, ultime_dieci)))# Crea il grafico a lineefig = go.Figure()# Aggiungi la regione di linea di basefig.add_trace(    go.Scatter(        x=list(x_values) + list(x_values)[::-1],         y=[mediana_combinata]*len(x_values) + [0]*len(x_values),         fill='toself',         fillcolor='rgba(128, 0, 128, 0.2)',         line=dict(width=0),         hoverinfo="skip",         showlegend=True,         name='Linea di base'))# Aggiungi il grafico a dispersionefig.add_trace(    go.Scatter(        x=x_values,         y=misure,         mode='lines+markers',         name='Traffico web',        line=dict(color='#636EFA')))# Aggiungi la linea medianafig.add_trace(    go.Scatter(        x=x_values,         y=[mediana_combinata]*len(x_values),         mode='lines',         name='Mediana',         line=dict(color='Viola', width=2, dash='dash')))# Aggiungi la regione sfumata per l'influenza dell'annunciofig.add_trace(    go.Scatter(        x=list(x_values[9:20]) + list(x_values[9:20])[::-1],         y=list([mediana_combinata]*11) + list(misure[9:20])[::-1],         fill='toself',         fillcolor='rgba(0,200,0,0.2)',         line=dict(width=0),         hoverinfo="skip",         showlegend=True,         name='Impatto dell\'annuncio'))# Aggiungi la linea verticale al minuto 10fig.add_shape(    type='line',    x0=10, y0=0,    x1=10, y1=max(misure),    line=dict(        color="Verde",        width=3,        dash="dot",    ))# Aggiungi l'annotazione per indicare lo spot TVfig.add_annotation(    x=10,    y=max(misure),    text="Annuncio TV trasmesso",    showarrow=True,    arrowhead=1,    ax=-100,    ay=-25,    font=dict(        size=16,        color="Verde"    ),)# Aggiungi la linea verticale al minuto 20fig.add_shape(    type='line',    x0=20, y0=0,    x1=20, y1=max(misure),    line=dict(        color="Rosso",        width=3,        dash="dot",    ))# Aggiungi l'annotazione per indicare la fine della finestra di incrementofig.add_annotation(    x=20,    y=max(misure),    text="Fine finestra di incremento",    showarrow=True,    arrowhead=1,    ax=100,    ay=-25,    font=dict(        size=16,        color="Rosso"    ),)# Aggiungi l'annotazione per l'incrementofig.add_annotation(    x=15,    y=mediana_combinata + (max(misure[9:20])-mediana_combinata)/4,    text="Incremento dovuto all'annuncio",    showarrow=False,    font=dict(        size=18,        color="Verde"    ),)# Aggiungi l'annotazione per la linea di basefig.add_annotation(    x=15,    y=mediana_combinata/2,    text="Linea di base",    showarrow=False,    font=dict(        size=18,        color="Viola"    ),)fig.update_layout(    title='Influenza dell\'annuncio sul traffico web',    xaxis_title='Tempo',    yaxis_title='Visite al minuto',    width=1000,    height=600)fig.show()
Grafico dal blocco di codice sopra, che ha aggiunto una sfumatura di base, una linea di taglio e altre annotazioni

La cattura della richiesta di sfumatura di base senza fornire valori specifici è di prim’ordine e, a questo punto, ero al di là dell’impressionante. Felice della visualizzazione, ho inserito il grafico nella presentazione, ho inviato un messaggio su Teams e, con uno sguardo affettuoso alla scheda ChatGPT, ho chiuso il mio laptop. Mentre stavo facendo le valigie, una notifica dalla chat di gruppo post-lavoro è apparsa sullo schermo del mio telefono.

Visto il messaggio della presentazione, pensavo avessi detto che saresti arrivato tardi? Ti aspetteremo con una pinta.

Cin cin, ChatGPT.