3

I want to create grouped bar plot while keeping order. If it was single column and not a grouped bar plot use of reorder function is obvious. But not sure how to use it on a melted data.frame.

Here is the detail explanation with code example:

Lets say we have following data.frame:

d.nfl <- data.frame(Team1=c("Vikings", "Chicago", "GreenBay", "Detroit"), Win=c(20, 13, 9, 12))

plotting a simple bar plot while flipping it.

ggplot(d.nfl, aes(x = Team1, y=Win)) + geom_bar(aes(fill=Team1), stat="identity") + coord_flip()

above plot will not have an order and if I want to order the plot by win I can do following:

d.nfl$orderedTeam <- reorder(d.nfl$Team1, d.nfl$Win)
ggplot(d.nfl, aes(x = orderedTeam, y=Win)) + geom_bar(aes(fill=orderedTeam), stat="identity") + coord_flip()

Now lets say we add another column (to original data frame)

d.nfl$points <- c(12, 3, 45, 5)

     Team1 Win points
1  Vikings  20     12
2  Chicago  13      3
3 GreenBay   9     45
4  Detroit  12      5

to generate grouped bar plot, first we need to melt it:

library(reshape2)
> d.nfl.melt <- melt(d.nfl[,c('Team1','Win','points')],id.vars = 1)
> ggplot(d.nfl.melt,aes(x = Team1,y = value)) + geom_bar(aes(fill = variable),position = "dodge", stat="identity") + coord_flip()

enter image description here

above ggplot is unordered.

but how I do ordered group bar plot (ascending manner)

oshun
  • 2,319
  • 18
  • 32
add-semi-colons
  • 18,094
  • 55
  • 145
  • 232
  • 2
    You reordered the factor levels the first time, and it worked. Maybe try setting the factor level order on the melted data frame...? – joran May 02 '16 at 16:11
  • 1
    Possible duplicate of [Order Bars in ggplot2 bar graph](http://stackoverflow.com/questions/5208679/order-bars-in-ggplot2-bar-graph) – alistaire May 02 '16 at 16:32
  • Something related [here](http://stackoverflow.com/questions/1721536/changing-the-order-of-dodged-bars-in-ggplot2-barplot). With stacking, you just need to order the dataset before plotting but that doesn't appear to work for dodging. – aosmith May 02 '16 at 17:06
  • @joran tried didn't work: `> melted.theTable <- within(d.nfl.melt, Team1 <- factor(Team1, levels = names(sort(table(Team1), decreasing=FALSE)))) > ggplot(melted.theTable,aes(x = Team1,y = value)) + geom_bar(aes(fill = variable),position = "dodge", stat="identity") + coord_flip()` – add-semi-colons May 02 '16 at 19:22
  • The reason I suggested it was because when I tried it, it did work. – joran May 02 '16 at 19:23
  • ...only other suggestion I have is to order the data frame itself, but I don't think that should matter in this case. – joran May 02 '16 at 19:28
  • 1
    Recheck your attempt to set the level order; it is wrong. – joran May 02 '16 at 20:04

2 Answers2

4

This is a non-issue.

The easiest way is to not discard your ordered team in the melt:

d.nfl.melt <- melt(d.nfl,id.vars = c("Team1", "orderedTeam"))

Alternatively, we can use reorder after melting and just only use the Win elements in computing the ordering:

d.nfl.melt$ordered_after_melting = reorder(
    d.nfl.melt$Team1,
    X = d.nfl.melt$value * (d.nfl.melt$variable == "Win")
)

Yet another idea is to take the levels from the original ordered column and apply them to a melted factor:

d.nfl.melt$copied_levels = factor(
    d.nfl.melt$Team1,
    levels = levels(d.nfl$orderedTeam)
)

All three methods give the same result. (I left out the coord_flips because they don't add anything to the question, but you can of course add them back in.)

gridExtra::grid.arrange(
    ggplot(d.nfl.melt,aes(x = orderedTeam, y = value)) + 
        geom_bar(aes(fill = variable),position = "dodge", stat="identity"),
    ggplot(d.nfl.melt,aes(x = ordered_after_melting, y = value)) + 
        geom_bar(aes(fill = variable),position = "dodge", stat="identity"),
    ggplot(d.nfl.melt,aes(x = copied_levels, y = value)) + 
        geom_bar(aes(fill = variable),position = "dodge", stat="identity")
)

enter image description here

As to the easiest, I would recommend just keeping the orderedTeam variable around while melting. Your code seems to work hard to leave it out, it's quite easy to keep it in.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
3

The challenge your question presents is how to reorder a factor Team1 based on a subset values in a melted column.

The comments to your question from @alistaire and @joran link to great answers. The tl;dr answer is to just apply the ordering from your original, unmelted data.frame to the new one using levels().

library(reshape2)
#Picking up from your example code:
d.nfl.melt <- melt(d.nfl[,c('Team1','Win','points')],id.vars = 1)
levels(d.nfl.melt$Team1)
#Current order is alphabetical
#[1] "Chicago"  "Detroit"  "GreenBay" "Vikings" 

#Reorder based on Wins (using the same order from your earlier, unmelted data.frame)
d.nfl.melt$Team1 <- factor(d.nfl.melt$Team1, levels = levels(d.nfl$orderedTeam)) #SOLUTION
levels(d.nfl.melt$Team1)
#New order is ascending by wins
#[1] "GreenBay" "Detroit"  "Chicago"  "Vikings" 

ggplot(d.nfl.melt,aes(x = Team1,y = value)) + 
  geom_bar(aes(fill = variable),position = "dodge", stat="identity") + coord_flip()

enter image description here

oshun
  • 2,319
  • 18
  • 32