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:
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:
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:
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~.)
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~.)
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.
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:
comme vous l'avez remarqué, Je ne peux pas déplacer les étiquettes pour le vert (Bas).