Il grafico a linee ha troppe linee, esiste una soluzione migliore?


31

Sto cercando di rappresentare graficamente il numero di azioni degli utenti (in questo caso "Mi piace") nel tempo.

Quindi ho "Numero di azioni" come il mio asse y, il mio asse x è il tempo (settimane) e ogni riga rappresenta un utente.

Il mio problema è che voglio esaminare questi dati per un set di circa 100 utenti. Un grafico a linee diventa rapidamente un pasticcio confuso con 100 linee. Esiste un tipo migliore di grafico che posso usare per visualizzare queste informazioni? O dovrei essere in grado di attivare / disattivare le singole linee?

Mi piacerebbe vedere tutti i dati in una volta, ma essere in grado di discernere il numero di azioni con alta precisione non è estremamente importante.

Perché lo sto facendo

Per un sottoinsieme dei miei utenti (utenti principali), voglio scoprire quali potrebbero non essere piaciuti a una nuova versione dell'applicazione che è stata lanciata in una determinata data. Sto cercando cali significativi nel numero di azioni da parte dei singoli utenti.


5
Hai pensato di rendere le linee semi-trasparenti modificando l'alfa che viene utilizzata per tracciarle?
Fomite,

1
@EpiGrad Suggerimento ragionevole ma questo non renderebbe più facile vedere cosa sto cercando.
regulatethis il

1
@regulatetis Suggerirei un approccio "piccolo multiplo" usando la facet_wrapfunzione di ggplot2 per creare un blocco di 4 x 5 grafici (4 righe, 5 colonne - regolare in base alle proporzioni desiderate) con ~ 5 utenti per grafico. Dovrebbe essere abbastanza chiaro e puoi ridimensionarlo fino a circa 10 utenti per grafico, dando spazio a 200 con un diagramma 4x5 o 360 con un diagramma 6x6.
SlowLearner,

Risposte:


31

Vorrei suggerire un'analisi preliminare (standard) per rimuovere i principali effetti di (a) variazione tra utenti, (b) la risposta tipica tra tutti gli utenti alla modifica e (c) variazione tipica da un periodo di tempo all'altro .

Un modo semplice (ma non il migliore) per farlo è quello di eseguire alcune iterazioni di "smalto mediano" sui dati per spazzare via le mediane degli utenti e le mediane del periodo di tempo, quindi lisciare i residui nel tempo. Identifica i smooth che cambiano molto: sono gli utenti che vuoi enfatizzare nella grafica.

Poiché si tratta di dati di conteggio, è una buona idea riesprimerli utilizzando una radice quadrata.

Come esempio di ciò che può derivare, ecco un set di dati simulato di 60 settimane di 240 utenti che in genere eseguono da 10 a 20 azioni alla settimana. Si è verificato un cambiamento in tutti gli utenti dopo la settimana 40. Tre di questi sono stati "informati" di rispondere negativamente al cambiamento. La trama a sinistra mostra i dati grezzi: conteggi dell'azione per utente (con utenti distinti per colore) nel tempo. Come affermato nella domanda, è un casino. La trama giusta mostra i risultati di questo EDA - negli stessi colori di prima - con gli utenti insolitamente sensibili automaticamente identificati ed evidenziati. L'identificazione, sebbene sia in qualche modo ad hoc, è completa e corretta (in questo esempio).

Figura 1

Ecco il Rcodice che ha prodotto questi dati ed effettuato l'analisi. Potrebbe essere migliorato in diversi modi, tra cui

  • Utilizzando uno smalto mediano completo per trovare i residui, piuttosto che una sola iterazione.

  • Levigare i residui separatamente prima e dopo il punto di cambio.

  • Forse usando un algoritmo di rilevamento anomalo più sofisticato. Quello attuale contrassegna semplicemente tutti gli utenti la cui gamma di residui è più del doppio dell'intervallo mediano. Anche se semplice, è robusto e sembra funzionare bene. (Un valore impostabile dall'utente thresholdpuò essere regolato per rendere questa identificazione più o meno rigorosa.)

