2012-03-08 15 views
36

Quiero crear una trama como la siguiente:creación de "gráfico de radar" (parcela estrellas aka; diagrama de araña) usando ggplot2 en I

enter image description here

Sé que puedo utilizar la función radarchart de fmsb paquete. Me pregunto si ggplot2 puede hacerlo, usando coordenadas polares? Gracias.

+6

dado que ggplot2 me da un mejor control sobre el título de la trama, etiquetas de escala xy, e incluso haciendo facetas, necesito hacer más de 30 diagramas de radar y quiero mostrarlos en 1 página, y esto me ayuda a entender mejor cómo funciona ggplot2 – lokheart

+3

Puede hacer todo eso con gráficos base. par (mfrow = c (5,6)) y sus 30 parcelas (minúsculas) en una página. ¿Qué pasa con 'title ("Hello")' para títulos de trama? A veces el tiempo dedicado a entender ggplot2 es mejor dedicarlo con gráficos base ... – Spacedman

+21

Creo que es una pregunta legítima querer hacer esto en ggplot2 –

Respuesta

9

Primero, cargamos algunos paquetes.

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

Estos son los datos del ejemplo de radarchart al que se ha vinculado.

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) 
) 

Necesitamos un poco de manipulación para que sean adecuados para ggplot.

Normalícelos, agregue una columna de id. Y conviértelos a formato largo.

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 también envuelve los valores para los primeros y últimos factores se reúnen. Agregamos un nivel de factor adicional para evitar esto. Esto ya no es cierto.

niveles (variable $ long_dat) < - c (niveles (long_dat $ variable), "")

Ésta es la trama. No es lo mismo, pero debería comenzar.

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 Tenga en cuenta que cuando se utiliza coord_polar, las líneas son curvas. Si quieres líneas rectas, tendrás que probar una técnica diferente.

+0

¿Alguien había logrado que las líneas fueran rectas? (PD a Richie: ¡Buena solución! ¿Puedes comentar también por qué mis primeros y últimos factores no coinciden, incluso si no hay ningún nivel "" "allí?) –

+0

@ Antón Parece que el comportamiento de' ggplot2' cambios desde que escribí esta respuesta. (Hubo una reescritura bastante grande en algún lugar alrededor de v0.9.) He actualizado el código para que funcione de nuevo. No veo una manera obvia de enderezar las líneas o de hacer coincidir los puntos de inicio y final. Pensé que 'geom_path' haría lo último, pero parece que no funciona. –

4

Si usted está buscando una versión de coordenadas no polar, creo que la siguiente función le ayudará:

################################### 
##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 

} 

Si están conspirando por conglomerado o grupo a continuación, puede utilizar el siguiente:

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) 

Esto debería funcionar con la siguiente muestra de datos:

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

Aquí hay una respuesta que casi lo hace en ggplot.

hago ni reclamar nada más que poner el ejemplo aquí, se basa en lo que Hadley mostraron aquí https://github.com/hadley/ggplot2/issues/516

Todo lo que hice fue el uso de activación/tidyr lugar y elegir sólo 3 coches por simplicidad

la los problemas pendientes son 1) el último y el primer punto no están conectados, esto es obvio si se ve el coord_polar como un ajuste del eje x tradicional. No hay ninguna razón por la cual deberían estar conectados. Pero así es como normalmente se muestran los gráficos de radar 2) para hacer eso, necesita agregar un segmento manualmente entre esos 2 puntos.Una pequeña manipulación y algunas capas más deberían hacerlo. Voy a tratar de trabajar en él si tengo algún tiempo

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

pasé varios días en este problema y al final decidí construida en lo alto de my own packageggradar. El núcleo de la misma es una versión mejorada de la función de @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 

Tenga en cuenta que en realidad he comparado more functions in this benchmark - entre otras funciones de ggradar. En general, la solución de @Tony M está bien escrita, en el sentido de la lógica, y puede usarla en muchos otros idiomas, como p. Ej. Javascript, con algunos ajustes. Sin embargo, R se vuelve mucho más rápido si vectoriza las operaciones. Por lo tanto, la ganancia masiva en tiempo de cálculo con mi solución.

Todas las respuestas excepto @Tony M. han utilizado la función coord_polar de ggplot2. Hay cuatro ventajas de permanecer dentro del sistema de coordenadas cartesianas:

  1. Le permite transportar su solución también a otros paquetes de trazado, p. plotly.
  2. Todo el mundo que tenga cierta comprensión del coseno estándar y la función sinusal puede comprender cómo funciona la transformación de datos.
  3. Puede ampliar y personalizar la trama como desee; ¡demonios, puede usarla con cualquier paquete de trazado disponible en R!
  4. No necesita cargar cualquier excepto su paquete de trazado. Sin embargo, tiene sentido sobre todo cambiar la escala de sus datos, p. con el paquete scales de Hadley.

One possible implementation

Si como yo, usted no sabe nada acerca de cómo hacer parcelas de radar cuando encuentre este hilo: El coord_polar() podría crear buen aspecto radar-parcelas. Sin embargo, la implementación es algo complicada. Cuando lo probé tuve varios problemas:

  1. El primer problema con este enfoque es que las líneas no se quedan recta.
  2. El coord_polar() hace p. no traducir a plotly.
  3. El sistema de coordenadas polares dificulta la personalización detallada, ya que las anotaciones y otras características también se incluirán en las coordenadas polares.

Este tipo aquí hizo un nice radar-chart usando coord_polar.

Sin embargo, dada mi experiencia, prefiero no utilizar el coord_polar() -trick. En cambio, si está buscando una 'manera fácil' de crear un ggplot-radar estático, tal vez use el gran paquete ggforce para dibujar círculos del radar. No hay garantías de que esto sea más fácil que usar mi paquete, pero la adaptabilidad parece más clara que coord_polar. La desventaja aquí es que, p. plotly no es compatible con ggforce-extention.

EDIT: Ahora encontré buen ejemplo con coord_polar de ggplot2 que revisó mi opinión un poco.

Cuestiones relacionadas