36 votes

Réalisation d'un tracé circulaire avec un centre creux (ou tracé de piste de course)

On m'a demandé de recréer le style de graphique suivant. (Veuillez ignorer la question de savoir si c'est un bon type de visualisation et considérez charitablement qu'il s'agit d'ajouter un élément coloré à un tableau numérique).

La plupart du temps, c'est assez simple, mais je n'ai pas encore trouvé un bon moyen de rendre le centre creux. Pour gagner du temps, il se peut que j'aie recours à l'ajout de données fictives invisibles (je publierai cette approche si personne d'autre ne le fait, mais elle semble moins optimale que celle qui modifie le thème). Existe-t-il une solution basée sur le thème ou une solution R non-ggplot2 ?

Ce que nous imitons

enter image description here

Résultat simple de ggplot2 (centre rempli non désiré)

library(ggplot2)

# make sample dataframe

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing", 
            "Cars", "Food/Beverages", "Personal Hygiene", 
            "Personal Health/OTC", "Hair Care")
Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)

internetImportance<-data.frame(Category,Percent)

# append number to category name
internetImportance$Category <-
     paste0(internetImportance$Category," - ",internetImportance$Percent,"%")

# set factor so it will plot in descending order 
internetImportance$Category <-
    factor(internetImportance$Category, 
    levels=rev(internetImportance$Category))

# plot

ggplot(internetImportance, aes(x = Category, y = Percent,
    fill = Category)) + 
    geom_bar(width = 0.9, stat="identity") + 
    coord_polar(theta = "y") +
    xlab("") + ylab("") +
    ylim(c(0,100)) +
    ggtitle("Top Product Categories Influenced by Internet") +
    geom_text(data = internetImportance, hjust = 1, size = 3,
              aes(x = Category, y = 0, label = Category)) +
    theme_minimal() +
    theme(legend.position = "none",
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          axis.line = element_blank(),
          axis.text.y = element_blank(),
          axis.text.x = element_blank(),
          axis.ticks = element_blank())

enter image description here

Comment pouvons-nous tracer ces données avec un centre creux ?

27voto

Theodore Lytras Points 2595

Voici une solution sansggplot2 (graphiques R de base) qui utilise la fonction plotrix qui contient deux fonctions intéressantes : draw.circle() et draw.arc() :

circBarPlot <- function(x, labels, colors=rainbow(length(x)), cex.lab=1) {
  require(plotrix)
  plot(0,xlim=c(-1.1,1.1),ylim=c(-1.1,1.1),type="n",axes=F, xlab=NA, ylab=NA)
  radii <- seq(1, 0.3, length.out=length(x))
  draw.circle(0,0,radii,border="lightgrey")
  angles <- (1/4 - x)*2*pi
  draw.arc(0, 0, radii, angles, pi/2, col=colors, lwd=130/length(x), lend=2, n=100)
  ymult <- (par("usr")[4]-par("usr")[3])/(par("usr")[2]-par("usr")[1])*par("pin")[1]/par("pin")[2]
  text(x=-0.02, y=radii*ymult, labels=paste(labels," - ", x*100, "%", sep=""), pos=2, cex=cex.lab)
}

circBarPlot(Percent/100, Category)
text(0,0,"GLOBAL",cex=1.5,col="grey")

Ça me donne :

Circular bar plot

23voto

Arun Points 41689

Je pense qu'une solution immédiate consiste à créer des entrées "vides". Je créerais internetImportance data.frame comme ceci :

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing", 
        "Cars", "Food/Beverages", "Personal Hygiene", 
        "Personal Health/OTC", "Hair Care")
Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)

internetImportance <- data.frame(Category,Percent)

len <- 4
df2 <- data.frame(Category = letters[1:len], Percent = rep(0, len), 
                                 Category2 = rep("", len))
internetImportance$Category2 <- 
 paste0(internetImportance$Category," - ",internetImportance$Percent,"%")

# append number to category name
internetImportance <- rbind(internetImportance, df2)

# set factor so it will plot in descending order 
internetImportance$Category <-
    factor(internetImportance$Category, 
    levels=rev(internetImportance$Category))

Et puis j'ai comploté ggplot2 avec fill=category2 comme suit :

ggplot(internetImportance, aes(x = Category, y = Percent,
    fill = Category2)) + 
    geom_bar(width = 0.9, stat="identity") + 
    coord_polar(theta = "y") +
    xlab("") + ylab("") +
    ylim(c(0,100)) +
    ggtitle("Top Product Categories Influenced by Internet") +
    geom_text(data = internetImportance, hjust = 1, size = 3,
              aes(x = Category, y = 0, label = Category2)) +
    theme_minimal() +
    theme(legend.position = "none",
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          axis.line = element_blank(),
          axis.text.y = element_blank(),
          axis.text.x = element_blank(),
          axis.ticks = element_blank())

Cela me donne :

enter image description here

Vous pouvez ajouter un geom_text(label="GLOBAL", x=.5, y=.5, size=4) + avant theme_minimal pour ajouter le texte GLOBAL .

13voto

plannapus Points 9026

Un autre base solution qui ne repose pas sur plotrix paquet :

circular.barplot<-function(values, labels, col, cex){
    df<-data.frame(values=sort(values), labels=labels[order(values)])
    col<-col[order(values)]
    plot(NA,xlim=c(-1.3,1.3),ylim=c(-1.3,1.3),axes=F, xlab=NA, ylab=NA, asp=1)
    t<-sapply(df$values,function(x).5*pi-seq(0, 2*pi*x/100,length=1000))
    x<-sapply(1:nrow(df),function(x)(.3+x/nrow(df))*cos(t[,x]))
    y<-sapply(1:nrow(df),function(x)(.3+x/nrow(df))*sin(t[,x]))
    for(i in 1:nrow(df)){
        lines(x=x[,i],y=y[,i],col=col[i],lwd=10,lend=1)
        text(x[1,i],y[1,i],paste(df$labels[i]," - ",df$values[i],"%",sep=""),
             pos=2,cex=cex)
        }
    }

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X