web-dev-qa-db-fra.com

R: camembert avec pourcentage comme étiquettes avec ggplot2

À partir d'un bloc de données, je souhaite tracer un graphique à secteurs pour cinq catégories avec leurs pourcentages sous forme d'étiquettes dans le même graphique, dans l'ordre croissant.

Mon code est:

League<-c("A","B","A","C","D","E","A","E","D","A","D")
data<-data.frame(League) # I have more variables 

p<-ggplot(data,aes(x="",fill=League))
p<-p+geom_bar(width=1)
p<-p+coord_polar(theta="y")
p<-p+geom_text(data,aes(y=cumsum(sort(table(data)))-0.5*sort(table(data)),label=paste(as.character(round(sort(table(data))/sum(table(data)),2)),rep("%",5),sep="")))
p

J'utilise

cumsum(sort(table(data)))-0.5*sort(table(data))

placer l'étiquette dans la partie correspondante et

label=paste(as.character(round(sort(table(data))/sum(table(data)),2)),rep("%",5),sep="")

pour les étiquettes qui est les pourcentages.

Je reçois la sortie suivante:

Error: ggplot2 doesn't know how to deal with data of class uneval
10
pescobar

J'ai conservé la majeure partie de votre code. J'ai trouvé cela assez facile à déboguer en laissant de côté le coord_polar... pour voir plus facilement ce qui se passe sous forme de graphique à barres.

L'essentiel était de réorganiser le facteur du plus élevé au plus bas pour obtenir l'ordre de traçage correct, puis de jouer avec les positions des étiquettes pour les corriger. J'ai également simplifié votre code pour les étiquettes (vous n'avez pas besoin du as.character ni de la rep, et paste0 est un raccourci pour sep = "".)

League<-c("A","B","A","C","D","E","A","E","D","A","D")
data<-data.frame(League) # I have more variables 

data$League <- reorder(data$League, X = data$League, FUN = function(x) -length(x))

at <- nrow(data) - as.numeric(cumsum(sort(table(data)))-0.5*sort(table(data)))

label=paste0(round(sort(table(data))/sum(table(data)),2) * 100,"%")

p <- ggplot(data,aes(x="", fill = League,fill=League)) +
  geom_bar(width = 1) +
  coord_polar(theta="y") +
  annotate(geom = "text", y = at, x = 1, label = label)
p

