Вопрос по visualization, ggplot2, facet, r, pie-chart – 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 для киоска выше, чем для агента, эта информация теряется в круговой диаграмме.

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

# 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~.)
</pi>

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