ggplot, facet, piechart: umieszczanie tekstu w środku plasterków wykresu kołowego
Próbuję stworzyć wykresy kołowe z ggplot i borykam się z problemami z umieszczeniem tekstu w środku każdego kawałka:
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))
Wyjście:
Jakie parametry {[2] } należy dostosować, aby umieścić etykiety numeryczne na środku plasterków?
Pokrewnym pytaniem jest Pie wykreśla swój tekst na sobie ale nie radzi sobie z case Z aspektem.
Aktualizacja: po poradzie Paula Hiemstry i podejściu w pytaniu powyżej zmieniłem kod w następujący sposób:
---> 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))
Jak się spodziewałem poprawianie tekstu coordiantes jest absolutne, ale musi być w danych facet:
4 answers
Nowa ODPOWIEDŹ: z wprowadzeniem ggplot2 v2.2.0, position_stack()
może być używany do pozycjonowania etykiet bez konieczności wcześniejszego obliczania zmiennej pozycji. Poniższy kod daje taki sam wynik jak stara odpowiedź:
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")
Aby usunąć "hollow" center, dostosuj kod do:
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")
Stara ODPOWIEDŹ: rozwiązaniem tego problemu jest stworzenie zmiennej pozycji, co można zrobić dość łatwo z bazą R lub z data.tabela, Pakiety plyr lub dplyr:
Krok 1: Tworzenie zmiennej pozycji dla każdego kanału
# 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)
Krok 2: Tworzenie wykresu
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")
Wynik:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-04 21:13:20
Chciałbym wypowiedzieć się przeciwko konwencjonalnemu sposobowi robienia ciast w ggplot2, który polega na narysowaniu ułożonego słupka we współrzędnych biegunowych. Chociaż doceniam matematyczną elegancję tego podejścia, to powoduje to różnego rodzaju bóle głowy, gdy fabuła nie wygląda tak, jak powinna. W szczególności precyzyjne dopasowanie rozmiaru ciasta może być trudne. (Jeśli nie wiesz, co mam na myśli, spróbuj zrobić Wykres kołowy, który rozciąga się aż do krawędzi wykresu panel.)
Wolę rysować placki w normalnym kartezjańskim układzie współrzędnych, używając geom_arc_bar()
z ggforce. Wymaga to trochę dodatkowej pracy na froncie, ponieważ sami musimy obliczać kąty, ale jest to łatwe i poziom kontroli, który otrzymujemy w rezultacie, jest więcej niż wart.
Zastosowałem to podejście w poprzednich odpowiedziach tutaj i tutaj.
Dane (z pytania):
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)
The pie-drawing kod:
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~.)
Aby pokazać, dlaczego uważam, że to podejście jest o wiele potężniejsze niż konwencjonalne (coord_polar()
) podejście, powiedzmy, że chcemy etykiety na zewnątrz ciasta, a nie wewnątrz. Tworzy to kilka problemów, takich jak będziemy musieli dostosować hjust
i vjust
w zależności od strony ciasta Etykieta spada, a także będziemy musieli zrobić
wykreśl panel szerszy niż wysoki, aby zrobić miejsce na etykiety z boku bez generowania nadmiernych spacja powyżej i poniżej. Rozwiązywanie tych problemów w podejściu współrzędnych biegunowych nie jest zabawne, ale jest trywialne we współrzędnych kartezjańskich:
# 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~.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-05 03:42:05
Aby dostosować pozycję tekstu etykiety względem współrzędnej, możesz użyć argumentów vjust
i hjust
z geom_text
. To określi pozycję wszystkich etykiet jednocześnie, więc może to nie być to, czego potrzebujesz.
Alternatywnie, można dostosować współrzędną etykiety. Zdefiniuj nową data.frame
, w której uśredniasz współrzędną Cnt
(label_x[i] = Cnt[i+1] + Cnt[i]
), aby umieścić etykietę w środku danego placka. Po prostu przekaż ten nowy data.frame
do geom_text
w zastępstwie oryginału data.frame
.
Ponadto piecharty mają pewne wady interpretacji wizualnej. W ogóle nie używałbym ich, zwłaszcza tam, gdzie istnieją dobre alternatywy, np. kropka: {]}
ggplot(dat, aes(x = Cnt, y = Volume)) +
geom_point() +
facet_wrap(~ Channel, ncol = 1)
Na przykład z tego wykresu wynika, że {[5] } jest wyższa dla Kiosku niż dla agenta, informacja ta jest tracona w Piech.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-24 05:56:34
Odpowiedź jest częściowa, niezgrabna i nie przyjmę jej. Mamy nadzieję, że będzie to zabiegać o lepsze rozwiązanie.
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))
Tworzy następujący wykres:
Jak zauważyłeś nie mogę przesunąć etykiet na zielony (niski).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-29 03:55:41