This question already has an answer here:
Dual y axis (second axis) use in ggplot2
(1 answer)
Closed 5 years ago.
I know this topic has arisen some time in different threads of this page, but I am afraid that following the instructions of all of them I have not managed to fix it. I have been trying to solve this problem for a week that seems quite trivial and I can not find the way.
I do not know if it's about differences in the graphics or that there is something I do wrong. The case is as follows. I have two graphics using the ggplot2 package:
library(ggplot2)
data<-data.frame(Age=0,var2=0,var1=0,inf=0,sup=0,ppv=0)
data[1,]<-c(1,1,0.857,0.793,0.904,0.03)
data[2,]<-c(1,2,0.771 ,0.74,0.799,0.056)
data[3,]<-c(1,3,0.763 ,0.717,0.804,0.06)
data[4,]<-c(1,4,0.724 ,0.653,0.785,0.09)
data[5,]<-c(2,1,0.906,0.866,0.934,0.055)
data[6,]<-c(2,2,0.785 ,0.754,0.813,0.067)
data[7,]<-c(2,3,0.660,0.593,0.722,0.089)
data[8,]<-c(2,4,0.544,0.425,0.658,0.123)
pd <- position_dodge(0.2) #
names(data)<-c("Age","var2","var1","inf","sup","ppv")
data$Age<-as.character(data$Age)
data$var2<-as.character(data$var2)
p<- ggplot(data, aes(x=var2, y=var1, colour=Age)) +
geom_errorbar(aes(ymin=inf, ymax=sup), width=.1 , position=pd) +
geom_line(position=pd,aes(group=Age),linetype=c("dashed")) +
geom_point(position=pd,size=3) +
theme_light()+
ylim(0,1) +
scale_color_manual(values=c("1"="grey55","2"="grey15"))+guides(fill=guide_legend(nrow=2,byrow=TRUE)
)
s<- ggplot(data, aes(x=var2, y=ppv, colour=Age)) +
geom_line(position=pd,aes(group=Age),linetype=c("dashed")) +
geom_point(position=pd,size=3) +
theme_light()+
ylim(0,0.2) + scale_color_manual(values=c("1"="grey55","2"="grey15"))+guides(fill=guide_legend(nrow=2,byrow=TRUE)
)
They look like this:
Image of p
Image of s
I was wondering if someone would know the way to put them together in a single graph, with the two scales that they currently have, for example, the y axis of the graph p at the left side and the y axis of the graph s at the right side since I can not directly draw both data in a graph due to the radical difference in the scales .
Thank you very much for your time,
Best regards,
try this code, you should set aes at new layer.
ggplot(data, aes(x = var2, y = var1, colour=Age)) +
geom_errorbar(aes(ymin = inf, ymax = sup), width = .1, position = pd) +
geom_line(position = pd, aes(group = Age), linetype = c("dashed")) +
geom_point(position = pd, size = 3) +
geom_line(position = pd, aes(x = var2, y = ppv * 5, colour = Age, group = Age), linetype = c("dashed"), data = data) +
geom_point(aes(x = var2, y = ppv * 5, colour = Age, group = Age), position = pd, size = 5) +
theme_light() +
scale_color_manual(values = c("1" = "grey55", "2" = "grey15")) +
scale_y_continuous(sec.axis = sec_axis(~./5)) +
guides(fill = guide_legend(nrow = 2, byrow = TRUE))
Related
I'm struggling with the following issue:
I want to plot two histograms, but since the statistics of one of the two classes is much less than the other I need to add a second y-axis to allow a direct comparison of the values.
I report below the code I used at the moment and the result.
Thank you in advance!
ggplot(data,aes(x= x ,group=class,fill=class)) + geom_histogram(position="identity",
alpha=0.5, bins = 20)+ theme_bw()
Consider the following situation where you have 800 versus 200 observations:
library(ggplot2)
df <- data.frame(
x = rnorm(1000, rep(c(1, 2), c(800, 200))),
class = rep(c("A", "B"), c(800, 200))
)
ggplot(df, aes(x, fill = class)) +
geom_histogram(bins = 20, position = "identity", alpha = 0.5,
# Note that y = stat(count) is the default behaviour
mapping = aes(y = stat(count)))
You could scale the counts for each group to a maximum of 1 by using y = stat(ncount):
ggplot(df, aes(x, fill = class)) +
geom_histogram(bins = 20, position = "identity", alpha = 0.5,
mapping = aes(y = stat(ncount)))
Alternatively, you can set y = stat(density) to have the total area integrate to 1.
ggplot(df, aes(x, fill = class)) +
geom_histogram(bins = 20, position = "identity", alpha = 0.5,
mapping = aes(y = stat(density)))
Note that after ggplot 3.3.0 stat() probably will get replaced by after_stat().
How about comparing them side by side with facets?
ggplot(data,aes(x= x ,group=class,fill=class)) +
geom_histogram(position="identity",
alpha=0.5,
bins = 20) +
theme_bw() +
facet_wrap(~class, scales = "free_y")
I have a graph made in ggplot that looks like this:
I wish to have the numeric labels at each of the bars to be grounded/glued to the x axis where y <= 0.
This is the code to generate the graph as such:
ggplot(data=df) +
geom_bar(aes(x=row, y=numofpics, fill = crop, group = 1), stat='identity') +
geom_point(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_line(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_text(aes(x=row, y=numofpics, label=bbch)) +
geom_hline(yintercept=300, linetype="dashed", color = "red", size=1) +
scale_y_continuous(sec.axis= sec_axis(~./50, name="Number of Parcels")) +
scale_x_discrete(name = c(),breaks = unique(df$crop), labels = as.character(unique(df$crop)))+
labs(x=c(), y="Number of Pictures")
I've tried vjust and experimenting with position_nudge for the geom_text element, but every solution I can find changes the position of each element of the geom_text respective to its current position. As such everything I try results in situation like this one:
How can I make ggplot ground the text to the bottom of the x axis where y <= 0, possibly with the possibility to also introduce a angle = 45?
Link to dataframe = https://drive.google.com/file/d/1b-5AfBECap3TZjlpLhl1m3v74Lept2em/view?usp=sharing
As I said in the comments, just set the y-coordinate of the text to 0 or below, and specify the angle : geom_text(aes(x=row, y=-100, label=bbch), angle=45)
I'm behind a proxy server that blocks connections to google drive so I can't access your data. I'm not able to test this, but I would introduce a new label field in my dataset that sets y to be 0 if y<0:
df <- df %>%
mutate(labelField = if_else(numofpics<0, 0, numofpics)
I would then use this label field in my geom_text call:
geom_text(aes(x=row, y=labelField, label=bbch), angle = 45)
Hope that helps.
You can simply define the y-value in geom_text (e.g. -50)
ggplot(data=df) +
geom_bar(aes(x=row, y=numofpics, fill = crop, group = 1), stat='identity') +
geom_point(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_line(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_text(aes(x=row, y=-50, label=bbch)) +
geom_hline(yintercept=300, linetype="dashed", color = "red", size=1) +
scale_y_continuous(sec.axis= sec_axis(~./50, name="Number of Parcels")) +
scale_x_discrete(name = c(),breaks = unique(df$crop), labels =
as.character(unique(df$crop)))+
labs(x=c(), y="Number of Pictures")
I'm having an issue with data labels incorrectly ordering using ggplot2.
Unfortunately, other SE Q&As on this topic are not very insightful(example), so I'm having to reach out with a reprex. I have the following data:
df = as.data.frame(structure(list(geotype = c('urban','urban','urban','urban','suburban','suburban','suburban','suburban'),
limitations = c('all','some','all','some','all','some','all','some'),
metric = c('lte','lte','5g','5g','lte','lte','5g','5g'),
capacity=c(12,11,5,4,14,10,5,3))))
If I then try to plot this data using this code:
ggplot(df, aes(x = geotype, y = capacity, fill=metric)) + geom_bar(stat="identity") +
facet_grid(~limitations) +
geom_text(data = df, aes(geotype, capacity + 2, label=capacity), size = 3)
I get this incorrect labelling order:
I've played with the ordering of the variables for ages (e.g. rev(capacity)) but I can't fix the issue. Can anyone provide a more comprehensive answer for the whole SE community as to how to deal with label ordering?
You need to call the position argument in geom_text to match the filled aesthetics data with geom_bar and to let the function know the data is stacked.
ggplot(df, aes(x = geotype, y = capacity, fill=metric)) +
geom_bar(stat="identity") +
geom_text(data = df, aes(geotype, capacity, label=capacity), size = 3, vjust = 2,
position = position_stack()) +
facet_grid(~limitations)
You can set the labels in the mail ggplot aes
ggplot(df, aes(x = geotype, y = capacity, fill = metric, label = capacity ) ) +
geom_col() +
geom_text( size = 3, position = position_stack( vjust = 0.5 ) ) +
facet_grid( ~limitations )
Something like this? The position stack.
g <- ggplot(df, aes(x = geotype, y = capacity, fill = metric, label =
capacity))
g + geom_col() + facet_grid(~limitations) +
geom_text(size = 3, vjust =3, position = position_stack())
I am trying to make a 2D histogram with the individual bins showing both the bin contents and a gradient. The data are integers ranging from 0 to 4 (only) in both axes.
I tried working with this answer but I end up with a few issues. First, a few bins end up getting no gradient at all. In the MWE below, the bottom left bins of 130 and 60 seems to be blank. Second, the bins are shifted to below 0 in both axes. For this axis issue, I found I could simply add a 0.5 to both x and y. In the end though, I also would like to have the axis labels to be centered within a bin and adding that 0.5 does not address that.
library(ggplot2)
# Construct the data to be plotted
x <- c(rep(0,190),rep(1,50),rep(2,10),rep(3,40))
y <- c(rep(0,130),rep(1,80),rep(2,30),rep(3,10),rep(4,40))
data <- data.frame(x,y)
# Taken from the example
ggplot(data, aes(x = x, y = y)) +
geom_bin2d(binwidth=1) +
stat_bin2d(geom = "text", aes(label = ..count..), binwidth=1) +
scale_fill_gradient(low = "snow3", high = "red", trans = "log10") +
xlim(-1, 5) +
ylim(-1, 5) +
coord_equal()
Is there something obvious I am doing wrong in both the color gradients and axis labels? I am also not married to ggplot or stat_bin2d if there is a better way to do it with some other package/command. Thanks in advance!
stat_bin2d uses the cut function to create the bins. By default, cut creates bins that are open on the left and closed on the right. stat_bin2d also sets include.lowest=TRUE so that the lowest interval will be closed on the left also. I haven't looked through the code for stat_bin2d to try and figure out exactly what's going wrong, but it seems like it has to do with how the breaks in cut are being chosen. In any case, you can get the desired behavior by setting the bin breaks explicitly to start at -1. For example:
ggplot(data, aes(x = x, y = y)) +
geom_bin2d(breaks=c(-1:4)) +
stat_bin2d(geom = "text", aes(label = ..count..), breaks=c(-1:4)) +
scale_fill_gradient(low = "snow3", high = "red", trans = "log10") +
xlim(-1, 5) +
ylim(-1, 5) +
coord_equal()
To center the tiles on the integer lattice points, set the breaks to half-integer values:
ggplot(data, aes(x = x, y = y)) +
geom_bin2d(breaks=seq(-0.5,4.5,1)) +
stat_bin2d(geom = "text", aes(label = ..count..), breaks=seq(-0.5,4.5,1)) +
scale_fill_gradient(low = "snow3", high = "red", trans = "log10") +
scale_x_continuous(breaks=0:4, limits=c(-0.5,4.5)) +
scale_y_continuous(breaks=0:4, limits=c(-0.5,4.5)) +
coord_equal()
Or, to emphasize that the values are discrete, set the bins to be half a unit wide:
ggplot(data, aes(x = x, y = y)) +
geom_bin2d(breaks=seq(-0.25,4.25,0.5)) +
stat_bin2d(geom = "text", aes(label = ..count..), breaks=seq(-0.25,4.25,0.5)) +
scale_fill_gradient(low = "snow3", high = "red", trans = "log10") +
scale_x_continuous(breaks=0:4, limits=c(-0.25,4.25)) +
scale_y_continuous(breaks=0:4, limits=c(-0.25,4.25)) +
coord_equal()
I have a ggplot2 linegraph with two lines featuring significant overlap. I'm trying to use position_jitterdodge() so that they are more visible, but I can't get the lines and points to both jitter in the same way. I'm trying to jitter the points and line horizontally only (as I don't want to suggest any change on the y-axis). Here is an MWE:
## Create data frames
dimension <- factor(c("A", "B", "C", "D"))
df <- data.frame("dimension" = rep(dimension, 2),
"value" = c(20, 21, 34, 32,
20, 21, 36, 29),
"Time" = c(rep("First", 4), rep("Second", 4)))
## Plot it
ggplot(data = df, aes(x = dimension, y = value,
shape = Time, linetype = Time, group = Time)) +
geom_line(position = position_jitterdodge(dodge.width = 0.45)) +
geom_point(position = position_jitterdodge(dodge.width = 0.45)) +
xlab("Dimension") + ylab("Value")
Which produces the ugly:
I've obviously got something fundamentally wrong here: What should I do to make the geom_point jitter follow the geom_line jitter?
Another option for horizontal only would be to specify position_dodge and pass this to the position argument for each geom.
pd <- position_dodge(0.4)
ggplot(data = df, aes(x = dimension, y = value,
shape = Time, linetype = Time, group = Time)) +
geom_line(position = pd) +
geom_point(position = pd) +
xlab("Dimension") + ylab("Value")
One solution is to manually jitter the points:
df$value_j <- jitter(df$value)
ggplot(df, aes(dimension, value_j, shape=Time, linetype=Time, group=Time)) +
geom_line() +
geom_point() +
labs(x="Dimension", y="Value")
The horizontal solution for your discrete X axis isn't as clean (it's clean under the covers when ggplot2 does it since it handles the axis and point transformations for you quite nicely) but it's doable:
df$dim_j <- jitter(as.numeric(factor(df$dimension)))
ggplot(df, aes(dim_j, value, shape=Time, linetype=Time, group=Time)) +
geom_line() +
geom_point() +
scale_x_continuous(labels=dimension) +
labs(x="Dimension", y="Value")
On July 2017, developpers of ggplot2 have added a seed argument on position_jitter function (https://github.com/tidyverse/ggplot2/pull/1996).
So, now (here: ggplot2 3.2.1) you can pass the argument seed to position_jitter in order to have the same jitter effect in geom_point and geom_line (see the official documentation: https://ggplot2.tidyverse.org/reference/position_jitter.html)
Note that this seed argument does not exist (yet) in geom_jitter.
ggplot(data = df, aes(x = dimension, y = value,
shape = Time, linetype = Time, group = Time)) +
geom_line(position = position_jitter(width = 0.25, seed = 123)) +
geom_point(position = position_jitter(width = 0.25, seed = 123)) +
xlab("Dimension") + ylab("Value")