scale_fill_manual to create a custom legend : appears to do nothing.

5,936 views
Skip to first unread message

hamamelis

unread,
Mar 9, 2012, 4:38:01 PM3/9/12
to ggp...@googlegroups.com
I am trying to create a custom legend. The following produces a legend with the labels called "red", "green" & "blue". I wish to replace them with my own labels like "No Risk Mean". scale_fill_manual appears to do nothing. What should I be doing?

Thanks for any help, Ian

Here is a description of my two Examples which follow the data preparation:

Example 1: produces a legend box, but scale_fill_manual does not change this to my values; it seems to do nothing.
Example 2 produces no legend and scale_manual_fill does nothing.
I need to use geom_line & geom_point to see the case that has only a single date entry (WellbeM, blue) which will produce no line without geom_point.

### Prepare and read Input Data #############################
DSString <-
"Weeks, NoRiskM, FunctM, WellbeM
1, 2, 2.5, 1
2, 1, 1.5,
3, 1.2, 1.7, "
DSString
DSTest <- read.csv( textConnection(DSString))
DSTest
# library(ggplot2)

### Example 1 uses plDSTest2 ##############################################
plDSTest2 <- ggplot(DSTest, aes(x=Weeks)) +
geom_point(aes(y = NoRiskM,colour="red")) +
geom_line(aes( y = NoRiskM,colour="red" )) +
geom_point(aes(y = FunctM,colour="green")) +
geom_line(aes(y = FunctM,colour="green")) +
geom_point(aes(y = WellbeM,colour="blue")) +
geom_line(aes(y = WellbeM,colour="blue"))

plDSTest2

plDSTest2 + scale_fill_manual(
#values=c("red", "green", "blue"),
name="Means ",
labels=c("No Risk Mean", "Function Mean", "Wellbeing Mean"),
breaks=c("NoRiskM", "FunctM", "WellbeM")
)

### Example 2 uses plDSTest1 ################################################
plDSTest1 <- ggplot(DSTest, aes(x=Weeks)) +
geom_point(colour="red",aes(y = NoRiskM)) +
geom_line(colour="red", aes( y = NoRiskM )) +
geom_point(colour="green",aes(y = FunctM)) +
geom_line(colour="green",aes(y = FunctM)) + ## colour = label for lines
geom_point(colour="blue",aes(y = WellbeM)) +
geom_line(colour="blue",aes(y = WellbeM))

plDSTest1

plDSTest1 + scale_fill_manual(
values=c("red", "green", "blue"),
name="Means ",
labels=c("No Risk Mean", "Function Mean", "Wellbeing Mean"),
breaks=c("NoRiskM", "FunctM", "WellbeM")
)

Dennis Murphy

unread,
Mar 10, 2012, 9:53:18 AM3/10/12
to hamamelis, ggp...@googlegroups.com
Hi:

Try this:

library('reshape2')
library('ggplot2')

### Starting from your input data frame DSTest...

DSm <- melt(DSTest, id = 'Weeks')
ggplot(DSm, aes(x = Weeks, y = value, colour = variable)) +
geom_point() + geom_line(aes(group = variable)) +
scale_colour_manual(breaks = levels(DSm$variable),
values = c('red', 'green', 'blue'))


Look at DSm carefully and observe that the reshape of your data from
wide format to long makes it easier to define a color aesthetic with
the factor 'variable' that can be applied to both points and lines
simultaneously. Then it's easy to apply scale_colour_manual to the
levels of the constructed factor variable. More inline.

What you're attempting to do here is to create a color aesthetic on
the fly, using red, green and blue as the names of the levels of the
factor being constructed with the multiple lines of code. However, you
then try to redefine the colors by using scale_fill_manual() rather
than scale_colour_manual(). That won't work. As someone who struggled
with scales when first learning ggplot2, I feel your pain...

Consult the examples on pp. 108-109 of the ggplot2 book to see how to
do this correctly; if you don't have a copy available, then the
relevant code from Chapter 6 is shown below. Take note of the
differences among the three graphs and make sure you understand why
they are different, because they illustrate the fundamental concepts
of mapping vs. setting a plot aesthetic. (Notice that only the
x-variable year is defined as an aesthetic in the ggplot() statement;
this is because the 'y' will vary in different calls to geom_line().
Aesthetics defined in ggplot() are expected to be the same in all
layers created by different geom calls.)

huron <- data.frame(year = 1875:1972, level = LakeHuron)
ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5), colour = "blue") +
geom_line(aes(y = level + 5), colour = "red")

ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5, colour = "below")) +
geom_line(aes(y = level + 5, colour = "above"))

ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5, colour = "below")) +
geom_line(aes(y = level + 5, colour = "above")) +
scale_colour_manual("Direction",
c("below" = "blue", "above" = "red"))

