ggplot, фасет, piechart: размещение текста в середине срезов круговой диаграммы
Я пытаюсь создать фасеточную круговую диаграмму с ggplot и сталкиваюсь с проблемами размещения текста в середине каждого среза:
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))
выход:
какие параметры geom_text
следует настроить, чтобы разместить числовые метки в середине ломтиков piechart?
вопрос пирог сюжет получает свой текст друг на друга но он не обрабатывает случай с фацетом.
обновление: после Павла Совет и подход Hiemstra в вопросе выше я изменил код следующим образом:
---> 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))
как я и ожидал, настройка текста coordiantes является абсолютной, но она должна быть в пределах данных фасета:
4 ответов
ОТВЕТ: введение ggplot2 версии v2.2.0, position_stack()
смогите быть использовано для того чтобы расположить ярлыки без потребности высчитать переменную положения сперва. Следующий код даст вам тот же результат, что и старый ответ:
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")
чтобы удалить "полый" центр, адаптируйте код к:
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")
ОТВЕТ: решение этой проблемы заключается в создании переменной положения, что можно сделать довольно легко с базой R или с данные.таблица, plyr или dplyr упаковка:
Шаг 1: создание переменной положения для каждого канала
# 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)
Шаг 2: создание фасетного участка
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")
результат:
Я хотел бы высказаться против обычного способа приготовления пирогов в ggplot2, который заключается в том, чтобы нарисовать сложенный барплот в полярных координатах. Хотя я ценю математическую элегантность этого подхода, он вызывает всевозможные головные боли, когда сюжет выглядит не совсем так, как он должен. В частности, точно регулировать размер пирога может быть сложно. (Если вы не знаете, что я имею в виду, попробуйте сделать круговую диаграмму, которая простирается вплоть до края участка панель.)
Я предпочитаю рисовать пироги в нормальной декартовой системе координат, используя geom_arc_bar()
от ggforce. Это требует немного дополнительной работы на переднем конце, потому что мы должны вычислить углы, но это легко и уровень контроля, который мы получаем в результате более чем стоит.
Я использовал этот подход в предыдущих ответах здесь и здесь.
данные (из вопроса):
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)
чертеж пирога код:
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~.)
чтобы показать, почему я думаю, что этот подход намного мощнее обычного (coord_polar()
) подходим, скажем, мы хотим, чтобы этикетки были снаружи пирога, а не внутри. Это создает пару проблем, например, нам придется настроить hjust
и vjust
в зависимости от стороны пирога ярлык падает, и также мы должны будем сделать
панель графика шире, чем высокая, чтобы освободить место для надписей сбоку без создания избыточного пространства сверху и снизу. Решение этих задач в Полярном координатном подходе не весело, но тривиально в декартовых координатах:
# 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~.)
чтобы настроить положение текста метки относительно координаты, вы можете использовать vjust
и hjust
доводы geom_text
. Это определит положение всех меток одновременно, поэтому это может быть не то, что вам нужно.
кроме того, вы можете настроить координату метки. Определите новое data.frame
где вы усредняете Cnt
координат (label_x[i] = Cnt[i+1] + Cnt[i]
), чтобы расположить метку в центре этого конкретного пирога. Просто передайте это новое data.frame
до geom_text
in замена оригинала data.frame
.
кроме того, piecharts имеют некоторые недостатки визуальной интерпретации. В общем, я бы не использовал их, особенно там, где существуют хорошие альтернативы, например, dotplot:
ggplot(dat, aes(x = Cnt, y = Volume)) +
geom_point() +
facet_wrap(~ Channel, ncol = 1)
например, из этого сюжета очевидно, что Cnt
выше для киоска чем для агента, эта информация потеряна в piechart.
следующий ответ частичный, неуклюжий, и я не буду его принимать. Есть надежда, что она приведет к лучшему решению.
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))
он создает следующую диаграмму:
Как вы заметили, я не могу перемещать метки для зеленого (низкий).