Layered axes in ggplot?

前端 未结 2 441
旧时难觅i
旧时难觅i 2020-12-07 02:54

I\'m wondering if it\'s possible to make layered/segmented axes in GGLPOT2 (or another graphics package; I just prefer ggplot).

What I want to do is take the below

相关标签:
2条回答
  • 2020-12-07 03:23

    Even if there already a good answer, I want to add my solution. I always use this kind of visualization if I have 3 categorical variables and I do not want to use faceting or similar visualizations.

    This chart is produced by the following code, even if the code looks cluttered, I am already used to it :-)

    Basically I just use the geom_rect to draw my barchart

    enter image description here

    And here is my code

    library(data.table)
    library(ggplot2)
    

    the data

    set.seed(1234)
    data <- data.frame(
    animal = sample(c('bear','tiger','lion'), 50, replace=T),
    color = sample(c('black','brown','orange'), 50, replace=T),
    period = sample(c('first','second','third'), 50, replace=T),
    value = sample(1:100, 50, replace=T))
    

    just for convenience, I'm more familiar with the data.table as with the basic data.frame

    dt <- as.data.table(data)
    

    grouping the basic data

    groups <- c("period", "animal", "color")
    thevalue <- c("value")
    dt.grouped <- dt[,lapply(.SD, sum), by = groups, .SDcols = thevalue]
    

    the inner group

    xaxis.inner.member <- unique(dt.grouped$animal)
    xaxis.inner.count <- length(unique(xaxis.inner.member))
    xaxis.inner.id <- seq(1:xaxis.inner.count)
    setkey(dt.grouped, animal)
    dt.grouped <- dt.grouped[J(xaxis.inner.member, inner.id = xaxis.inner.id)]
    

    the outer group

    xaxis.outer.member <- unique(dt.grouped$period)
    xaxis.outer.count <- length(unique(xaxis.outer.member))
    xaxis.outer.id <- seq(1:xaxis.outer.count)
    setkey(dt.grouped, period)
    dt.grouped <- dt.grouped[J(xaxis.outer.member, outer.id = xaxis.outer.id)]
    

    charting parameters

    xaxis.outer.width <- 0.9
    xaxis.inner.width <- (xaxis.outer.width / xaxis.inner.count)
    xaxis.inner.width.adjust <- 0.01 / 2
    
    dt.ordered <- dt.grouped[order(outer.id,inner.id, color),]
    dt.ordered[,value.cum := cumsum(value), by = list(period, animal)]
    dt.ordered[,xmin := (outer.id - xaxis.outer.width / 2) + xaxis.inner.width * (inner.id - 1) +     xaxis.inner.width.adjust] 
    dt.ordered[,xmax := (outer.id - xaxis.outer.width / 2) + xaxis.inner.width * inner.id -    xaxis.inner.width.adjust]
    dt.ordered[,ymin := value.cum - value]
    dt.ordered[,ymax := value.cum]
    

    building the data.table for the text labels of the inner xaxis

    dt.text <- data.table(
    period = rep(xaxis.outer.member, each = xaxis.inner.count)
    ,animal = rep(xaxis.inner.member, times = xaxis.inner.count)
    )
    setkey(dt.text, animal)
    dt.text <- dt.text[J(xaxis.inner.member,inner.id = xaxis.inner.id),]
    setkey(dt.text, period)
    dt.text <- dt.text[J(xaxis.outer.member,outer.id = xaxis.outer.id),]
    dt.text[, xaxis.inner.label := animal]
    dt.text[, xaxis.inner.label.x := (outer.id - xaxis.outer.width / 2) + xaxis.inner.width * inner.id - (xaxis.inner.width / 2) ]
    

    the plotting starts here

    p <- ggplot()
    p <- p + geom_rect(data = dt.ordered,
    aes(
    ,x = period
    ,xmin = xmin
    ,xmax = xmax 
    ,ymin = ymin
    ,ymax = ymax
    ,fill = color)
    )
    

    adding the values as labels

    p <- p + geom_text(data = dt.ordered,
    aes(
    label = value
    ,x = (outer.id - xaxis.outer.width / 2) + xaxis.inner.width * inner.id - (xaxis.inner.width / 2)
    ,y = value.cum
    )
    ,colour = "black"
    ,vjust = 1.5               
    )
    

    adding the labels for the inner xaxis

    p <- p + geom_text(data = dt.text,
    aes(
    label = xaxis.inner.label
    ,x = xaxis.inner.label.x
    ,y = 0
    )
    ,colour = "darkgrey"
    ,vjust = 1.5   
    )
    

    finally plotting the chart

    p
    
    0 讨论(0)
  • 2020-12-07 03:32

    Two steps to doing this:

    1. Add the group=animal aesthetic to the plot (tell it to group by animal)

    2. Add position="dodge" to your geom_bar layer (tell it the bars should be separate)

    Thus:

    ggplot(data, aes(x=period, y=value, fill=color, group=animal, color=animal)) +
            geom_bar(stat="identity", position="dodge")
    

    This looks like:

    enter image description here

    One of the issues here is that it doesn't describe which animal is which: there isn't a particularly easy way to fix that. That's why I would probably make this plot through faceting:

    ggplot(data, aes(x=animal, y=value, fill=color)) + geom_bar(stat="identity") +
        facet_wrap(~ period)
    

    enter image description here

    0 讨论(0)
提交回复
热议问题