>
> plDSTest2
>
> plDSTest2 + scale_fill_manual(
> #values=c("red", "green", "blue"),
> name="Means ",
> labels=c("No Risk Mean", "Function Mean", "Wellbeing Mean"),
> breaks=c("NoRiskM", "FunctM", "WellbeM")
> )
>
> ### Example 2 uses plDSTest1
> ################################################
> plDSTest1 <- ggplot(DSTest, aes(x=Weeks)) +
> geom_point(colour="red",aes(y = NoRiskM)) +
> geom_line(colour="red", aes( y = NoRiskM )) +
> geom_point(colour="green",aes(y = FunctM)) +
> geom_line(colour="green",aes(y = FunctM)) + ## colour = label for lines
> geom_point(colour="blue",aes(y = WellbeM)) +
> geom_line(colour="blue",aes(y = WellbeM))

In this case, you're setting colors for each variable rather than
mapping them, so no legend will be created because legends are only
drawn from *mapped* aesthetics**. You get the graph you want, but not
the legend, and trying to create it with the ensuing code will be a
futile exercise.

The code from the book shows you how to correctly define a factor for
an aesthetic on the fly - note that the aesthetic used in the manual
scale must match the aesthetic you created (in your case, color rather
than fill). A simpler way to do things, once you know how, is to
reshape the data first. Quite often, rearranging the data prior to
invoking ggplot() or qplot() can save a lot of time and frustration.

** A single value associated with an aesthetic is said to be 'set';
this is done outside the aes() call inside ggplot() or a geom. A
variable associated with an aesthetic is said to be 'mapped' (i.e., a
1-1 mathematical relationship) and is defined inside an aes() call. In
order for a legend to be drawn, an aesthetic has to be mapped. This is
a fundamental concept in ggplot2 which has to be grasped; otherwise,
you're in for a lot of frustration.

Dennis


>
> plDSTest1
>
> plDSTest1 + scale_fill_manual(
> values=c("red", "green", "blue"),
> name="Means ",
> labels=c("No Risk Mean", "Function Mean", "Wellbeing Mean"),
> breaks=c("NoRiskM", "FunctM", "WellbeM")
> )
>

> --
> You received this message because you are subscribed to the ggplot2 mailing
> list.
> Please provide a reproducible example: http://gist.github.com/270442
>
> To post: email ggp...@googlegroups.com
> To unsubscribe: email ggplot2+u...@googlegroups.com
> More options: http://groups.google.com/group/ggplot2

hamamelis

unread,
Mar 10, 2012, 12:59:51 PM3/10/12
to ggp...@googlegroups.com, hamamelis
Thanks Dennis, that worked a treat, but with one question. How do I control the order of the legends?
It is important that I can set the order with "No Risk" at the top. I shall explore melt further for my data is much more complicated than the minimalist abstraction presented here.
Kind wishes for this support, Ian.
Here are my solutions:

DSString <-
"Weeks, NoRiskM, FunctM, WellbeM
1, 2, 2.5, 1
2, 1, 1.5,
3, 1.2, 1.7, "
DSString
DSTest <- read.csv( textConnection(DSString))
DSTest

library(ggplot2)
plDSTest2 <- ggplot(DSTest, aes(x=Weeks)) +
    geom_point(aes(y = NoRiskM,colour="No Risk")) + 
    geom_line(aes( y = NoRiskM,colour="No Risk" )) +  
    geom_point(aes(y = FunctM,colour="Function")) +
    geom_line(aes(y = FunctM,colour="Function")) + 
    geom_point(aes(y = WellbeM,colour="Wellbeing")) +
    geom_line(aes(y = WellbeM,colour="Wellbeing")) 
plDSTest2

plDSTest2 + scale_colour_manual(
    values=c("No Risk"="red", "Function"="green", "Wellbeing"="blue"),
    name="Means " )

### using melt ################################
library(reshape2)

DSm <- melt(DSTest, id = 'Weeks')
DSm

ggplot(DSm, aes(x = Weeks, y = value, colour = variable)) +
    geom_point() + geom_line(aes(group = variable)) +
    scale_colour_manual(breaks = levels(DSm$variable),
    values = c('red', 'green', 'blue'),
    name = "Means:")

Dennis Murphy

unread,
Mar 10, 2012, 6:06:38 PM3/10/12
to hamamelis, ggp...@googlegroups.com
Hi:

The simplest way is to melt the data, reorder the levels of the factor
variable in the melted data and then construct the ggplot.

Starting from DSString,

DSm <- melt(DSString, id = 'Weeks')
DSm$variable <- factor(DSm$variable, levels = c('NoRiskM', 'FunctM', 'WellbeM'))

...same ggplot() + ... code as before based on the melted data.

I wouldn't even try to order the factor levels within ggplot() using
your approach, but maybe someone else is willing to go there. AFAIK,
factors created in ggplot() are ordered lexicographically, but there
may be a trick of which I'm unaware. My suggestion would be to melt
the data; it gives you more control and the graphics code is cleaner,
less repetitive and easier to understand.

Dennis

hamamelis

