65

I have a data.frame, that is sorted from highest to lowest. For example:

x <- structure(list(variable = structure(c(10L, 6L, 3L, 4L, 2L, 8L, 
9L, 5L, 1L, 7L), .Label = c("a", "b", "c", "d", "e", "f", "g", 
"h", "i", "j"), class = c("ordered", "factor")), value = c(0.990683229813665, 
0.975155279503106, 0.928571428571429, 0.807453416149068, 0.717391304347826, 
0.388198757763975, 0.357142857142857, 0.201863354037267, 0.173913043478261, 
0.0496894409937888)), .Names = c("variable", "value"), row.names = c(10L, 
6L, 3L, 4L, 2L, 8L, 9L, 5L, 1L, 7L), class = "data.frame")

ggplot(x, aes(x=variable,y=value)) + geom_bar(stat="identity") + 
 scale_y_continuous("",label=scales::percent) + coord_flip() 

Now, the data is nice and sorted, but when I plot, it comes out sorted by factor. It's annoying, how do I fix it?

Brian Fisher
  • 1,305
  • 7
  • 17
Brandon Bertelsen
  • 43,807
  • 34
  • 160
  • 255
  • 1
    With R version 3.2.2, I get an error: `scale_y_continuous("", formatter = "percent") : unused argument (formatter = "percent")` – Iris Apr 05 '16 at 09:00
  • Yes, I beleive its `scale_y_continuos(labels=percent)` and you must also load the `scales` package. – Brandon Bertelsen Apr 05 '16 at 13:47
  • Then I have a new error `Error: stat_count() must not be used with a y aesthetic.` – Iris Apr 05 '16 at 13:52

5 Answers5

88

This seems to be what you're looking for:

g <- ggplot(x, aes(reorder(variable, value), value))
g + geom_bar() + scale_y_continuous(formatter="percent") + coord_flip()

The reorder() function will reorder your x axis items according to the value of variable.

slhck
  • 36,575
  • 28
  • 148
  • 201
djmuseR
  • 881
  • 5
  • 2
64

Here are a couple of ways.

The first will order things based on the order seen in the data frame:

x$variable <- factor(x$variable, levels=unique(as.character(x$variable)) )

The second orders the levels based on another variable (value in this case):

x <- transform(x, variable=reorder(variable, -value) ) 
Greg Snow
  • 48,497
  • 6
  • 83
  • 110
11

I've recently been struggling with a related issue, discussed at length here: Order of legend entries in ggplot2 barplots with coord_flip() .

As it happens, the reason I had a hard time explaining my issue clearly, involved the relation between (the order of) factors and coord_flip(), as seems to be the case here.

I get the desired result by adding + xlim(rev(levels(x$variable))) to the ggplot statement:

ggplot(x, aes(x=variable,y=value)) + geom_bar() + 
scale_y_continuous("",formatter="percent") + coord_flip() 
+  xlim(rev(levels(x$variable)))

This reverses the order of factors as found in the original data frame in the x-axis, which will become the y-axis with coord_flip(). Notice that in this particular example, the variable also happen to be in alphabetical order, but specifying an arbitrary order of levels within xlim() should work in general.

Community
  • 1
  • 1
MatteoS
  • 745
  • 2
  • 6
  • 17
4

I don't know why this question was reopened but here is a tidyverse option.

x %>% 
  arrange(desc(value)) %>%
  mutate(variable=fct_reorder(variable,value)) %>% 
ggplot(aes(variable,value,fill=variable)) + geom_bar(stat="identity") + 
  scale_y_continuous("",label=scales::percent) + coord_flip() 
NelsonGon
  • 13,015
  • 7
  • 27
  • 57
2

You need to make the x-factor into an ordered factor with the ordering you want, e.g

x <- data.frame("variable"=letters[1:5], "value"=rnorm(5)) ## example data
x <- x[with(x,order(-value)), ] ## Sorting
x$variable <- ordered(x$variable, levels=levels(x$variable)[unclass(x$variable)])

ggplot(x, aes(x=variable,y=value)) + geom_bar() +
   scale_y_continuous("",formatter="percent") + coord_flip()

I don't know any better way to do the ordering operation. What I have there will only work if there are no duplicate levels for x$variable.

zwol
  • 135,547
  • 38
  • 252
  • 361