web-dev-qa-db-fra.com

création d'un "diagramme radar" (a.k.a. star plot; spider plot) using ggplot2 in R

Je veux créer un tracé comme celui ci-dessous:

enter image description here

Je sais que je peux utiliser la fonction radarchart du package fmsb. Je me demande si ggplot2 peut le faire en utilisant les coordonnées polaires? Merci.

36
lokheart

Tout d'abord, nous chargeons certains packages.

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

Voici les données de l'exemple de radarchart auquel vous avez lié.

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

Nous avons besoin d'un peu de manipulation pour les rendre adaptés à ggplot.

Normalisez-les, ajoutez une colonne id et convertissez-les en format long.

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 encapsule également les valeurs afin que les premier et dernier facteurs se rencontrent. Nous ajoutons un niveau de facteur supplémentaire pour éviter cela. Ce n'est plus vrai.

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

Voici l'intrigue. Ce n'est pas tout à fait la même chose, mais cela devrait vous aider à démarrer.

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 Notez que lorsque vous utilisez coord_polar, les lignes sont courbes. Si vous voulez des lignes droites, vous devrez essayer une technique différente.

8
Richie Cotton

Si vous recherchez une version à coordonnées non polaires, je pense que la fonction suivante vous aidera:

###################################
##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 vous tracez par cluster ou groupe, vous pouvez alors utiliser ce qui suit:

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)

Cela devrait fonctionner avec l'exemple de données suivant:

   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

4
Tony M.

J'ai passé plusieurs jours sur ce problème et j'ai finalement décidé de construire mon propre paquet au sommet de ggradar. Le noyau est une version améliorée de la fonction 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

Notez que j'ai en fait comparé plus de fonctions dans ce benchmark - entre autres fonctions de ggradar. Généralement, la solution de @Tony M est bien écrite - dans le sens de la logique et que vous pouvez l'utiliser dans de nombreuses autres langues, comme par exemple Javascript, avec quelques ajustements. Cependant R devient beaucoup plus rapide si vous vectorisez les opérations. D'où le gain massif de temps de calcul avec ma solution.

Toutes les réponses sauf @Tony M. ont utilisé la fonction coord_polar De ggplot2. Il y a quatre avantages à rester dans le système de coordonnées cartésiennes:

  1. Il vous permet également de transporter votre solution vers d'autres packages de traçage, par ex. plotly.
  2. Quiconque a une certaine compréhension du cosinus et de la fonction sinus standard peut comprendre comment fonctionne la transformation des données.
  3. Vous pouvez étendre et personnaliser l'intrigue comme vous le souhaitez - diable, vous pouvez l'utiliser avec n'importe quel package de traçage disponible dans R!
  4. Vous n'avez pas besoin de charger aucun sauf votre package de traçage. Cependant, il est généralement logique de redimensionner vos données, par exemple avec le package scales- de Hadley.

One possible implementation

Si, comme moi, vous ne savez rien sur la façon de faire des tracés radar lorsque vous trouvez ce fil : La coord_polar() pourrait créer de beaux radars- parcelles. Cependant, la mise en œuvre est quelque peu délicate. Quand je l'ai essayé, j'ai eu plusieurs problèmes:

  1. Le premier problème avec cette approche est que les lignes ne restent pas droites.
  2. La coord_polar() fait par exemple pas traduire en complot.
  3. Le système de coordonnées polaires rend la personnalisation détaillée délicate, car les annotations et autres fonctionnalités seront également intégrées aux coordonnées polaires.

Ce gars-là a fait un Nice radar-chart en utilisant coord_polar.

Cependant, étant donné mes expériences - je déconseille plutôt d'utiliser l'astuce coord_polar()-. Au lieu de cela, si vous cherchez un "moyen facile" de créer un radar ggplot statique, utilisez peut-être le grand package ggforce- pour dessiner des cercles du radar. Aucune garantie que c'est plus facile que d'utiliser mon package, mais de l'adaptabilité semble plus propre que coord_polar. L'inconvénient ici est que par ex. plotly ne prend pas en charge l'extension ggforce.

EDIT: Maintenant, j'ai trouvé un bel exemple avec coord_polar de ggplot2 qui a un peu révisé mon opinion.

4
5th

Voici une réponse qui le fait presque dans ggplot.

Je ne prétends rien de plus que de mettre l'exemple ici, il est basé sur ce que Hadley a montré ici https://github.com/hadley/ggplot2/issues/516

Tout ce que j'ai fait, c'est utiliser deployer/tidyr à la place et choisir seulement 3 voitures pour plus de simplicité

les problèmes encore en suspens sont 1) le dernier et le premier point ne sont pas connectés, c'est évident si vous voyez le coord_polar comme un habillage de l'axe x traditionnel. Il n'y a aucune raison pour qu'ils soient connectés. Mais c'est ainsi que les diagrammes radar sont normalement affichés 2) pour ce faire, vous devez ajouter manuellement un segment entre ces 2 points. Une petite manipulation et quelques couches supplémentaires devraient le faire. J'essaierai d'y travailler si j'ai un peu de temps

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
user1617979

Je suis tombé sur cette grande bibliothèque qui donne des tracés d'araignée parfaits et compatibles avec ggplot:

https://github.com/ricardo-bion/ggradar

Très facile à installer et à utiliser, comme vous pouvez le voir sur le github:

devtools::install_github("ricardo-bion/ggradar", dependencies=TRUE)
library(ggradar)

suppressPackageStartupMessages(library(dplyr))
library(scales)
library(tibble)

mtcars %>%
     rownames_to_column( var = "group" ) %>%
     mutate_at(vars(-group),funs(rescale)) %>%
     tail(4) %>% select(1:10) -> mtcars_radar

ggradar(mtcars_radar) 

enter image description here

1
Dan Chaltiel