I test suggeriscono tuttavia che questa soluzione funziona bene per una vasta gamma di conteggi degli utenti, 12 - 240 o più.

n.users <- 240        # Number of users (here limited to 657, the number of colors)
n.periods <- 60       # Number of time periods
i.break <- 40         # Period after which change occurs
n.outliers <- 3       # Number of greatly changed users
window <- 1/5         # Temporal smoothing window, fraction of total period
response.all <- 1.1   # Overall response to the change
threshold <- 2        # Outlier detection threshold

# Create a simulated dataset
set.seed(17)
base <- exp(rnorm(n.users, log(10), 1/2))
response <- c(rbeta(n.users - n.outliers, 9, 1),
              rbeta(n.outliers, 5, 45)) * response.all
actual <- cbind(base %o% rep(1, i.break), 
                base * response %o% rep(response.all, n.periods-i.break))
observed <- matrix(rpois(n.users * n.periods, actual), nrow=n.users)

# ---------------------------- The analysis begins here ----------------------------#
# Plot the raw data as lines
set.seed(17)
colors = sample(colors(), n.users) # (Use a different method when n.users > 657)
par(mfrow=c(1,2))
plot(c(1,n.periods), c(min(observed), max(observed)), type="n",
     xlab="Time period", ylab="Number of actions", main="Raw data")
i <- 0
apply(observed, 1, function(a) {i <<- i+1; lines(a, col=colors[i])})
abline(v = i.break, col="Gray")  # Mark the last period before a change

# Analyze the data by time period and user by sweeping out medians and smoothing
x <- sqrt(observed + 1/6)                        # Re-express the counts
mean.per.period <- apply(x, 2, median)
residuals <- sweep(x, 2, mean.per.period)
mean.per.user <- apply(residuals, 1, median)
residuals <- sweep(residuals, 1, mean.per.user)

smooth <- apply(residuals, 1, lowess, f=window)  # Smooth the residuals
smooth.y <- sapply(smooth, function(s) s$y)      # Extract the smoothed values
ends <- ceiling(window * n.periods / 4)          # Prepare to drop near-end values
range <- apply(smooth.y[-(1:ends), ], 2, function(x) max(x) - min(x))

# Mark the apparent outlying users
thick <- rep(1, n.users)
thick[outliers <- which(range >= threshold * median(range))] <- 3
type <- ifelse(thick==1, 3, 1)

cat(outliers) # Print the outlier identifiers (ideally, the last `n.outliers`)

# Plot the residuals
plot(c(1,n.periods), c(min(smooth.y), max(smooth.y)), type="n",
     xlab="Time period", ylab="Smoothed residual root", main="Residuals")
i <- 0
tmp <- lapply(smooth, 
       function(a) {i <<- i+1; lines(a, lwd=thick[i], lty=type[i], col=colors[i])})
abline(v = i.break, col="Gray")

3
Per più di 100-200 utenti, aumenterei thresholda circa per evitare falsi positivi. Ad esempio , provare il codice con , (che è una grande percentuale!), E . 2.5n.users <- 500n.outliers <- 100threshold <- 2.5
whuber

16

Generalmente trovo più di due o tre righe su una singola sfaccettatura di una trama che inizia a essere difficile da leggere (anche se lo faccio ancora tutto il tempo). Quindi questo è un esempio interessante di cosa fare quando hai qualcosa che concettualmente potrebbe essere una trama a 100 facce. Un modo possibile è quello di disegnare tutte e 100 le sfaccettature ma invece di cercare di metterle tutte sulla pagina contemporaneamente, guardandole una alla volta in un'animazione.

Abbiamo effettivamente utilizzato questa tecnica nel mio lavoro: inizialmente abbiamo realizzato l'animazione che mostrava 60 diversi grafici di trama come sfondo per un evento (il lancio di una nuova serie di dati), quindi abbiamo scoperto che così facendo abbiamo effettivamente raccolto alcune caratteristiche dei dati che non era stato visibile nelle trame sfaccettate con 15 o 30 sfaccettature per pagina.

