ggplot,facet,piechart:将文本放在饼图切片的中间

时间:2013-04-24 05:40:44

标签: r ggplot2 visualization pie-chart facet

我正在尝试使用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))

输出: enter image description here

为了将数字标签放在饼图切片的中间,应调整geom_text的哪些参数?

相关问题是Pie plot getting its text on top of each other,但它不处理facet的情况。

更新:按照Paul 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是绝对的,但它需要在facet数据中: enter image description here

4 个答案:

答案 0 :(得分:34)

新答案:随着 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")

OLD ANSWER:此问题的解决方案是创建一个位置变量,可以使用基数R或data.tableplyr或{{ 3}}包:

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

结果:

enter image description here

答案 1 :(得分:14)

我想反对在ggplot2中制作馅饼的传统方法,即在极坐标中绘制堆积的条形图。虽然我很欣赏这种方法的数学优雅,但当情节看起来不像预期时,它会引起各种各样的麻烦。特别是,精确调整馅饼的大小可能很困难。 (如果您不知道我的意思,请尝试制作一个延伸到绘图面板边缘的饼图。)

我更喜欢使用ggforce中的geom_arc_bar()在普通笛卡尔坐标系中绘制馅饼。它需要在前端进行一些额外的工作,因为我们必须自己计算角度,但这很容易,因此我们得到的控制水平更值得。 我在以前的答案herehere.

中使用了这种方法

数据(来自问题):

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

enter image description here

为了说明为什么我认为这种方法比传统的(coord_polar())方法更强大,让我们说我们希望标签位于饼外而不是内部。这会产生一些问题,例如我们必须根据标签所在的馅饼边调整hjustvjust,同时我们还必须制作 绘图面板宽于高,以便为侧面的标签留出空间,而不会在上方和下方产生过多空间。在极坐标方法中解决这些问题并不好玩,但在笛卡尔坐标系中它是微不足道的:

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

enter image description here

答案 2 :(得分:5)

要调整标签文本相对于坐标的位置,您可以使用vjust的{​​{1}}和hjust参数。这将同时确定所有标签的位置,因此这可能不是您所需要的。

或者,您可以调整标签的坐标。定义一个新的geom_text,您可以在其中平均data.frame坐标(Cnt),以将标签定位在该特定饼图的中心。只需将此新label_x[i] = Cnt[i+1] + Cnt[i]传递给data.frame,即可取代原来的geom_text

此外,饼图有一些视觉解释缺陷。一般来说,我不会使用它们,特别是在存在良好替代品的地方,例如一个点图:

data.frame

例如,从这个图中可以看出,Kiosk的ggplot(dat, aes(x = Cnt, y = Volume)) + geom_point() + facet_wrap(~ Channel, ncol = 1) 高于Agent,这个信息会在饼图中丢失。

enter image description here

答案 3 :(得分: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))

它产生以下图表: enter image description here

正如您所注意到的,我无法移动绿色标签(低)。