CH_A_M
CH_A_M

Reputation: 33

How to rearrange legend order in ggplot

I am plotting the graph in ggplot. Everything was fine but now I have a problem with the legend. i want to rearrange the legend. The legend that I am getting now is

0-63

100-200

200-400

63-100

what I want is. I want to move the last value to the 2nd position. It should be like that

0-63

63-100

100-200

200-400

my code is below

my data look like this but up to 1000 rows

time  less63  63_100    100_200   200_400
06:01   0       4        3           1
06:02   2       6        6           5
06:03   4       8        8           6
06:04   6       9        7           8
06:05   7       10       8           7
06:06   3       11       3           7
06:07   6       7        2           2
06:08   7       3        0           3





ggplot(df)+
      geom_area(aes(x=time,y=less63,fill="0-63"),alpha=0.5)+
      geom_area(aes(x=time,y=63_100,fill="63-100"),alpha=0.5)+
      geom_area(aes(x=time,y=100_200,fill="100-200"),alpha=0.5)+
      geom_area(aes(x=time,y=200_400,fill="200-400"),alpha=0.5)+
      scale_colour_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))

I shall be really thankful in this regard

Upvotes: 1

Views: 8174

Answers (3)

chemdork123
chemdork123

Reputation: 13863

The first thing you want to do here is put your data into Tidy Data format. Notice how you are specifying a geom_area() function for every fill color in your dataset and naming them manually? The philosophy behind grammar of graphics upon which ggplot2 was built is that you can use the mapping function aes() to let ggplot2 define the separation and how fill= is applied by the observations in your dataset. We'll do that here, then demonstrate a few other things that will get your plot working... as well as finally making your legend in the proper order.

Tidying the Data

To tidy the data, you need to gather the columns except for time into 2 new columns: one to specify the range (i.e. "0-63" or "100-200") which will be mapped to the fill aesthetic, and one to specify the value that will be mapped to the y aesthetic. As the data stands in df_original, you have the values spread across multiple columns (they should be in one column) and the names of those columns should really be gathered into a separate column. The result of doing this is called "gathering" or making your data "long".

Here, we'll use pivot_longer():

library(ggplot2)
library(dplyr)
library(tidyr)
library(scales)

df_original <- read.table(text="time  less63  63_100    100_200   200_400
06:01   0       4        3           1
06:02   2       6        6           5
06:03   4       8        8           6
06:04   6       9        7           8
06:05   7       10       8           7
06:06   3       11       3           7
06:07   6       7        2           2
06:08   7       3        0           3", header=TRUE)

df <- df_original %>%
  pivot_longer(
    cols = -time,   # take everything except time column
    names_to = "ranges",
    values_to = "val"
  ) %>%
  mutate(ranges = factor(
    ranges,
    levels=c("less63", "X63_100", "X100_200", "X200_400"),
    labels=c("0-63", "63-100", "100-200", "200-400")))

Then you want to change the df$range column to a factor. Here you can specify the order of the ranges using the levels= argument, and while we're at it you can specify how they look (renaming) by the labels= argument:

df <- df %>%
  mutate(ranges = factor(
    ranges,
    levels=c("less63", "X63_100", "X100_200", "X200_400"),
    labels=c("0-63", "63-100", "100-200", "200-400")))

The last step is converting your df$time column to a POSIXct object, which means we can reference it as a time object. You can look at the help for strptime() to see what the string for format= is referencing:

# OP states time is in hours:minutes, so reflected here
df$time <- as.POSIXct(df$time, format="%H:%M")

Plotting

Plotting is pretty simple now. Since we tidied up the data, we can just use one geom_area() call. Also, we reordered the factor levels for range, so the order of objects in the legend is set the way it should be and looks the way it should be thanks to the labels= argument.

One more thing: you need to reference scale_fill_manual() not scale_colour_manual(), since the aesthetic that is mapped in geom_area() is the fill:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
  geom_area(alpha=0.5) +
  scale_x_time(labels = scales::time_format(format = "%H:%M")) +
  scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))

enter image description here

Update: Overlapping Plot

The OP indicated they were creating an overlapping plot. By default, the position of the layers mapped to fill in geom_are() is set to "stacked", which means the y values are stacked on top of one another. This makes it easy to view and see the way the areas change over the x axis. However, OP wanted to prepare a plot where the y values are... the value. This would be position="identity" and creates overlapping areas. You can see the direct effect here:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
  geom_area(alpha=0.2, position="identity") +
  scale_x_time(labels = scales::time_format(format = "%M:%S")) +
  scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))

enter image description here

You can see that even cutting down the alpha= value, this is going to be hard to discern what's going on. If you want to see this information more clearly, I would recommend using horizontally-placed facets as a better viewing option:

ggplot(df, aes(x=time, y=val, fill=ranges)) +
  geom_area(alpha=0.2, position="identity") +
  scale_x_time(labels = scales::time_format(format = "%M:%S")) +
  scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue")) +
  facet_grid(ranges ~ .)

enter image description here

Upvotes: 5

TarJae
TarJae

Reputation: 79246

  1. Bring your data in long format with pivot_longer
  2. transform name to factor (level and label it)
  3. In ggplot use fill and group

Custom OP:

library(ggplot2)
library(dplyr)
library(tidyr)

df %>% 
  pivot_longer(
    -time
  ) %>% 
  mutate(name = factor(name, levels = c("less63", "X63_100", "X100_200", "X200_400"),
                       labels = c("0-63","63-100","100-200","200-400"))) %>% 
  ggplot(aes(x=factor(time), y=value, fill=name, group=name))+
  geom_area(position = "identity", alpha=0.5) +
  scale_fill_manual(name="Legend", values = c("0-63" = "red","63-100" = "green","100-200" = "black","200-400" = "blue"))


enter image description here General approach:

library(ggplot2)
library(dplyr)
library(tidyr)

df %>% 
  pivot_longer(
    -time
  ) %>% 
  mutate(name = factor(name, levels = c("less63", "X63_100", "X100_200", "X200_400"))) %>% 
  ggplot(aes(x=time, y=value, fill=name, group=name))+
  geom_area()

enter image description here

Upvotes: 1

stomper
stomper

Reputation: 1385

I suggest using tidyr::pivot_longer() on your data so that it looks like this.

time variable value 1 06:01 less63 0 2 06:01 63_100 4

etc...

Then use ordered() to set "variable" as an ordered factor. Then use geom_area(aes(x = time, y = variable, full = variable)

Upvotes: 1

Related Questions