So I know many people have asked similar questions but the code others have used does not seem to be working for my graph hence why I'm wondering if I have done something wrong.
I have this code:
ggplot(dfMonth)
+ geom_col(aes(x=Month, y=NumberMO), size=.7, colour="black", fill="white")
+ geom_line(aes(x=Month, y=NumberME), size=1, colour="black", group=1)
+ xlab("Month")
+ ylab("No. of birds observed")
+ theme_bw()
+ geom_point(x=Month, y=NumberME)
+ scale_colour_manual("" ,values =c("NumberME"="black"), labels=c("Expected No. of birds"))
+ theme(legend.key=element_blank(),legend.title=element_blank(), legend.box="horizontal")
+ theme(axis.title.x = element_text(margin = unit(c(5, 0, 0, 0), "mm")),
axis.title.y = element_text(margin = unit(c(0,3 , 0, 0), "mm")))
Which produces this graph:
so as you can see, the legend to show what the black line with the points mean has not been added to my graph even though I have inputted the code. No error comes up so hence why I'm lost on whats wrong. Any ideas on what i've failed to include?
Thanks
In order for ggplot to know to draw a legend, you need to include one of the aesthetics for a geom within aes(). In this case, if you want a legend to be drawn for your line, you need to include within the aes() in the geom_line() call one of the aesthetics that you have identified for the line: linetype or color works. We'll use color here.
Oh... and in the absence of OP sharing their dataset, here's a made-up example:
set.seed(1234)
dfMonth <- data.frame(
Month=month.name,
NumberMO=sample(50:380, 12),
NumberME=sample(50:380, 12)
)
Now the code to make the plot and ensure the legend is created.
p <- ggplot(dfMonth, aes(x=Month)) +
geom_col(aes(y=NumberMO), size=0.7, color="black", fill="white") +
geom_line(aes(y=NumberME, color='black'), size=1, group=1)
p
We have a legend, but there's some problems. You get the default title of the legend (which is the name of the aesthetic), and the default label (which is whatever text you put inside aes(color=.... Since we put "black" as the value there, it's applied as the label, and not the actual color. The actual color of the line is to default to the first level of the standard colorset used by ggplot2, which in this case is that light red color.
To set the color, name of the legend, and name of the label, we should specify the value. There's only one item in the legend, so there's no need to specify, but if you were to send a named vector to indicate the name for our single line explicitly, you end up with the somewhat strange-looking c('black'='black'). I also included a line break in the label name to make the look a bit better. Also, the months were running into each other, so I also changed the angle of the x axis labels.
Finally, you might notice the months were out of order. That's because default ggplot2 behavior is to factor a column of discrete values, which uses alphabetical ordering for the levels. To fix that, you specify the column as a factor before plotting with the correct levels.
dfMonth$Month <- factor(dfMonth$Month, levels=month.name)
p + scale_color_manual(
name=NULL, values=c('black'='black'),
labels='Expected No.\nof birds') +
theme(axis.text.x=element_text(angle=30, hjust=1))
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 would like to customize the legend of the above plot. Basically, I would like the x values of the dashed lines to define specific items in the legend. For example the dashed line at time step "11:00" is coloured by "#00CC66" (green colour). I would like to have in the legend an 'X' colored by green color (eg. "#00CC66"). Another example, the dashed line at time step "12:00" is colored by"#FF3333" (red color).I would like to have in the legend an 'X' colored by red color. (eg."#FF3333")
OP. What you are looking to apply color to specifically appears to be from your description the axis text, rather than the legend. Without your particular dataset, I can only show you how you can do this generally, and then leave it up to you to apply to your particular case.
The color of the axis text is a theme element, and is controlled by axis.text generally (for both axes), or axis.text.x / axis.text.y for the x and y axes separately. Addressing and changing aspects of the text on both axes will require you to specify element_text() within theme(). It looks generally like this in a typical plotting code:
ggplot(...) + geom_...() + theme(
axis.text.x = element_text(color = 'blue', face = 'bold', size = 12, angle=20)
)
This particular case above would make the x axis text all blue in color, size 12, bold, and set the angle at 20 degrees.
To specify parts of the axis to have different color, size, face, etc therefore has to access this theme element. The important thing to keep in mind is that theme elements are not mapped in the same way that the rest of your plot and geoms are mapped - which is through aes(). In place of color = 'blue', you can either pass one value (which is applied to all), or you can supply a character vector which is the same length as the labels on your axis. <-- this point is very important to remember.
Here's an example.
set.seed(1234)
df <- data.frame(
x=1:20,
y=rnorm(20)
)
Let's create a simple plot:
p <- ggplot(df, aes(x,y)) + geom_point() + theme_bw() +
geom_hline(yintercept=0, linetype=2) +
scale_x_continuous(breaks=1:20)
p
If you wanted to color the axis labels based on criteria, you can directly code that into theme(axis.text.x = element_blank(color=... in such a way that creates a character vector of colors. Remember, there's no mapping here, so make sure that the order of the resulting vector follows the same order of your dataset. If in your case your x values are not ordered in the original dataset, you will need them to be for this to work properly. Here's how you can cause the x axis labels to be colored differently for negative vs. positive values of df$y:
p + theme(
axis.text.x = element_text(color=ifelse(df$y < 0,'red', 'gray80'))
)
You can even get more complicated with this method, but as long as the resulting character vector is in the same order as the axis labels, it should work:
p + theme(
axis.text.x = element_text(
color=ifelse(df$y < 0,'red', 'gray80'),
size=ifelse(abs(df$y) > 1, 18, 8),
face=ifelse(abs(df$y) > 1, 'bold', 'italic')
)
)
So created a loadings plot via arrow style using ggplot command. In order to make things easier for graphing, I added a column into the dataframe of my rr.pr$rotation code with colours so that it graphs those arrows based on the colour I specified. The colours that match the arrows are important which is why I did it that way. I am having trouble now adding a legend as ggplot isn't adding a legend.
Is there a way to add one or do I have to do something to the dataframe?
I was thinking of adding the colours manually, but I am getting stuck.
Green represents Sulfated, Orange represents Sialyllated, and Brown represents Neutral. And I would like the legend to show that.
Here is the code:
Dataframe
rrload<-data.frame(rr.pr$rotation[c(2,15,17,24,52),c(1:5)])
rrload$class<-c('orange','springgreen3','bisque3','bisque3','bisque3')
rrload1<-rrload[,c(1:5)]
rrload1<-as.numeric(as.matrix(rrload1))
rrload1<-matrix(rrload1,nrow=5,ncol=5,byrow = F)
rrload[,c(1:5)]<-rrload1
Code for plotting it:
ggplot(rrload)+geom_segment(aes(xend=PC1,yend=PC2),x=0,y=0,arrow = arrowstyle2,color=rrload$class)+
geom_text(aes(x=PC1,y=PC2,label=row.names(rrload)),hjust=0,nudge_x = -0.05,vjust=1,nudge_y = 0.025,size=3.5,color='black')+xlim(-0.3,0.3)+ylim(-0.3,0.3)+theme_light()+
theme_minimal()+theme(legend.title = element_text("Class"),axis.text.x = element_text(colour = "black",size = 10),axis.text.y = element_text(colour = "black",size = 10),axis.title.x = element_text(colour = "black",size = 10),axis.title.y = element_text(colour = "black",size = 10),axis.ticks = element_line(color = "black"),panel.grid = element_blank(), panel.border = element_rect(colour = "black",fill = NA,size = 1))+geom_hline(yintercept = 0,linetype="dashed",color="gray69")+geom_vline(xintercept = 0,linetype="dashed",color="gray69")
This is the graph:
Loadings plot
Without access to your full data (your code is unable to recreate the dataframe, rrload properly), it's hard to help. I managed to estimate the numbers based on the plot you shared. Here's the dataframe I used - note the naming conventions for the columns:
d <- data.frame(
PC1=c(-0.2,-0.2,0.1,0.15,-0.08),
PC2=c(0.13,-0.1,0.2,0.1,-0.2300),
class=c('Neutral','Neutral','Neutral','Sulfated','Silylated'),
name=c('o53','o18','o25','o15','o2')
)
To prepare the data for plotting, I included d$name and d$class. d$class is similar to the column you had, although instead of the color, I'm using the actual name. d$name is the name that I'm using to plot your labels.
Here's the code I used and resulting plot. Explanation will come after:
library(ggrepel)
ggplot(d) + theme_classic() +
geom_vline(xintercept=0, linetype=2, color='gray60') +
geom_hline(yintercept=0, linetype=2, color='gray60') +
geom_segment(
aes(xend=PC1,yend=PC2, color=class), x=0,y=0,
arrow=arrow(type='closed', angle=20, length=unit(0.02,'npc'))
) +
geom_text_repel(
aes(x=PC1, y=PC2, label=name), force=6, min.segment.length = 10, seed=123
) +
ylim(-0.3,0.3) + xlim(-0.3,0.3) +
scale_color_manual(
name='Legend Title',
values=c('Neutral'='bisque3','Sulfated'='springgreen3','Silylated'='orange'))
ggplot2 will create a legend for certain aesthetics, but they must be placed within aes(). Once you do that, ggplot2 will create the legend and automatically assign colors. This means that if we want to create a legend for color=, you need to put it within aes(). The interesting part is that you can put it within aes() anywhere in the call, or just apply to specific geom/geoms. This allows a lot of flexibility in creating your plot. In this case, I only want to color the arrows, so you include color=class within the geom_segment() call. If you put it within the ggplot() call, it would color both the line segment as well as the text geom.
I'm also paying attention to the ordering. We want to make sure the background dotted lines for the central axis at 0,0 are "behind" everything, so they go first. Then the segments, and then the text geom.
The scale_color_manual() function is used to specify the colors for the different d$class values explicitly and the name of the legend. You can also just let ggplot2 find a palette by default, or you can specify via a palette (there are a ton of other methods to specify color). BTW - you can also specify the name of the legend via labs(color=....
Finally, I decided to use geom_text_repel() rather than geom_text(). Since the lines go out in every direction, the "nudge" values for each text item are not going to work going in the same direction. In other words, if you plot the text at x=PC1, y=PC2, it will overlap the arrowheads. You noticed this too and applied nudge_ values, which happens to work, but if your data was a bit different, it would not have worked. geom_text_repel from the ggrepel package can work to do this by kind of "pushing" the text away from your points.
I am currently working on the code of "R graphics cookbook", Chapter 3, as provided by user "gaorongchao" on github:
A) code as given
install.packages(gcookbook)
library(gcookbook)
tophit <- tophitters2001[1:25, ]
nameorder <- tophit$name[order(tophit$lg, tophit$avg)]
tophit$name <- factor(tophit$name, levels=nameorder)
ggplot(tophit, aes(x=avg, y=name)) +
geom_segment(aes(yend=name), xend=0, colour="grey50") +
geom_point(aes(colour=lg), size=3) +
scale_colour_brewer(palette="Set1", limits=c("NL","AL")) +
theme_bw() +
theme(panel.grid.major.y = element_blank(),
legend.position=c(1, 0.55),
legend.justification=c(1, 0.5))
B) Then I tried a variation with
ggplot(tophit, aes(x=avg, y=name)) +
geom_segment(aes(xend=0, yend=name), colour="grey50") +
geom_point(aes(colour=lg), size=3) +
scale_colour_brewer(palette="Set1", limits=c("NL","AL")) +
theme_bw() +
theme(panel.grid.major.y = element_blank(),
legend.position=c(1, 0.55),
legend.justification=c(1, 0.5))
where xend is part of the aes mapping in geom_segment(). B) leads to another graphic with another scale, where xend=0 is explicitly part of the x-scale. Can somehow explain the systematic behind this difference of code A) and B)? xend being part of aes and being not. What is the difference? Thanks
It is required to set aesthetic mappings to your data frame inside aes() - only inside aes() will ggplot know to look in your data frame form a column name.
Constants, like your xend = 0, or color = "red" if you wanted to color all points red, can be set inside aes(), but it is generally preferred to set them outside of aes(). For something like color, this will not automatically create a legend (you normally don't want a color legend if there is only one color). Similarly, in your example, you saw that putting xend = 0 inside aes() make it "explicitly part of the scale".
Setting a constant inside aes is equivalent to adding that column to your data frame and then mapping it, outside aes tells ggplot "hey, just do this, but don't worry about adding it to the data frame or legends or anything".
I've only very recently started learning R. Now what I'm trying to do is to integrate two legends for the same plot. In other words, I want the default size legend to change color depending on it's size.
I have been Googling several solutions that apparently all don't seem to work, but again, I'm new to R so maybe I'm just doing something wrong.
My code:
ggplot(Caschool, aes(x=testscr, y=avginc), colour="green") +
geom_point(aes(size=enrltot, color=enrltot)) +
geom_smooth(colour="blue") +
labs(x="Test Score", y="Average Income", title="California Test Score Data", color="Number of Students\nPer District") +
theme(
panel.grid.minor = element_blank(),
panel.grid.major=element_line(colour="grey", size=0.4),
panel.background=element_rect(fill="beige"),
axis.line=element_line(size = 1.2, colour = "black"),
plot.title = element_text(size = rel(2))) +
scale_color_continuous(limits=c(0, 30000), breaks=seq(0,30000, by=2500)) +
guides(color= guide_legend(), size=guide_legend())
Apparently, I'm not allowed to post pictures, or I would have shown what this looks like so far.
ggplot2 can indeed combine size and colour legends into one, however, this only works, if they are compatible: they need to have exactly the same breaks, otherwise they can not be combined.
Let me make an example: Assume, you have values between 0 and 10 that you want to map on size and colour. You tell ggplo2 to use small points for values below 5 and large points for larger value. It will then plot a legend with a small and a large point, as expected. Now, you also want to add colour and you require points below 3 to be green and points above to be blue. ggplot2 will also draw a legend for this, but it is impossible to combine the two legends. The small point would have to be both, green and blue. The problem can be solved by using the same breaks for colour and size.
In your example, you manually change the breaks of the colour scale, but not those of the size scale. This results in incompatible legends that can not be combined.
I can not demonstrate this using your date, because I don't have it. So I will create an example with mtcars. The variant with incompatible legends is constructed as follows:
p <- ggplot(mtcars, aes(x=mpg, y=drat)) +
geom_point(aes(size=gear, color=gear)) +
scale_color_continuous(limits=c(2, 5), breaks=seq(2, 5, by=0.5)) +
guides(color= guide_legend(), size=guide_legend())
which gives the following plot:
If I now add the same breaks for size,
p + scale_size_continuous(limits=c(2, 5), breaks=seq(2, 5, by=0.5))
I get a plot with only one legend:
For your code, this means that you should add the following to your plot:
+ scale_size_continuous(limits=c(0, 30000), breaks=seq(0,30000, by=2500))
A little side remark: What do you intend by using colour = "green" in your call to ggplot? I don't see that this has any effect at all, because you set the colour again in both geoms that you use later. Maybe a relic from an older variant of the plot?