ggplot, facet, piechart: colocando texto en el medio de las partes del gráfico circular

Intento producir un gráfico circular facetado con ggplot y enfrentar problemas al colocar texto en el medio de cada segmento:

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

La salida: enter image description here

¿Qué parámetros de geom_text deberían ajustarse para colocar tags numéricas en el medio de las secciones de los gráficos circulares?

La pregunta relacionada es Pie plot obteniendo su texto uno encima del otro, pero no maneja el caso con faceta.

ACTUALIZACIÓN: siguiendo el consejo y enfoque de Paul Hiemstra en la pregunta anterior, cambié el código de la siguiente manera:

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

Como esperaba ajustar las coordinadas de texto es absoluto, pero debe estar dentro de los datos de facetas: enter image description here

NUEVA RESPUESTA: Con la introducción de ggplot2 v2.2.0 , position_stack() se puede utilizar para posicionar las tags sin la necesidad de calcular primero una variable de posición. El siguiente código le dará el mismo resultado que la respuesta anterior:

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

Para eliminar el centro “hueco”, adapte el código a:

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

ANTERIOR RESPUESTA: La solución a este problema es crear una variable de posición, que se puede hacer con bastante facilidad con la base R o con los paquetes data.table , plyr o dplyr :

Paso 1: Creando la variable de posición para cada canal

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

Paso 2: Creando el diagtwig facetado

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

El resultado:

enter image description here

Me gustaría hablar en contra de la forma convencional de hacer pasteles en ggplot2, que es dibujar una barra de barras astackdas en coordenadas polares. Si bien aprecio la elegancia matemática de ese enfoque, causa todo tipo de dolores de cabeza cuando la ttwig no se ve del modo que se supone. En particular, ajustar el tamaño del pastel puede ser difícil. (Si no sabe a qué me refiero, intente hacer un gráfico circular que se extienda hasta el borde del panel de trazado).

Prefiero dibujar pasteles en un sistema de coordenadas cartesianas normal, usando geom_arc_bar() desde ggforce. Se requiere un poco de trabajo extra en la parte delantera, porque tenemos que calcular los angularjs nosotros mismos, pero eso es fácil y el nivel de control que obtenemos como resultado es más que valioso. He usado este enfoque en respuestas anteriores aquí y aquí.

Los datos (de la pregunta):

 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) 

El código de dibujo de tarta:

 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

Para mostrar por qué creo que este enfoque es mucho más poderoso que el coord_polar() convencional ( coord_polar() ), digamos que queremos las tags en el exterior del gráfico en lugar de en el interior. Esto crea un par de problemas, como que tendremos que ajustar hjust y vjust dependiendo del lado del pastel en el que caiga una etiqueta, y también tendremos que hacer que el panel de trazado sea más ancho que alto para dejar espacio para las tags en el lateral sin generar espacio excesivo arriba y abajo. Resolver estos problemas en el enfoque de coordenadas polares no es divertido, pero es trivial en las coordenadas cartesianas:

 # 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_angle3*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

Para ajustar la posición del texto de la etiqueta en relación con la coordenada, puede usar los argumentos vjust e hjust de geom_text . Esto determinará la posición de todas las tags al mismo tiempo, por lo que es posible que esto no sea lo que necesita.

Alternativamente, podría modificar la coordenada de la etiqueta. Defina un nuevo data.frame donde promedia la coordenada Cnt ( label_x[i] = Cnt[i+1] + Cnt[i] ) para colocar la etiqueta en el centro de ese pie en particular. Simplemente pase este nuevo data.frame a geom_text en reemplazo del data.frame original.

Además, los diagtwigs tienen algunos defectos de interpretación visual. En general, no los usaría, especialmente cuando existen buenas alternativas, por ejemplo, un gráfico de puntos:

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

Por ejemplo, a partir de este gráfico, es obvio que Cnt es mayor para Kiosk que para Agent, esta información se pierde en el pie cartagtwig.

enter image description here

La siguiente respuesta es parcial, torpe y no la aceptaré. La esperanza es que solicitará una mejor solución.

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

Produce la siguiente tabla: enter image description here

Como ha notado, no puedo mover tags para verde (bajo).