2012-03-08 11 views
36

Voglio creare un grafico come quello qui sotto:creare "grafico radar" (rappresentazione della stella alias; trama ragno) utilizzando ggplot2 in R

enter image description here

So che posso utilizzare la funzione di radarchart da fmsb pacchetto. Mi chiedo se lo ggplot2 può farlo, usando la coordinata polare? Grazie.

+6

dato che ggplot2 mi dà un controllo migliore sul titolo della trama, etichette in scala xy, e anche facendo sfaccettatura, ho bisogno di fare i tratteggi radar 30+ e voglio mostrarli in 1 pagina, e questo mi aiuta a capire meglio come funziona ggplot2 – lokheart

+3

Puoi fare tutto ciò con la grafica di base. par (mfrow = c (5,6)) e ci sono i 30 (piccoli minuscoli) grafici su una pagina. Cosa c'è di sbagliato con "title (" Hello ")" per i titoli di trama? A volte il tempo trascorso a capire ggplot2 è meglio speso per andare avanti con esso con la grafica di base .... – Spacedman

+21

Penso che sia una domanda legittima a voler fare questo in ggplot2 –

risposta

9

Per prima cosa, cariciamo alcuni pacchetti.

library(reshape2) 
library(ggplot2) 
library(scales) 

Ecco i dati dell'esempio di radarchart a cui è stato collegato.

maxmin <- data.frame(
    total = c(5, 1), 
    phys = c(15, 3), 
    psycho = c(3, 0), 
    social = c(5, 1), 
    env = c(5, 1) 
) 
dat <- data.frame(
    total = runif(3, 1, 5), 
    phys = rnorm(3, 10, 2), 
    psycho = c(0.5, NA, 3), 
    social = runif(3, 1, 5), 
    env = c(5, 2.5, 4) 
) 

Abbiamo bisogno di una piccola manipolazione per renderli adatti per ggplot.

Normalizza, aggiungi una colonna id e converti in formato lungo.

normalised_dat <- as.data.frame(mapply(
    function(x, mm) 
    { 
     (x - mm[2])/(mm[1] - mm[2]) 
    }, 
    dat, 
    maxmin 
)) 

normalised_dat$id <- factor(seq_len(nrow(normalised_dat))) 
long_dat <- melt(normalised_dat, id.vars = "id") 

ggplot avvolge anche i valori in modo che i primi e gli ultimi fattori incontrano. Aggiungiamo un livello di fattore extra per evitare questo. Questo non è più vero.

livelli (long_dat $ variabile) < - c (livelli (long_dat $ variabile), "")

Ecco la trama. Non è esattamente la stessa cosa, ma dovrebbe iniziare.

ggplot(long_dat, aes(x = variable, y = value, colour = id, group = id)) + 
    geom_line() + 
    coord_polar(theta = "x", direction = -1) + 
    scale_y_continuous(labels = percent) 

enter image description here Nota che quando si utilizza coord_polar, le linee sono curve. Se vuoi linee rette, dovrai provare una tecnica diversa.

+0