unread,
Mar 11, 2012, 3:23:49 PM3/11/12
to ggp...@googlegroups.com
Dennis, Thanks indeed for your help. With your help, I have fathomed how to use melt, and I am so glad of your support. Here is what I was actually trying to do using facets to display a third variable NickN. I would like to hear any  any comments about how I have done this which might help me. I hope it will help others to see all this. Google seems to remove all my carefully placed spaces and tabs.
Kind wishes, Ian

## now with Clients NickN ####################################
DSString <-
"NickN, Weeks, NoRiskM, FunctM, WellbeM
\"Runner\", 1, 2.1, 2.5, 1.3
\"Runner\", 2, 1.5, 1.7
\"Runner\", 3, 1.2, 1.5,
\"Jumper\", 1, 1.9, 2.4, 1.1
\"Jumper\", 2, 1.5, 1.2
\"Jumper\", 3, 1.2, 1.7,
\"Walker\", 1, 2.3, 2.9, 1.2
\"Walker\", 2, 1.2, 1.3
\"Walker\", 3, 1.2, 1.2, "

DSString
DSTest <- read.csv( textConnection(DSString))
DSTest

library(reshape)
DSm <- melt(DSTest, id = c('Weeks','NickN'))
str(DSm)
DSm$variable <- factor(DSm$variable, levels = c('NoRiskM', 'FunctM', 'WellbeM'))
str(DSm)

library(ggplot2)

ggplot(DSm, aes(x = Weeks, y = value, colour = variable)) +
geom_point() + geom_line(aes(group = variable)) +
scale_colour_manual(breaks = levels(DSm$variable),
values = c('red', 'green', 'blue'),
labels=c("No Risk", "Function", "Wellbeing"),
name="Means ") +
opts( title="Means by Week by Nick Name" ) +
scale_x_continuous("Week Number", limits = c(0, 5)) + ##set horiz scale x
scale_y_continuous("Mean Score", limits = c(0, 3)) + ##sets vert scale y
facet_wrap(~NickN, ncol=3, drop=F) ## for each NickN, 3 cols, drop=F: do not drop missing


hamamelis

unread,
Mar 18, 2012, 3:36:20 PM3/18/12
to ggp...@googlegroups.com
I have finally worked out how to use scale_colour_manual, scale_linetype_manual and scale_shape_manual. These can be used either separately or in combination. The linetype & shape are useful for black and white illustration. Further, if all three are used together then people who have trouble seeing the full range of rgb colours can interpret the graphs easily. I hope this example will help others. 

To use the B&W version just delete or comment out the whole of the scale_colour_manual entry and remove "colour=variable" from aes(). Remember that each colour, linetype and shape manual function has to have a matching entry in aes(). No error is signalled if this is not done but the output will not be what you expect and the legends will probably not be correct.

You can miss out the facet_wrap as well but the graph will look odd because it has observations for three individuals.

How to use the shape (p196) , legends (p196) and colours (p102 on, p190) can be found in Hadley Wickham#s book "ggplot2". Details of how the palette of colours can be referenced is given in the example at the bottom of help page on palette {grDevices} in R on line documentation.

I am beholden to Dennis Murphy for his help with this. I hope this example will help others. 

Ian.

### Data preparation

DSString <-
"NickN,       Weeks,  NoRiskM,  FunctM,   WellbeM
\"Runner\",   1,      2.1,      2.5,      1.3
\"Runner\",   2,      1.5,      1.7
\"Runner\",   3,      1.2,      1.5,
\"Jumper\",   1,      1.9,      2.4,      1.1
\"Jumper\",   2,      1.5,      1.2
\"Jumper\",   3,      1.2,      1.7,
\"Walker\",   1,      2.3,      2.9,      1.2
\"Walker\",   2,      1.2,      1.3
\"Walker\",   3,      1.2,      1.2,          "
DSString
DSTest <- read.csv( textConnection(DSString))
DSTest

library(reshape)
DSm <- melt(DSTest, id = c('Weeks','NickN'))
DSm
DSm$variable <- factor(DSm$variable, levels = c('NoRiskM', 'FunctM', 'WellbeM')) # set the levels so that the legend.type appear in the correct order
library(ggplot2)

#### plot with  linetype, shape & colout ################################

ggplot(DSm, aes(x = Weeks, y = value,
                colour = variable,          # delete if no matching scale_colour_manual
                linetype = variable,        # delete if no matching scale_linetype_manual
                shape = variable)) +       # delete if no matching scale_shape_manual
    geom_point() + geom_line(aes(group = variable)) +  ## you can set the point (shape) size & line thickness here.
    scale_colour_manual(breaks = levels(DSm$variable),
                        values = c('red', 'green', 'blue'),   ## you can get better palettes than this, but it works.

                        labels=c("No Risk", "Function", "Wellbeing"),
                        name="Means ") +
    scale_linetype_manual(breaks = levels(DSm$variable),
                        values = 1:3,   ## change these to get the linetype you want

                        labels=c("No Risk", "Function", "Wellbeing"),
                        name="Means ") +
    scale_shape_manual(breaks = levels(DSm$variable),
                        values = 1:3,   ## change these to get the shapes you want
Reply all
Reply to author
Forward
0 new messages