This question already has answers here:
How to set multiple legends / scales for the same aesthetic in ggplot2?
(2 answers)
Closed last year.
I would like to combine a colour scale gradient for the points on a scatter plot together with a colour scale gradient for some text that goes on the plot. I can do them separately as shown in my example below, but i can't seem to put them together...is there a way of doing this?
Here is my example code of the two types of plots (p and p1) that I want to combine
l <- data.frame(prev=rnorm(1266),
aft=rnorm(1266),
day=as.factor(wday(sample(c(2:6),1266,replace=TRUE),abbr=TRUE, label=TRUE)),
month=as.factor(month(Sys.Date()+months(sample(0:11,1266,replace=TRUE)),abbr=TRUE, label=TRUE)),
ind=c(1:1266))
cors <- ddply(l, c("month", "day"), summarise, cor = round(cor(prev, aft), 3))
# below the text gains the colour gradient
p <- ggplot(l, aes(x=prev, y=aft)) +
geom_point() +
scale_colour_gradient(low = "red", high="blue")+
facet_grid(day~month, scales="free_x")+
geom_text(data=cors,aes(label=paste("r= ",cor,sep=""), size=abs(cor), colour=cor), x=Inf, y=Inf, vjust=1, hjust=1, show_guide=FALSE)+
geom_hline(aes(yintercept=0))+
geom_smooth(method="loess")
p
# below the points gain the colour gradient
p1 <- ggplot(l, aes(x=prev, y=aft)) +
geom_point(aes(colour=ind)) +
scale_colour_gradient("gray")+
facet_grid(day~month, scales="free_x")+
geom_text(data=cors,aes(label=paste("r= ",cor,sep=""), size=abs(cor), colour=cor), x=Inf, y=Inf, vjust=1, hjust=1, show_guide=FALSE)+
geom_hline(aes(yintercept=0))+
opts(legend.position="none") +
geom_smooth(method="loess")
p1
I do not expect that this can be done. A plot only has one scale per aesthetic. I believe that if you add multiple scale_color's, the second will overwrite the first. I think Hadley created this behavior on purpose, within a plot the mapping from data to a scale in the plot, e.g. color, is unique. This ensures that all color in the plot can be compared easily, because they share the same scale_color.
Related
I plotted a grouped boxplot and trying to change the background color for each panel. I can use panel.background function to change whole plot background. But how this can be done for individual panel? I found a similar question here. But I failed to adopt the code to my plot.
Top few lines of my input data look like
Code
p<-ggplot(df, aes(x=Genotype, y=Length, fill=Treatment)) + scale_fill_manual(values=c("#69b3a2", "#CF7737"))+
geom_boxplot(width=2.5)+ theme(text = element_text(size=20),panel.spacing.x=unit(0.4, "lines"),
axis.title.x=element_blank(),axis.text.x=element_blank(),axis.ticks.x=element_blank(),axis.text.y = element_text(angle=90, hjust=1,colour="black")) +
labs(x = "Genotype", y = "Petal length (cm)")+
facet_grid(~divide,scales = "free", space = "free")
p+theme(panel.background = element_rect(fill = "#F6F8F9", colour = "#E7ECF1"))
Unfortunately, like the other theme elements, the fill aesthetic of element_rect() cannot be mapped to data. You cannot just send a vector of colors to fill either (create your own mapping of sorts). In the end, the simplest solution probably is going to be very similar to the answer you linked to in your question... with a bit of a twist here.
I'll use mtcars as an example. Note that I'm converting some of the continuous variables in the dataset to factors so that we can create some more discrete values.
It's important to note, the rect geom is drawn before the boxplot geom, to ensure the boxplot appears on top of the rect.
ggplot(mtcars, aes(factor(carb), disp)) +
geom_rect(
aes(fill=factor(carb)), alpha=0.5,
xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
geom_boxplot() +
facet_grid(~factor(carb), scales='free_x') +
theme_bw()
All done... but not quite. Something is wrong and you might notice this if you pay attention to the boxes on the legend and the gridlines in the plot panels. It looks like the alpha value is incorrect for some facets and okay for others. What's going on here?
Well, this has to do with how geom_rect works. It's drawing a box on each plot panel, but just like the other geoms, it's mapped to the data. Even though the x and y aesthetics for the geom_rect are actually not used to draw the rectangle, they are used to indicate how many of each rectangle are drawn. This means that the number of rectangles drawn in each facet corresponds to the number of lines in the dataset which exist for that facet. If 3 observations exist, 3 rectangles are drawn. If 20 observations exist for one facet, 20 rectangles are drawn, etc.
So, the fix is to supply a dataframe that contains one observation each for every facet. We have to then make sure that we supply any and all other aesthetics (x and y here) that are included in the ggplot call, or we will get an error indicating ggplot cannot "find" that particular column. Remember, even if geom_rect doesn't use these for drawing, they are used to determine how many observations exist (and therefore how many to draw).
rect_df <- data.frame(carb=unique(mtcars$carb)) # supply one of each type of carb
# have to give something to disp
rect_df$disp <- 0
ggplot(mtcars, aes(factor(carb), disp)) +
geom_rect(
data=rect_df,
aes(fill=factor(carb)), alpha=0.5,
xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
geom_boxplot() +
facet_grid(~factor(carb), scales='free_x') +
theme_bw()
That's better.
I am trying to generate density plot with two overlaid distributions using ggplot2. My data looks like:
diag_elements <- data.frame(x = c(diag(Am.dent), diag(Am.flint)),
group=rep(c("Dent", "Flint"), c(length(diag(Am.dent)), length(diag(Am.flint)))))
And my call to ggplot is:
ggplot(diag_elements) +
geom_density(aes(x=x, colour=group, fill=group), alpha=0.5) +
labs(x = "Diagonal elements of the matrix", y = "Density", fill = "Heterotic Group") +
theme(legend.position = c(0.85, .75))
However, instead of simply renaming the legend with the more complete name specified in fill, this generates a second legend:
Does anyone have any suggestions for getting this same graph, but without the improperly formatted legend?
Thanks!
The other option is guides which allows specific removal of certain legneds. You simply add to your ggplot
+guides(color=FALSE)
This question already has answers here:
ggplot2: Adjust the symbol size in legends
(5 answers)
Closed 8 years ago.
For scatterplots with many points, one common technique is to reduce the size of the points and to make them transparent.
library(ggplot2)
ggplot(diamonds, aes(x, y, colour = cut)) +
geom_point(alpha = 0.25, size = 0.5) +
ylim(0, 12)
Unfortunately, the points in the legend are now too small and faint to see properly.
I would like a way to change the points in the legend independently of the plots in the main plot panel. It ought to be one of the setting contained in:
thm <- theme_get()
thm[grepl("legend", names(thm))]
I'm struggling to find the appropriate setting though. How do I change the point size?
You can use the function guide_legend() in package scales to achieve your effect.
This function allows you to override the aes values of the guides (legends) in your plot. In your case you want to override both alpha and size values of the colour scale.
Try this:
library(ggplot2)
library(scales)
ggplot(diamonds, aes(x, y, colour = cut)) +
geom_point(alpha = 0.25, size = 1) +
ylim(0, 12) +
guides(colour=guide_legend(override.aes=list(alpha=1, size=3)))
If you need to change formatting only in the legend you should use override.aes= and size= in guide_legend (see below). This will override size used in plot and will use new size value just for legend.
To get points in legend and lines in plot workaround would be add geom_point(size=0) to ensure that points are invisible and then in guides() set linetype=0 to remove lines and size=3 to get larger points.
ggplot(iris,aes(Petal.Width,Petal.Length,color=Species))+geom_line()+theme_bw()+
geom_point(size=0)+
guides(colour = guide_legend(override.aes = list(size=3,linetype=0)))
I'm having two different problems with specifying the colors in my legends in ggplot. I've tried to make a simplified examples that shows my problem:
df <- data.frame(x=rep(1:9, 10), y=as.vector(t(aaply(1:10, 1, .fun=function(x){x:(x+8)}))), method=factor(rep(1:9, each=10)), DE=factor(rep(1:9, each=10)))
ggplot(df, aes(x, y, color=method, group=DE, linetype=DE)) + geom_smooth(stat="identity")
For some reason, the line types shown in the legend under the title DE are all blue. I'd like them to be black, but I have no idea why they're blue in the first place, so I'm not sure how to change them.
For my other problem, I'm trying to use both point color and point shape to show two different distinctions in my data. I'd like to have legends for both of these. Here's what I have:
classifiers <- c("KNN", "RF", "NB", "LR", "Tree")
des <- c("Uniform", "Gaussian", "KDE")
withoutDE <- c(.735, .710, .706, .628, .614, .720, .713, .532, .523, .557, .677, .641, .398, .507, .538)
withDE <- c(.769, .762, .758, .702, .707, .752, .745, .655, .721, .733, .775, .772, .749, .756, .759)
df <- data.frame(WithoutDE=withoutDE, WithDE=withDE, DE=rep(des, each=5), Classifier=rep(classifiers, 3))
df <- cbind(df, Method=paste(df$DE, df$Classifier, sep=""))
ggplot() + geom_point(data=df, aes(x=WithoutDE, y=WithDE, shape=Classifier, fill=DE), size=3) + ylim(0,1) + xlim(0,1) + xlab("AUC without DE") + ylab("AUC with DE") + scale_shape_manual(values=21:25) + scale_fill_manual(values=c("pink", "blue", "white"), labels=c("Uniform", "KDE", "Gaussian")) + theme(legend.position=c(.85,.3))
If I change the color to change as well as the fill (by putting color=DE into the aes), then those are visible in the legend. I like having the black border around the points, though. I'd just like to have the inside of the points in the legend reflect the point fill in the plot. (I'd also like to position the two legends side-by-side, but I really just want to get the color to work right now)
I've spent way too long googling about both of these problems and trying various solutions without any success. Does anyone have any idea what I'm doing wrong?
For question 1:
Give the legend for line type and the legend for colour the same name.
ggplot(df, aes(x, y, color=method, group=DE, linetype=DE)) +
geom_smooth(stat="identity") +
scale_color_discrete("Line") +
scale_linetype_discrete("Line")
For question 2:
I do not think your fills are matching your data. You should assign the name of the value to each colour in the scale_x_manual calls.
I couldn't get the black border for the points. Here is what I was able to get, though:
ggplot() +
geom_point(data=df, aes(x=WithoutDE, y=WithDE, shape=Classifier,
fill=DE, colour=DE), size=3) +
ylim(0,1) + xlim(0,1) +
xlab("AUC without DE") +
ylab("AUC with DE") +
scale_shape_manual(values=21:25) +
scale_fill_manual(values=c("Uniform"="pink", "KDE"="blue", "Gaussian"="white"),
guide="none") +
scale_colour_manual(values=c("Uniform"="pink", "KDE"="blue", "Gaussian"="white"),
labels=c("Uniform", "KDE", "Gaussian")) +
theme(legend.position=c(.85,.3))
I don't know if you can control the point type inside the legends. Maybe someone else with more knowledge of ggplot2 can figure it out.
I'd like to use ggplot2's stat_binhex() to simultaneously plot two independent variables on the same chart, each with its own color gradient using scale_colour_gradientn().
If we disregard the fact that the x-axis units do not match, a reproducible example would be to plot the following in the same image while maintaining separate fill gradients.
d <- ggplot(diamonds, aes(x=carat,y=price))+
stat_binhex(colour="white",na.rm=TRUE)+
scale_fill_gradientn(colours=c("white","blue"),name = "Frequency",na.value=NA)
try(ggsave(plot=d,filename=<some file>,height=6,width=8))
d <- ggplot(diamonds, aes(x=depth,y=price))+
stat_binhex(colour="white",na.rm=TRUE)+
scale_fill_gradientn(colours=c("yellow","black"),name = "Frequency",na.value=NA)
try(ggsave(plot=d,filename=<some other file>,height=6,width=8))
I found some conversation of a related issue in ggplot2 google groups here.
Here is another possible solution: I have taken #mnel's idea of mapping bin count to alpha transparency, and I have transformed the x-variables so they can be plotted on the same axes.
library(ggplot2)
# Transforms range of data to 0, 1.
rangeTransform = function(x) (x - min(x)) / (max(x) - min(x))
dat = diamonds
dat$norm_carat = rangeTransform(dat$carat)
dat$norm_depth = rangeTransform(dat$depth)
p1 = ggplot(data=dat) +
theme_bw() +
stat_binhex(aes(x=norm_carat, y=price, alpha=..count..), fill="#002BFF") +
stat_binhex(aes(x=norm_depth, y=price, alpha=..count..), fill="#FFD500") +
guides(fill=FALSE, alpha=FALSE) +
xlab("Range Transformed Units")
ggsave(plot=p1, filename="plot_1.png", height=5, width=5)
Thoughts:
I tried (and failed) to display a sensible color/alpha legend. Seems tricky, but should be possible given all the legend-customization features of ggplot2.
X-axis unit labeling needs some kind of solution. Plotting two sets of units on one axis is frowned upon by many, and ggplot2 has no such feature.
Interpretation of cells with overlapping colors seems clear enough in this example, but could get very messy depending on the datasets used, and the chosen colors.
If the two colors are additive complements, then wherever they overlap equally you will see a neutral gray. Where the overlap is unequal, the gray would shift to more yellow, or more blue. My colors are not quite complements, judging by the slightly pink hue of the gray overlap cells.
I think what you want goes against the principles of ggplot2 and the grammar of graphics approach more generally. Until the issue is addressed (for which I would not hold my breath), you have a couple of choices
Use facet_wrap and alpha
This is will not produce a nice legend, but takes you someway to what you want.
You can set the alpha value to scale by the computed Frequency, accessed by ..Frequency..
I don't think you can merge the legends nicely though.
library(reshape2)
# in long format
dm <- melt(diamonds, measure.var = c('depth','carat'))
ggplot(dm, aes(y = price, fill = variable, x = value)) +
facet_wrap(~variable, ncol = 1, scales = 'free_x') +
stat_binhex(aes(alpha = ..count..), colour = 'grey80') +
scale_alpha(name = 'Frequency', range = c(0,1)) +
theme_bw() +
scale_fill_manual('Variable', values = setNames(c('darkblue','yellow4'), c('depth','carat')))
Use gridExtra with grid.arrange or arrangeGrob
You can create separate plots and use gridExtra::grid.arrange to arrange on a single image.
d_carat <- ggplot(diamonds, aes(x=carat,y=price))+
stat_binhex(colour="white",na.rm=TRUE)+
scale_fill_gradientn(colours=c("white","blue"),name = "Frequency",na.value=NA)
d_depth <- ggplot(diamonds, aes(x=depth,y=price))+
stat_binhex(colour="white",na.rm=TRUE)+
scale_fill_gradientn(colours=c("yellow","black"),name = "Frequency",na.value=NA)
library(gridExtra)
grid.arrange(d_carat, d_depth, ncol =1)
If you want this to work with ggsave (thanks to #bdemarest comment below and #baptiste)
replace grid.arrange with arrangeGrob something like.
ggsave(plot=arrangeGrob(d_carat, d_depth, ncol=1), filename="plot_2.pdf", height=12, width=8)