Qualcuno è riuscito a rendere le linee diritte? (PS a Richie: Bella soluzione! Puoi commentare anche perché il mio primo e l'ultimo fattore non si incontrano, anche se non c'è nessun livello "" "lì?) –

+0

@Anton Sembra che il comportamento di' ggplot2' abbia cambiamenti da quando ho scritto questa risposta. (C'era una riscrittura piuttosto grande da qualche parte intorno alla v0.9.) Ho aggiornato il codice in modo che funzioni di nuovo. Non riesco a vedere un modo ovvio per raddrizzare le linee o per far coincidere i punti iniziale e finale. Ho pensato che 'geom_path' avrebbe fatto il secondo, ma non sembra funzionare. –

4

Se siete alla ricerca di una versione non coordinata polare, penso che la seguente funzione aiuterà:

################################### 
##Radar Plot Code 
########################################## 
##Assumes d is in the form: 
# seg meanAcc sdAcc meanAccz sdAccz meanSpd sdSpd cluster 
# 388 -0.038 1.438 -0.571 0.832 -0.825 0.095  1 
##where seg is the individual instance identifier 
##cluster is the cluster membership 
##and the variables from meanACC to sdSpd are used for the clustering 
##and thus should be individual lines on the radar plot 
radarFix = function(d){ 
    ##assuming the passed in data frame 
    ##includes only variables you would like plotted and segment label 
    d$seg=as.factor(d$seg) 
    ##find increment 
    angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(d)-2)) 
    ##create graph data frame 
    graphData= data.frame(seg="", x=0,y=0) 
    graphData=graphData[-1,] 



    for(i in levels(d$seg)){ 
    segData= subset(d, seg==i) 
    for(j in c(2:(ncol(d)-1))){ 
     ##set minimum value such that it occurs at 0. (center the data at -3 sd) 
     segData[,j]= segData[,j]+3 

     graphData=rbind(graphData, data.frame(seg=i, 
              x=segData[,j]*cos(angles[j-1]), 
              y=segData[,j]*sin(angles[j-1]))) 
    } 
    ##completes the connection 
    graphData=rbind(graphData, data.frame(seg=i, 
              x=segData[,2]*cos(angles[1]), 
              y=segData[,2]*sin(angles[1]))) 

    } 
    graphData 

} 

Se si stanno complottando per cluster o gruppo è possibile utilizzare il seguente:

radarData = ddply(clustData, .(cluster), radarFix) 
ggplot(radarData, aes(x=x, y=y, group=seg))+ 
    geom_path(alpha=0.5,colour="black")+ 
    geom_point(alpha=0.2, colour="blue")+ 
    facet_wrap(~cluster) 

Questo dovrebbe funzionare con il seguente campione di dati:

seg meanAccVs sdAccVs meanSpd sdSpd cluster 
    1470  1.420 0.433 -0.801 0.083  1 
    1967 -0.593 0.292 1.047 0.000  3 
    2167 -0.329 0.221 0.068 0.053  7 
    2292 -0.356 0.214 -0.588 0.056  4 
    2744  0.653 1.041 -1.039 0.108  5 
    3448  2.189 1.552 -0.339 0.057  8 
    7434  0.300 0.250 -1.009 0.088  5 
    7764  0.607 0.469 -0.035 0.078  2 
    7942  0.124 1.017 -0.940 0.138  5 
    9388  0.742 1.289 -0.477 0.301  5 

Radar plot

2

Ecco una risposta che fa quasi in ggplot.

faccio né pretendo qualcosa di più che mettere l'esempio qui, si basa su quello che Hadley mostrato qui https://github.com/hadley/ggplot2/issues/516

Tutto quello che ho fatto è stato l'uso deployer/tidyr invece e scegliere solo 3 vetture per semplicità

la i problemi ancora in sospeso sono 1) l'ultimo e il primo punto non sono collegati, questo è ovvio se si vede il coord_polar come un avvolgimento dell'asse x tradizionale. Non c'è motivo per cui dovrebbero essere collegati. Ma è così che normalmente vengono mostrati i grafici radar 2) per fare ciò è necessario aggiungere manualmente un segmento tra questi 2 punti.Una piccola manipolazione e alcuni altri strati dovrebbero farlo. Cercherò di lavorare su di essa, se ho un po 'di tempo

library(dplyr);library(tidyr);library(ggplot2) 
#make some data 
data = mtcars[c(27,19,16),] 
data$model=row.names(data) 

#connvert data to long format and also rescale it into 0-1 scales 
data1 <- data %>% gather(measure,value,-model) %>% group_by(measure) %>% mutate(value1=(value-min(value))/(max(value)-min(value))) 

is.linear.polar <- function(coord) TRUE 
ggplot(data1,aes(x=measure,y=value1,color=model,group=model))+geom_line()+coord_polar() 
2

Ho trascorso diversi giorni in questo problema e alla fine ho deciso di costruire my own package cima di ggradar. Il nucleo di esso è una versione migliorata della funzione di @Tony M.:

CalculateGroupPath4 <- function(df) { 
    angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(df)-1)) # find increment 
    xx<-c(rbind(t(plot.data.offset[,-1])*sin(angles[-ncol(df)]), 
       t(plot.data.offset[,2])*sin(angles[1]))) 
    yy<-c(rbind(t(plot.data.offset[,-1])*cos(angles[-ncol(df)]), 
       t(plot.data.offset[,2])*cos(angles[1]))) 
    graphData<-data.frame(group=rep(df[,1],each=ncol(df)),x=(xx),y=(yy)) 
    return(graphData) 
} 
CalculateGroupPath5 <- function(mydf) { 
    df<-cbind(mydf[,-1],mydf[,2]) 
    myvec<-c(t(df)) 
    angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(df)-1)) # find increment 
    xx<-myvec*sin(rep(c(angles[-ncol(df)],angles[1]),nrow(df))) 
    yy<-myvec*cos(rep(c(angles[-ncol(df)],angles[1]),nrow(df))) 
    graphData<-data.frame(group=rep(mydf[,1],each=ncol(mydf)),x=(xx),y=(yy)) 
    return(graphData) 
} 

microbenchmark::microbenchmark(CalculateGroupPath(plot.data.offset), 
           CalculateGroupPath4(plot.data.offset), 
           CalculateGroupPath5(plot.data.offset), times=1000L) 
Unit: microseconds 
expr  min   lq  mean  median   uq  max neval 
CalculateGroupPath(plot.data.offset) 20768.163 21636.8715 23125.1762 22394.1955 23946.5875 86926.97 1000 
CalculateGroupPath4(plot.data.offset) 550.148 614.7620 707.2645 650.2490 687.5815 15756.53 1000 
CalculateGroupPath5(plot.data.offset) 577.634 650.0435 738.7701 684.0945 726.9660 11228.58 1000 

noti che ho effettivamente rispetto more functions in this benchmark - tra le altre funzioni da ggradar. Generalmente la soluzione di @Tony M è ben scritta - nel senso della logica e che puoi usarla in molti altri linguaggi, come ad es. Javascript, con alcune modifiche. Tuttavia, R diventa molto più veloce se si vectorise le operazioni. Quindi il grande guadagno nel tempo di calcolo con la mia soluzione.

Tutte le risposte tranne @Tony M.'s hanno utilizzato la funzione coord_polar da ggplot2. Ci sono quattro vantaggi di rimanere all'interno del sistema di coordinate cartesiane:

  1. Consente di trasportare la soluzione anche ad altri pacchetti di tracciamento, ad es. plotly.
  2. Chiunque abbia una certa comprensione del coseno standard e della funzione del seno può comprendere come funziona la trasformazione dei dati.
  3. È possibile estendere e personalizzare la trama come si desidera: dannazione, è possibile utilizzarla con qualsiasi pacchetto di stampa disponibile in R!
  4. Non è necessario caricare qualsiasi tranne il pacchetto di stampa. Tuttavia, per lo più ha senso ridimensionare i dati, ad es. con il pacchetto scales di Hadley.

One possible implementation

Se, come me, non si sa nulla su come fare trame radar quando si trova questa discussione: Il coord_polar() potrebbe creare buone alla ricerca radar-trame. Tuttavia l'implementazione è alquanto complicata. Quando ho provato ho avuto più problemi:

  1. Il primo problema con questo approccio è che le linee non rimangono dritto.
  2. Il coord_polar() fa ad es. non tradurre in trama.
  3. Il sistema di coordinate polari rende complicata la personalizzazione dettagliata, poiché le annotazioni e altre funzionalità verranno inserite anche nelle coordinate polari.

Questo ragazzo ha effettuato un nice radar-chart utilizzando coord_polar.

Tuttavia, date le mie esperienze, sconsiglio piuttosto di usare il coord_polar() -trick. Invece se stai cercando un "modo semplice" per creare un radar ggplot statico, forse usa l'ottimo pacchetto ggforce per disegnare cerchi del radar. Non ci sono garanzie che sia più facile che usare il mio pacchetto, ma dall'adattamento sembra più ordinario di coord_polar. Lo svantaggio qui è che per es. plotly non supporta ggforce-extention.

EDIT: Ora ho trovato un bell'esempio con coord_polar di ggplot2 che ha rivisto un po 'la mia opinione.