Rótulos do eixo de múltiplas linhas com variables ​​de agrupamento aninhadas

Eu gostaria que os níveis de duas variables ​​de agrupamento aninhadas diferentes aparecessem em linhas separadas abaixo do gráfico, e não na legenda. O que eu tenho agora é esse código:

data <- read.table(text = "Group Category Value S1 A 73 S2 A 57 S1 B 7 S2 B 23 S1 C 51 S2 C 87", header = TRUE) ggplot(data = data, aes(x = Category, y = Value, fill = Group)) + geom_bar(position = 'dodge') + geom_text(aes(label = paste(Value, "%")), position = position_dodge(width = 0.9), vjust = -0.25) 

insira a descrição da imagem aqui

O que eu gostaria de ter é algo assim:

insira a descrição da imagem aqui

Alguma ideia?

Você pode criar uma function de elemento personalizado para axis.text.x

insira a descrição da imagem aqui

 library(ggplot2) library(grid) ## create some data with asymmetric fill aes to generalize solution data < - read.table(text = "Group Category Value S1 A 73 S2 A 57 S3 A 57 S4 A 57 S1 B 7 S2 B 23 S3 B 57 S1 C 51 S2 C 57 S3 C 87", header=TRUE) # user-level interface axis.groups = function(groups) { structure( list(groups=groups), ## inheritance since it should be a element_text class = c("element_custom","element_blank") ) } # returns a gTree with two children: # the categories axis # the groups axis element_grob.element_custom <- function(element, x,...) { cat <- list(...)[[1]] groups <- element$group ll <- by(data$Group,data$Category,I) tt <- as.numeric(x) grbs <- Map(function(z,t){ labs <- ll[[z]] vp = viewport( x = unit(t,'native'), height=unit(2,'line'), width=unit(diff(tt)[1],'native'), xscale=c(0,length(labs))) grid.rect(vp=vp) textGrob(labs,x= unit(seq_along(labs)-0.5, 'native'), y=unit(2,'line'), vp=vp) },cat,tt) gX <- textGrob(cat, x=x) gTree(children=gList(do.call(gList,grbs),gX), cl = "custom_axis") } ## # gTrees don't know their size grobHeight.custom_axis = heightDetails.custom_axis = function(x, ...) unit(3, "lines") ## the final plot call ggplot(data=data, aes(x=Category, y=Value, fill=Group)) + geom_bar(position = position_dodge(width=0.9),stat='identity') + geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9), vjust=-0.25)+ theme(axis.text.x = axis.groups(unique(data$Group)), legend.position="none") 

O argumento strip.position em facet_wrap() e o argumento switch em facet_grid() já que o ggplot2 2.2.0 agora torna a criação de uma versão simples deste gráfico bastante direta através do lapidação. Para dar uma aparência ininterrupta ao gráfico, defina o panel.spacing para 0.

Aqui está o exemplo usando o dataset com um número diferente de Grupos por Categoria da resposta do @ agtudy.

  • Eu usei scales = "free_x" para eliminar o Grupo extra das Categorias que não o possuem, embora isso nem sempre seja desejável.
  • O strip.position = "bottom" move os labels facetados para a parte inferior. Eu removi o fundo da tira todos juntos com o strip.background , mas pude ver que deixar o retângulo de faixa seria útil em algumas situações.
  • Eu usei width = 1 para fazer as barras dentro de cada toque de categoria – elas teriam espaços entre elas por padrão.

Eu também uso strip.placement e strip.background no theme para obter as tiras na parte inferior e remover o retângulo de faixa.

O código para versões do ggplot2_2.2.0 ou mais recente:

 ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + geom_bar(stat = "identity", width = 1) + geom_text(aes(label = paste(Value, "%")), vjust = -0.25) + facet_wrap(~Category, strip.position = "bottom", scales = "free_x") + theme(panel.spacing = unit(0, "lines"), strip.background = element_blank(), strip.placement = "outside") 

insira a descrição da imagem aqui

Você pode usar space= "free_x" em facet_grid() se quiser que todas as barras tenham a mesma largura, independentemente de quantos Grupos por Categoria. Observe que isso usa switch = "x" vez de strip.position . Você também pode querer alterar o label do eixo x; Eu não tinha certeza do que deveria ser, talvez Category em vez de Group?

 ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + geom_bar(stat = "identity", width = 1) + geom_text(aes(label = paste(Value, "%")), vjust = -0.25) + facet_grid(~Category, switch = "x", scales = "free_x", space = "free_x") + theme(panel.spacing = unit(0, "lines"), strip.background = element_blank(), strip.placement = "outside") + xlab("Category") 

insira a descrição da imagem aqui

Versões mais antigas de código

O código para ggplot2_2.0.0, quando este recurso foi introduzido pela primeira vez, foi um pouco diferente. Eu salvei abaixo para a posteridade:

 ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + geom_bar(stat = "identity") + geom_text(aes(label = paste(Value, "%")), vjust = -0.25) + facet_wrap(~Category, switch = "x", scales = "free_x") + theme(panel.margin = unit(0, "lines"), strip.background = element_blank()) 

Uma alternativa ao método do agstudy é editar a tabela e inserir um “eixo” calculado por ggplot2,

 p < - ggplot(data=data, aes(x=Category, y=Value, fill=Group)) + geom_bar(position = position_dodge(width=0.9),stat='identity') + geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9), vjust=-0.25) axis <- ggplot(data=data, aes(x=Category, y=Value, colour=Group)) + geom_text(aes(label=Group, y=0), position=position_dodge(width=0.9)) annotation <- gtable_filter(ggplotGrob(axis), "panel", trim=TRUE) annotation[["grobs"]][[1]][["children"]][c(1,3)] <- NULL #only keep textGrob library(gtable) g <- ggplotGrob(p) gtable_add_grobs <- gtable_add_grob # let's use this alias g <- gtable_add_rows(g, unit(1,"line"), pos=4) g <- gtable_add_grobs(g, annotation, t=5, b=5, l=4, r=4) grid.newpage() grid.draw(g) 

insira a descrição da imagem aqui

Uma solução muito simples que dá um resultado semelhante (embora não idêntico) é usar lapidação. A desvantagem é que o marcador de categoria está acima e não abaixo.

 ggplot(data=data, aes(x=Group, y=Value, fill=Group)) + geom_bar(position = 'dodge', stat="identity") + geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9), vjust=-0.25) + facet_grid(. ~ Category) + theme(legend.position="none") 

Usando lapidação para fornecer rótulo secundário

@agstudy já respondeu a esta pergunta e eu vou usá-lo sozinho, mas se você aceitar algo mais feio, mas mais simples, é com isso que eu cheguei antes de sua resposta:

 data < - read.table(text = "Group Category Value S1 A 73 S2 A 57 S1 B 7 S2 B 23 S1 C 51 S2 C 87", header=TRUE) p <- ggplot(data=data, aes(x=Category, y=Value, fill=Group)) p + geom_bar(position = 'dodge') + geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9), vjust=-0.25) + geom_text(colour="darkgray", aes(y=-3, label=Group), position=position_dodge(width=0.9), col=gray) + theme(legend.position = "none", panel.background=element_blank(), axis.line = element_line(colour = "black"), axis.line.x = element_line(colour = "white"), axis.ticks.x = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank()) + annotate("segment", x = 0, xend = Inf, y = 0, yend = 0) 

O que nos dará:

insira a descrição da imagem aqui