ggplot, facette, piechart: placer le texte au milieu de camembert en tranches

j'essaie de produire un diagramme à secteurs facetté avec ggplot et faire face à des problèmes avec placer le texte au milieu de chaque tranche:

dat = read.table(text = "Channel Volume Cnt
                         AGENT   high   8344
                         AGENT medium   5448
                         AGENT    low  23823
                         KIOSK   high  19275
                         KIOSK medium  13554
                         KIOSK    low  38293", header=TRUE)

vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) +
  geom_bar(stat="identity", position="fill") +
  coord_polar(theta="y") +
  facet_grid(Channel~.) +
  geom_text(aes(x=factor(1), y=Cnt, label=Cnt, ymax=Cnt), 
            position=position_fill(width=1))

la sortie: enter image description here

quels paramètres de geom_text faudrait-il ajuster pour placer des étiquettes numériques au milieu des tranches de piechart?

question connexe est diagramme à secteurs obtenir son texte sur l'autre mais il ne gère pas les cas avec des facettes.

mise à jour: suivant les conseils et l'approche de Paul Hiemstra dans la question ci-dessus, j'ai changé le code comme suit:

---> pie_text = dat$Cnt/2 + c(0,cumsum(dat$Cnt)[-length(dat$Cnt)])

     vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) +
     geom_bar(stat="identity", position="fill") +
     coord_polar(theta="y") +
     facet_grid(Channel~.) +
     geom_text(aes(x=factor(1), 
--->               y=pie_text, 
                   label=Cnt, ymax=Cnt), position=position_fill(width=1))

comme je m'y attendais tweaking coordiantes texte est absolu, mais il doit être dans les données de la facette: enter image description here

17
demandé sur Community 2013-04-24 09:40:43

4 réponses

nouvelle réponse: avec l'introduction de ggplot2 v2.2.0 , position_stack() peut être utilisé pour positionner les étiquettes sans avoir à calculer d'abord une variable de position. Le code suivant vous donnera le même résultat que l'ancienne réponse:

ggplot(data = dat, aes(x = "", y = Cnt, fill = Volume)) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = Cnt), position = position_stack(vjust = 0.5)) +
  coord_polar(theta = "y") +
  facet_grid(Channel ~ ., scales = "free")

pour supprimer le centre "creux", adapter le code à:

ggplot(data = dat, aes(x = 0, y = Cnt, fill = Volume)) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = Cnt), position = position_stack(vjust = 0.5)) +
  scale_x_continuous(expand = c(0,0)) +
  coord_polar(theta = "y") +
  facet_grid(Channel ~ ., scales = "free")

ancienne réponse: le la solution à ce problème est de créer une variable de position, ce qui peut être fait très facilement avec la base R ou avec les données .tableau , plyr ou dplyr colis:

Étape 1: création de la variable de position pour chaque canal

# with base R
dat$pos <- with(dat, ave(Cnt, Channel, FUN = function(x) cumsum(x) - 0.5*x))

# with the data.table package
library(data.table)
setDT(dat)
dat <- dat[, pos:=cumsum(Cnt)-0.5*Cnt, by="Channel"]

# with the plyr package
library(plyr)
dat <- ddply(dat, .(Channel), transform, pos=cumsum(Cnt)-0.5*Cnt)

# with the dplyr package
library(dplyr)
dat <- dat %>% group_by(Channel) %>% mutate(pos=cumsum(Cnt)-0.5*Cnt)

Étape 2: Créer les facettes de l'intrigue

library(ggplot2)
ggplot(data = dat) + 
  geom_bar(aes(x = "", y = Cnt, fill = Volume), stat = "identity") +
  geom_text(aes(x = "", y = pos, label = Cnt)) +
  coord_polar(theta = "y") +
  facet_grid(Channel ~ ., scales = "free") 

le résultat:

enter image description here

30
répondu Jaap 2017-12-04 21:13:20

je voudrais m'élever contre la manière conventionnelle de faire des tartes en ggplot2, qui est de dessiner un terrain de bar empilé en coordonnées polaires. Bien que j'apprécie l'élégance mathématique de cette approche, elle provoque toutes sortes de maux de tête quand l'intrigue ne ressemble pas tout à fait à la façon dont il est censé. En particulier, ajuster précisément la taille de la tarte peut être difficile. (Si vous ne savez pas ce que je veux dire, essayez de faire un diagramme à secteurs qui s'étend jusqu'au bord du panneau.)

je préfère dessiner des pies dans un système normal de coordonnées cartésiennes, en utilisant geom_arc_bar() de ggforce. Cela demande un peu de travail supplémentaire sur le devant, parce que nous devons calculer nous-mêmes les angles, mais c'est facile et le niveau de contrôle que nous obtenons en conséquence est plus que la valeur. J'ai utilisé cette approche dans les réponses précédentes ici et ici.

Les données (à partir de la question):