Quindi ecco un modo alternativo di presentare i dati grezzi, prima di iniziare a rimuovere l'utente e gli effetti temporali tipici come raccomandato da @whuber. Questo è presentato solo come un'alternativa aggiuntiva alla sua presentazione dei dati grezzi: consiglio vivamente di procedere con l'analisi secondo linee come quelle che suggerisce.

Un modo per aggirare questo problema è produrre separatamente le trame di 100 (o 240 nell'esempio di @buber) e unirle insieme in un'animazione. Il codice qui sotto produrrà 240 immagini separate di questo tipo e quindi puoi usare un software gratuito per la creazione di filmati per trasformarlo in un film. Sfortunatamente, l'unico modo per farlo e mantenere una qualità accettabile era un file da 9 MB, ma se non è necessario inviarlo su Internet potrebbe non essere un problema e comunque sono sicuro che ci sono modi per aggirare questo con un po 'di più esperto di animazione. Il pacchetto di animazione in R potrebbe essere utile qui (ti consente di fare tutto in una chiamata da R) ma l'ho tenuto semplice per questa illustrazione.

Ho realizzato l'animazione in modo tale che disegna ogni linea in nero pesante, quindi lascia dietro di sé un'ombra verde semi-trasparente pallida in modo che l'occhio ottenga un'immagine graduale dei dati accumulati. In questo ci sono sia rischi che opportunità: l'ordine in cui le linee vengono aggiunte lascerà un'impressione diversa, quindi dovresti considerare di renderlo significativo in qualche modo.

Ecco alcuni degli alambicchi del film, che utilizza gli stessi dati generati da @whuber: inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

# ---------------------------- Data generation - by @whuber ----------------------------#

n.users <- 240        # Number of users (here limited to 657, the number of colors)
n.periods <- 60       # Number of time periods
i.break <- 40         # Period after which change occurs
n.outliers <- 3       # Number of greatly changed users
window <- 1/5         # Temporal smoothing window, fraction of total period
response.all <- 1.1   # Overall response to the change
threshold <- 2        # Outlier detection threshold

# Create a simulated dataset
set.seed(17)
base <- exp(rnorm(n.users, log(10), 1/2))
response <- c(rbeta(n.users - n.outliers, 9, 1),
              rbeta(n.outliers, 5, 45)) * response.all
actual <- cbind(base %o% rep(1, i.break), 
                base * response %o% rep(response.all, n.periods-i.break))
observed <- matrix(rpois(n.users * n.periods, actual), nrow=n.users)

# ---------------------------- The analysis begins here ----------------------------#

# Alternative presentation of original data 
# 
setwd("eg animation")

for (i in 1:n.users){
    png(paste("line plot", i, ".png"),600,600,res=60)
    plot(c(1,n.periods), c(min(observed), max(observed)), 
        xlab="Time period", ylab="Number of actions", 
        main="Raw data", bty="l", type="n")
    if(i>1){apply(observed[1:i,], 1, function(a) {lines(a, col=rgb(0,100,0,50,maxColorValue=255))})}
    lines(observed[i,], col="black", lwd=2)
    abline(v = i.break, col="Gray")  # Mark the last period before a change
    text(1,60,i)
    dev.off()
}

##
# Then proceed to further analysis eg as set out by @whuber

+1, questa è una bella idea. Puoi anche avviare una nuova finestra del dispositivo usando windows()o quartz(), quindi annidare il tuo for()loop al suo interno. NB, dovrai mettere a Sys.sleep(1)nella parte inferiore del ciclo in modo da poter effettivamente vedere le iterazioni. Naturalmente, questa strategia in realtà non salva un file di film: devi solo eseguirlo nuovamente ogni volta che vuoi guardarlo di nuovo.
gung - Ripristina Monica

+1 Un'idea molto bella: proverò questa la prossima occasione che avrò. (GTW, Mathematica , per esempio, fa poco lavoro per creare e salvare tali animazioni.)
whuber

Un'idea incredibile: un'animazione lungo queste linee (o il codice e i dati da generare) renderebbe un'appendice online molto sexy a una pubblicazione.
N Brouwer,

7

Una delle cose più semplici da fare è un diagramma a scatole. Puoi immediatamente vedere come si muovono le tue mediane campione e quali giorni hanno il maggior numero di valori anomali.

day <- rep(1:10, 100)
likes <- rpois(1000, 10)
d <- data.frame(day, likes)
library(ggplot2)
qplot(x=day, y=likes, data=d, geom="boxplot", group=day)

inserisci qui la descrizione dell'immagine

Per l'analisi individuale, suggerisco di prelevare un piccolo campione casuale dai dati e di analizzare serie storiche separate.


1
Soluzione interessante, ma quello che voglio davvero essere in grado di vedere come è il "cambiamento" su una base per utente. Voglio vedere le fluttuazioni dell'attività dei singoli utenti. Ecco perché inizialmente ho scelto una linea, ma ora la visualizzazione è troppo ingombra.
regulatethis il

beh, dipende davvero da quali schemi vuoi vedere nei tuoi dati, forse se potessi dirci cosa stai cercando di scoprire, potremmo trovare una soluzione.
jem77bfp,

Per un sottoinsieme dei miei utenti (utenti principali), voglio scoprire quali potrebbero non essere piaciuti a una nuova versione dell'applicazione che è stata lanciata in una determinata data. Sto cercando cali significativi nel numero di azioni da parte dei singoli utenti.
regulatethis il

Benvenuti nel sito @ jem77bfp. ha detto che voleva vedere tutti i dati. Ma sarebbe bello avere maggiori dettagli, sono d'accordo.
Peter Flom - Ripristina Monica

+1 - invece di visualizzare i grafici a riquadri sebbene possa essere utile collegare le statistiche di riepilogo in grafici a linee. Vedi questa mia risposta per un esempio e una discussione di seguito.
Andy W,

7

Sicuro. Innanzitutto, ordina per numero medio di azioni. Quindi crea (diciamo) 4 grafici, ciascuno con 25 righe, uno per ogni quartile. Ciò significa che è possibile ridurre gli assi y (ma rendere chiara l'etichetta dell'asse y). E con 25 linee, puoi modificarle in base al tipo e al colore della linea e magari tracciare un simbolo e ottenere un po 'di chiarezza

Quindi impilare i grafici verticalmente con un singolo asse temporale.

Questo sarebbe abbastanza facile in R o SAS (almeno se hai la v. 9 di SAS).


2
+1 - Suggerirei ancora meno linee per piccolo multiplo! Vedi il mio post sul blog correlato sull'argomento e un esempio. Anche l'ordinamento è un'ottima idea e altri potenziali potrebbero includere valore alla base o follow-up o misure di cambiamento (come pendenza positiva o negativa, variazione percentuale, ecc.).
Andy W,

Bello! Cos'è il blog della community? Come si accede o si scrive per esso?
Peter Flom - Ripristina Monica

3
sentiti libero di fermarti nella chat room di Skewed Distribution per i dettagli su come entrare nel blog. Siamo sempre aperti per ulteriori contributi da parte dei membri della comunità.
Andy W,

0

Trovo che quando esaurisci le opzioni relative al tipo se il grafico e le impostazioni del grafico l'introduzione del tempo attraverso l'animazione è il modo migliore per visualizzare perché ti dà una dimensione extra con cui lavorare e ti consente di visualizzare più informazioni in un modo facile da seguire . Il tuo obiettivo principale deve essere l'esperienza dell'utente finale.


Avevi in ​​mente qualcosa che differisce dalla soluzione che Peter Ellis ha pubblicato qui ? Se è così, potresti approfondire per favore?
whuber

0

Se sei più interessato alla modifica per i singoli utenti, forse questa è una buona situazione per una raccolta di Sparkline (come questo esempio di The Pudding ):

Esempio di sparkline da pudding.cool

Sono piuttosto dettagliati, ma puoi mostrare molti più grafici contemporaneamente rimuovendo le etichette e le unità degli assi.

Molti strumenti per i dati li hanno integrati ( Microsoft Excel ha sparkline ), ma suppongo che vorrai inserire un pacchetto per crearli in R.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.