Вопрос по r, facet, pie-chart, ggplot2, visualization – ggplot, facet, piechart: размещение текста в середине фрагментов круговой диаграммы

17

Я пытаюсь создать ограненную круговую диаграмму с помощью 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 должны быть скорректированы для того, чтобы разместить числовые метки в середине кусочков круговой диаграммы?

Связанный вопросПирог, получающий текст поверх друг друга но это не обрабатывает случай с аспектом.

ОБНОВЛЕНИЕ: следуя совету Пола Химстры и подходу в приведенном выше вопросе, я изменил код следующим образом:

---> 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))

Как я и ожидал, настройка текстовых координат абсолютно, но она должна быть в данных фасета:

Мое последнее решение этой проблемы - избегать круговых диаграмм, когда это возможно :-) topchef

Ваш Ответ

4   ответа
5

вы можете использоватьvjust а такжеhjust аргументыgeom_text, Это будет определять положение всех меток одновременно, поэтому это может быть не то, что вам нужно.

Кроме того, вы можете настроить координаты метки. Определить новыйdata.frame где вы в среднемCnt координата (label_x[i] = Cnt[i+1] + Cnt[i]) разместить метку в центре этого конкретного пирога. Просто передайте это новоеdata.frame вgeom_text в замен оригиналаdata.frame.

Кроме того, у круговых диаграмм есть некоторые недостатки визуальной интерпретации. В общем, я бы не стал их использовать, особенно там, где существуют хорошие альтернативы, например точечный участок:

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

Например, из этого сюжета очевидно, чтоCnt для киоска выше, чем для агента, эта информация теряется в круговой диаграмме.

Круговая диаграмма не может показать разницу в величине между AGENT и KIOSK, потому что она показывает только относительный размер. По моему мнению, текст намного сложнее интерпретировать. Этот эффект становится больше при сравнении, скажем, 5 категорий. Книги какamazon.com/Elements-Graphing-Data-William-Cleveland/dp/... пропагандируйте использование диаграмм, таких как точечный график, в пользу круговой диаграммы. Просто Google дляpie chart evil. Paul Hiemstra
Я не использую fact_wrap - я использую facet_grid (в этом случае не имеет значения, какнапольные весы относится к обоим). Какнапольные весы помочь с выравниванием текстовых меток? Я имею в виду тот факт, чтоpie_text числа должны быть вычислены с соответствующим фасетом (переменнаяканал). topchef
К сожалению, этот ответ не решает проблему наличия фасетов, поскольку длина должна быть выровнена по переменной фасета. topchef
Я не совсем уверен, что вы имеете в виду. Если вам нужны относительные размеры между гранями, просто используйтеscale = free_x в призыве кfacet_wrap. Paul Hiemstra
Спасибо за ответ - я попробую. Но я не могу согласиться с тем, что предложенный сюжет является заменой для круговой диаграммы. На мой взгляд, тот факт, что Cnt выше, теряется в первую очередь из-за невыровненной текстовой метки. topchef
34

НОВЫЙ ОТВЕТ: С введениемggplot2 v2.2.0, position_stack() может использоваться для позиционирования меток без необходимости сначала вычислять переменную позиции. Следующий код даст вам тот же результат, что и старый ответ:

  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") 

Результат:

@jesusgarciab может быть немного поздно, но я обновил ответ Jaap
Спасибо за вашу актуальную помощь. Есть ли способ избавиться от «пустого» центра? (Маленький белый кружочек в центре) jesusgarciab
12

в в 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~.)

0

неуклюжим, и я не приму его. Надежда состоит в том, что он будет искать лучшее решение.

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))

Создает следующий график:

Как вы заметили, я не могу переместить метки на зеленый (низкий).

Да спасибо. Я буду придерживаться правила «нет круговой диаграммы» всякий раз, когда возможно, но для исключений ваш ответ является ответом :-) topchef
Вы видели мой ответ? Я думаю, что это дает решение, которое вы просили. Jaap
Вы абсолютно правы, чтобы как можно больше избегать круговых диаграмм, но некоторые люди, кажется, действительно их любят. Большую часть времени хорошо сконструированная гистограмма гораздо яснее. Jaap

Похожие вопросы