Le calcul at consiste à trouver les centres des cales. (Il est plus facile de penser à eux comme aux centres de barres d'un graphe empilé, il suffit de lancer le graphe ci-dessus sans la ligne coord_polar à voir.) Le calcul de at peut être ventilé comme suit:

table(data) est le nombre de lignes de chaque groupe et sort(table(data)) les classe dans l’ordre dans lequel elles seront tracées. Prendre la cumsum() de cela nous donne les bords de chaque barre empilées les unes sur les autres et multiplier par 0,5 nous donne la moitié de la hauteur de chaque barre de la pile (ou la moitié de la largeur des coins de la tarte). 

as.numeric() s'assure simplement que nous avons un vecteur numérique plutôt qu'un objet de classe table.

En soustrayant les demi-largeurs des hauteurs cumulées, on donne aux centres chaque barre empilée. Mais ggplot empilera les barres avec les plus grandes en bas, alors que toutes nos sort()ing placent les plus petites en premier, nous devons donc tout faire en nrow - car ce que nous avons en fait calculé sont les positions des étiquettes par rapport à top du bar, pas le fond. (Et, avec les données désagrégées d'origine, nrow() est le nombre total de lignes, donc la hauteur totale de la barre.)

10
Gregor

Préface: je n'ai pas fait de camemberts de mon plein gré.

Voici une modification de la fonction ggpie qui inclut des pourcentages:

library(ggplot2)
library(dplyr)

#
# df$main should contain observations of interest
# df$condition can optionally be used to facet wrap
#
# labels should be a character vector of same length as group_by(df, main) or
# group_by(df, condition, main) if facet wrapping
#

pie_chart <- function(df, main, labels = NULL, condition = NULL) {

  # convert the data into percentages. group by conditional variable if needed
  df <- group_by_(df, .dots = c(condition, main)) %>%
    summarize(counts = n()) %>%
    mutate(perc = counts / sum(counts)) %>%
    arrange(desc(perc)) %>%
    mutate(label_pos = cumsum(perc) - perc / 2,
           perc_text = paste0(round(perc * 100), "%"))

  # reorder the category factor levels to order the legend
  df[[main]] <- factor(df[[main]], levels = unique(df[[main]]))

  # if labels haven't been specified, use what's already there
  if (is.null(labels)) labels <- as.character(df[[main]])

  p <- ggplot(data = df, aes_string(x = factor(1), y = "perc", fill = main)) +

    # make stacked bar chart with black border
    geom_bar(stat = "identity", color = "black", width = 1) +

    # add the percents to the interior of the chart
    geom_text(aes(x = 1.25, y = label_pos, label = perc_text), size = 4) +

    # add the category labels to the chart
    # increase x / play with label strings if labels aren't pretty
    geom_text(aes(x = 1.82, y = label_pos, label = labels), size = 4) +

    # convert to polar coordinates
    coord_polar(theta = "y") +

    # formatting
    scale_y_continuous(breaks = NULL) +
    scale_fill_discrete(name = "", labels = unique(labels)) +
    theme(text = element_text(size = 22),
          axis.ticks = element_blank(),
          axis.text = element_blank(),
          axis.title = element_blank())

  # facet wrap if that's happening
  if (!is.null(condition)) p <- p + facet_wrap(condition)

  return(p)
}

Exemple:

# sample data
resps <- c("A", "A", "A", "F", "C", "C", "D", "D", "E")
cond <- c(rep("cat A", 5), rep("cat B", 4))
example <- data.frame(resps, cond)

Juste comme un appel typique de ggplot:

ex_labs <- c("alpha", "charlie", "delta", "echo", "foxtrot")

pie_chart(example, main = "resps", labels = ex_labs) +
  labs(title = "unfacetted example")

 Unfacetted pie chart abomination

ex_labs2 <- c("alpha", "charlie", "foxtrot", "delta", "charlie", "echo")

pie_chart(example, main = "resps", labels = ex_labs2, condition = "cond") +
  labs(title = "facetted example")

 enter image description here

10
alexpghayes

Cela a fonctionné sur toutes les fonctions incluses grandement inspirées de ici

 ggpie <- function (data) 
{
  # prepare name
  deparse( substitute(data) ) -> name ;

  # prepare percents for legend
  table( factor(data) ) -> tmp.count1
  prop.table( tmp.count1 ) * 100 -> tmp.percent1 ;
  paste( tmp.percent1, " %", sep = "" ) -> tmp.percent2 ;
  as.vector(tmp.count1) -> tmp.count1 ;

  # find breaks for legend
  rev( tmp.count1 ) -> tmp.count2 ;
  rev( cumsum( tmp.count2 ) - (tmp.count2 / 2) ) -> tmp.breaks1 ;

  # prepare data
  data.frame( vector1 = tmp.count1, names1 = names(tmp.percent1) ) -> tmp.df1 ;


  # plot data
  tmp.graph1 <- ggplot(tmp.df1, aes(x = 1, y = vector1, fill = names1 ) ) +
    geom_bar(stat = "identity", color = "black" ) +
    guides( fill = guide_legend(override.aes = list( colour = NA ) ) ) +
    coord_polar( theta = "y" ) +
    theme(axis.ticks = element_blank(),
          axis.text.y = element_blank(),
          axis.text.x = element_text( colour = "black"),
          axis.title = element_blank(),
          plot.title = element_text( hjust = 0.5, vjust = 0.5) ) +
    scale_y_continuous( breaks = tmp.breaks1, labels = tmp.percent2 ) +   
    ggtitle( name ) + 
    scale_fill_grey( name = "") ;

  return( tmp.graph1 )
} ;

Un exemple :

sample( LETTERS[1:6], 200, replace = TRUE) -> vector1 ;
ggpie(vector1)

Sortie

0
François Jean