dat = read.table(text = "Channel Volume Cnt
AGENT   high   8344
AGENT medium   5448
AGENT    low  23823
KIOSK   high  19275
KIOSK medium  13554
KIOSK    low  38293", header=TRUE)

La pie-le code de dessin:

library(ggplot2)
library(ggforce)
library(dplyr)

# calculate the start and end angles for each pie
dat_pies <- left_join(dat,
                      dat %>% 
                        group_by(Channel) %>%
                        summarize(Cnt_total = sum(Cnt))) %>%
  group_by(Channel) %>%
  mutate(end_angle = 2*pi*cumsum(Cnt)/Cnt_total,      # ending angle for each pie slice
         start_angle = lag(end_angle, default = 0),   # starting angle for each pie slice
         mid_angle = 0.5*(start_angle + end_angle))   # middle of each pie slice, for the text label

rpie = 1 # pie radius
rlabel = 0.6 * rpie # radius of the labels; a number slightly larger than 0.5 seems to work better,
                    # but 0.5 would place it exactly in the middle as the question asks for.

# draw the pies
ggplot(dat_pies) + 
  geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = 0, r = rpie,
                   start = start_angle, end = end_angle, fill = Volume)) +
  geom_text(aes(x = rlabel*sin(mid_angle), y = rlabel*cos(mid_angle), label = Cnt),
            hjust = 0.5, vjust = 0.5) +
  coord_fixed() +
  scale_x_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) +
  scale_y_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) +
  facet_grid(Channel~.)

enter image description here

Pour montrer pourquoi je pense que cette approche est beaucoup plus efficace que les classiques ( coord_polar() ) approche, disons que nous voulons les étiquettes à l'extérieur de la tarte plutôt qu'à l'intérieur. Cela crée un couple de problèmes, comme nous devrons ajuster hjust et vjust en fonction du côté de la tarte a label tombe, et aussi nous devrons faire le tracez un panneau plus large que haut pour faire de l'espace pour les étiquettes sur le côté sans générer un espace excessif au-dessus et en dessous. Résoudre ces problèmes dans l'approche des coordonnées polaires n'est pas amusant, mais c'est trivial dans les coordonnées cartésiennes:

# generate hjust and vjust settings depending on the quadrant into which each
# label falls
dat_pies <- mutate(dat_pies,
                   hjust = ifelse(mid_angle>pi, 1, 0),
                   vjust = ifelse(mid_angle<pi/2 | mid_angle>3*pi/2, 0, 1))

rlabel = 1.05 * rpie # now we place labels outside of the pies

ggplot(dat_pies) + 
  geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = 0, r = rpie,
                   start = start_angle, end = end_angle, fill = Volume)) +
  geom_text(aes(x = rlabel*sin(mid_angle), y = rlabel*cos(mid_angle), label = Cnt,
                hjust = hjust, vjust = vjust)) +
  coord_fixed() +
  scale_x_continuous(limits = c(-1.5, 1.4), name = "", breaks = NULL, labels = NULL) +
  scale_y_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) +
  facet_grid(Channel~.)

enter image description here

7
répondu Claus Wilke 2017-12-05 03:42:05

pour modifier la position du texte de l'étiquette par rapport à la coordonnée, vous pouvez utiliser les arguments vjust et hjust de geom_text . Cela va déterminer la position de toutes les étiquettes simultanément, de sorte que cela pourrait ne pas être ce dont vous avez besoin.

alternativement, vous pouvez modifier la coordonnée du label. Définissez un nouveau data.frame où vous faites la moyenne des coordonnées Cnt ( label_x[i] = Cnt[i+1] + Cnt[i] ) pour positionner l'étiquette au centre de cette tarte particulière. Il suffit de passer ce nouveau data.frame à geom_text en remplacement de l'original data.frame .

en outre, piecharts ont quelques défauts d'interprétation visuelle. En général, je ne les utiliserais pas, surtout lorsqu'il existe de bonnes alternatives, par exemple un dotplot:

ggplot(dat, aes(x = Cnt, y = Volume)) + 
  geom_point() + 
  facet_wrap(~ Channel, ncol = 1)

par exemple, de cette parcelle il est évident que Cnt est plus élevé pour Kiosque que pour Agent, cette information est perdue dans le piechart.

enter image description here

5
répondu Paul Hiemstra 2013-04-24 05:56:34

la réponse suivante est partielle, clunky et je ne l'accepterai pas. L'espoir est qu'elle sollicitera une meilleure solution.

text_KIOSK = dat$Cnt
text_AGENT = dat$Cnt
text_KIOSK[dat$Channel=='AGENT'] = 0
text_AGENT[dat$Channel=='KIOSK'] = 0
text_KIOSK = text_KIOSK/1.7 + c(0,cumsum(text_KIOSK)[-length(dat$Cnt)])
text_AGENT = text_AGENT/1.7 + c(0,cumsum(text_AGENT)[-length(dat$Cnt)])
text_KIOSK[dat$Channel=='AGENT'] = 0
text_AGENT[dat$Channel=='KIOSK'] = 0
pie_text = text_KIOSK + text_AGENT


vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) +
  geom_bar(stat="identity", position=position_fill(width=1)) +
  coord_polar(theta="y") +
  facet_grid(Channel~.) +
  geom_text(aes(y=pie_text, label=format(Cnt,format="d",big.mark=','), ymax=Inf), position=position_fill(width=1))

il produit le graphique suivant: enter image description here

comme vous l'avez remarqué, Je ne peux pas déplacer les étiquettes pour le vert (Bas).

0
répondu topchef 2013-04-29 03:55:41