How do I eliminate whitespace between rows in a ggplot2 grid layout? - r

I'm trying to control the height of plots in a grid layout in ggplot2. I found some promising examples for page spacing using viewport.
I managed to control the column width. I want the first column to be a third of the page width.
However, I want the second row of figures to sit close to the first row. I tried fooling around with the plot margins, but I'm unable to affect the spacing between the two rows.
Here's the code that draws my figures.
library(ggplot2)
library(gridExtra)
# Generate a vector of times.
t=seq(0, 2 , 0.0001)
# Draw some figures using segments.
df1 <- data.frame(x1 = 0, x2 = 1, y1 = 0, y2 = .1)
open_pipe_p <- ggplot(data = df1) +
theme(panel.background = element_rect(fill = "white"),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
plot.margin = unit(c(0,0.0,0,0), units="npc")) +
coord_fixed() +
geom_segment(aes(x = x1, y = y1, xend = x2, yend = y1), size = .75) +
geom_segment(aes(x = x1, y = y2, xend = x2, yend = y2), size = .75)
closed_pipe_p <- ggplot(data = df1) +
theme(panel.background = element_rect(fill = "white"),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
plot.margin = unit(c(0,0.0,0,0), units="npc")) +
coord_fixed() +
geom_segment(aes(x = x1, y = y1, xend = x2, yend = y1), size = .75) +
geom_segment(aes(x = x1, y = y2, xend = x2, yend = y2), size = .75) +
geom_segment(aes(x = x1, y = y1, xend = x1, yend = y2), size = .75) +
xlim(0, 2)
# Draw some sinusoids.
# Parameters of sinusoid.
A <- 1
f <- .5
phi <- pi / 2
# Y values.
y <- A * sin(2 * pi * f * t + phi)
df_sin <- data.frame(cbind(t, y))
# I only need 1 second.
df_sin <- df_sin[df_sin$t <= 1, ]
df_sin$y[df_sin$t > 1] <- NA
open_wave_p <- ggplot(data = df_sin) +
theme(panel.background = element_rect(fill = "white"),
axis.line = element_line(),
axis.text.y = element_blank(),
axis.title = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0.0,0,0), units="npc")) +
scale_x_continuous(breaks = seq(0, 1, .2),
expand = c(0, 0)) +
coord_fixed(ratio = .1) +
geom_line(mapping = aes(x = t, y = y)) +
geom_line(mapping = aes(x = t, y = -y))
A <- 1
f <- .25
phi <- 0
y <- A * sin(2 * pi * f * t + phi)
df_sin <- data.frame(cbind(t, y))
closed_wave_p <- ggplot(data = df_sin) +
theme(panel.background = element_rect(fill = "white"),
axis.line = element_line(),
axis.text.y = element_blank(),
axis.title = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0.0,0,0), units="npc")) +
scale_x_continuous(breaks = seq(0, 1, .2),
expand = c(0, 0)) +
coord_fixed(ratio = .1) +
geom_line(mapping = aes(x = t, y = y)) +
geom_line(mapping = aes(x = t, y = -y))
# Set up the grid.
grid.newpage()
pushViewport(viewport(layout=grid.layout(
nrow = 2,
ncol = 2,
widths = c(0.333, 0.667),
heights = c(0.25, 0.75))))
print(open_pipe_p, vp=viewport(layout.pos.row=1,layout.pos.col=1))
print(closed_pipe_p, vp=viewport(layout.pos.row=1,layout.pos.col=2))
print(open_wave_p, vp=viewport(layout.pos.row=2,layout.pos.col=1))
print(closed_wave_p, vp=viewport(layout.pos.row=2,layout.pos.col=2))

If you're using something like coord_fixed() then the plots won't automatically expand to fill all available space. Finding a good plot size that will show all the plots without too much whitespace is often a bit of a process of trial and error (although I guess you could do some rough math to figure it out based on the ratio of width to height).
In that case, rather than solving it with code, you can just view the plot in a resizable window (e.g. by clicking "Zoom" in RStudio), and manually resize the window to figure out a good size.

Related

In ggplot, how to fill area between two normal curves

I have two normal curves and I want to fill the right area between both curves, so left curve is inferior y limit and right curve is superior y limit. To plot the curves I am using stat_function() so ggplot draws the curve without defining an y-column in aes(). I have drawn the fill area between the curve and the X axis, but I need the area between both curves and the trick of emptying the left curve with NA doesn't seem to work as I expected.
The code to generate the plot is in a function as I need to plot several different couples of normal curves.
How can I do that?
library(ggplot2)
library(ggthemes)
graf_normal <- function(Xmedia1, Xdt1, Xmedia2, Xdt2) {
Xmin1 <- Xmedia1-4*Xdt1
Xmax1 <- Xmedia1+4*Xdt1
Xmin2 <- Xmedia2-4*Xdt2
Xmax2 <- Xmedia2+4*Xdt2
Ymax1 <- max(dnorm(Xmedia1, Xmedia1, Xdt1))
Ymax2 <- max(dnorm(Xmedia2, Xmedia2, Xdt2))
Xmin <- min(Xmin1, Xmin2)
Xmax <- max(Xmax1, Xmax2)
ggplot(data.frame(X = c(Xmin, Xmax)), aes(x = X)) +
geom_hline(yintercept = 0, colour = "grey", linewidth = 1) +
stat_function(fun = dnorm,
args = c(Xmedia1, Xdt1),
linewidth = 1,
colour = "grey") +
stat_function(fun = dnorm,
args = c(Xmedia2, Xdt2),
linewidth = 1,
colour = "black") +
geom_segment(aes(x = Xmedia1, y = 0, xend = Xmedia1, yend = Ymax1),
linetype = "dashed",
linewidth = 0,
colour = "grey") +
geom_segment(aes(x = Xmedia2, y = 0, xend = Xmedia2, yend = Ymax2),
linetype = "dashed",
linewidth = 0,
colour = "black") +
####################################################################
stat_function(fun = dnorm,
args = c(Xmedia2, Xdt2),
xlim = c(Xmedia2+1.5*Xdt2,Xmax2),
geom = "area",
fill = "red",
alpha = 0.5) +
stat_function(fun = dnorm,
args = c(Xmedia1, Xdt1),
xlim = c(Xmedia1,Xmax1),
geom = "area",
fill = NA,
alpha = 0.01) +
##################################################################
theme(
line = element_blank(),
axis.line.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis. Ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
legend. Position = "none",
panel. Grid = element_blank(),
panel. Background = element_rect(fill = "lightgray", colour = NA),
) +
xlim(c(Xmin, Xmax))
}
g1 <- graf_normal(250, 7, 253, 7)
g1
The plot of both curves I get is this:
Thanks,
EDIT:
Using #stephan's code and playing with data filtering, I've been able to do this, easier using geom_ribbon():
Cool way of differencing overlapping zones!
Complete code:
library(ggplot2)
graf_normal <- function(Xmedia1, Xdt1, Xmedia2, Xdt2, n = 1000) {
x1 <- Xmedia1 + 4 * Xdt1 * seq(-1, 1, length. Out = n)
x2 <- Xmedia2 + 4 * Xdt2 * seq(-1, 1, length. Out = n)
dat <- data. Frame(
x = union(x1, x2)
)
dat$y1 <- dnorm(dat$x, Xmedia1, Xdt1)
dat$y2 <- dnorm(dat$x, Xmedia2, Xdt2)
Ymax1 <- dnorm(Xmedia1, Xmedia1, Xdt1)
Ymax2 <- dnorm(Xmedia2, Xmedia2, Xdt2)
ggplot(dat, aes(x)) +
geom_hline(yintercept = 0, colour = "grey", linewidth = 1) +
geom_ribbon(
data = subset(dat, x >= Xmedia2 + 1.5 * Xdt2),
aes(ymin = y1, ymax = y2),
fill = "red", alpha = 0.8
) +
geom_ribbon(
data = subset(dat, (x <= Xmedia2 + 1.5 * Xdt2) & (y2 > y1)),
aes(ymin = y1, ymax = y2),
fill = "red", alpha = 0.2
) +
geom_ribbon(
data = subset(dat, x <= Xmedia1 - 1.5 * Xdt2),
aes(ymin = y1, ymax = y2),
fill = "blue", alpha = 0.8
) +
geom_ribbon(
data = subset(dat, (x <= Xmedia2 ) & (y1 > y2)),
aes(ymin = y1, ymax = y2),
fill = "blue", alpha = 0.2
) +
annotate(
geom = "segment",
x = c(Xmedia1, Xmedia2), y = 0,
xend = c(Xmedia1, Xmedia2), yend = c(Ymax1, Ymax2),
linetype = "dashed",
linewidth = 1,
colour = c("grey", "black")
) +
geom_line(aes(y = y1), linewidth = 1, colour = "grey") +
geom_line(aes(y = y2), linewidth = 1, colour = "black") +
theme(
line = element_blank(),
axis.line.y = element_blank(),
axis. Text = element_blank(),
axis. Ticks = element_blank(),
axis. Title = element_blank(),
legend. Position = "none",
panel. Grid = element_blank(),
panel. Background = element_rect(fill = "lightgray", colour = NA),
)
}
graf_normal(250, 7, 253, 7)
However, the code doesn't work for all curves, working on it!:
graf_normal(250, 7, 253, 3)
One option to fill the area between the normal curves would be to use ggh4x::stat_difference which however requires to compute the values for the densities manually and drawing via geom_line instead of relying on stat_function():
library(ggplot2)
library(ggh4x)
graf_normal <- function(Xmedia1, Xdt1, Xmedia2, Xdt2, n = 101) {
x1 <- Xmedia1 + 4 * Xdt1 * seq(-1, 1, length.out = n)
x2 <- Xmedia2 + 4 * Xdt2 * seq(-1, 1, length.out = n)
dat <- data.frame(
x = union(x1, x2)
)
dat$y1 <- dnorm(dat$x, Xmedia1, Xdt1)
dat$y2 <- dnorm(dat$x, Xmedia2, Xdt2)
Ymax1 <- dnorm(Xmedia1, Xmedia1, Xdt1)
Ymax2 <- dnorm(Xmedia2, Xmedia2, Xdt2)
ggplot(dat, aes(x)) +
geom_hline(yintercept = 0, colour = "grey", linewidth = 1) +
ggh4x::stat_difference(
data = ~ subset(.x, x >= Xmedia2 + 1.5 * Xdt2),
aes(ymin = y1, ymax = y2)
) +
annotate(
geom = "segment",
x = c(Xmedia1, Xmedia2), y = 0,
xend = c(Xmedia1, Xmedia2), yend = c(Ymax1, Ymax2),
linetype = "dashed",
linewidth = 1,
colour = c("grey", "black")
) +
geom_line(aes(y = y1), linewidth = 1, colour = "grey") +
geom_line(aes(y = y2), linewidth = 1, colour = "black") +
scale_fill_manual(values = c(scales::alpha("red", .5), "transparent")) +
theme(
line = element_blank(),
axis.line.y = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank(),
legend.position = "none",
panel.grid = element_blank(),
panel.background = element_rect(fill = "lightgray", colour = NA),
)
}
graf_normal(250, 7, 253, 7)
EDIT Actually stat_differnce is not really needed for your case. Was thinking too complicated. As #JuanRiera mentioned in his comment, we could fill the area using a geom_ribbon:
library(ggplot2)
graf_normal <- function(Xmedia1, Xdt1, Xmedia2, Xdt2, n = 101) {
x1 <- Xmedia1 + 4 * Xdt1 * seq(-1, 1, length.out = n)
x2 <- Xmedia2 + 4 * Xdt2 * seq(-1, 1, length.out = n)
dat <- data.frame(
x = union(x1, x2)
)
dat$y1 <- dnorm(dat$x, Xmedia1, Xdt1)
dat$y2 <- dnorm(dat$x, Xmedia2, Xdt2)
Ymax1 <- dnorm(Xmedia1, Xmedia1, Xdt1)
Ymax2 <- dnorm(Xmedia2, Xmedia2, Xdt2)
ggplot(dat, aes(x)) +
geom_hline(yintercept = 0, colour = "grey", linewidth = 1) +
geom_ribbon(aes(ymin = y1, ymax = y2),
data = subset(dat, x >= Xmedia2 + 1.5 * Xdt2),
fill = "red", alpha = 0.5
) +
annotate(
geom = "segment",
x = c(Xmedia1, Xmedia2), y = 0,
xend = c(Xmedia1, Xmedia2), yend = c(Ymax1, Ymax2),
linetype = "dashed",
linewidth = 1,
colour = c("grey", "black")
) +
geom_line(aes(y = y1), linewidth = 1, colour = "grey") +
geom_line(aes(y = y2), linewidth = 1, colour = "black") +
theme(
line = element_blank(),
axis.line.y = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank(),
legend.position = "none",
panel.grid = element_blank(),
panel.background = element_rect(fill = "lightgray", colour = NA),
)
}
graf_normal(250, 7, 253, 7)
One way is using a geom_polygon instead of stat_function.
Add this to your function, before the ggplot():
poly <- data.frame(xs = seq(Xmedia2+1.5*Xdt2, Xmax2, length.out = n)) |>
transform(
y1 = dnorm(xs, Xmedia1, Xdt1),
y2 = dnorm(xs, Xmedia2, Xdt2)
) |>
with(data.frame(X = c(xs, rev(xs)), Y = c(y1, rev(y2))))
And then replace your both of your second two stat_function calls with a single
geom_polygon(aes(X, Y), data = poly,
fill = "red", alpha = 0.5) +
Full source:
graf_normal <- function(Xmedia1, Xdt1, Xmedia2, Xdt2, n = 20) {
Xmin1 <- Xmedia1-4*Xdt1
Xmax1 <- Xmedia1+4*Xdt1
Xmin2 <- Xmedia2-4*Xdt2
Xmax2 <- Xmedia2+4*Xdt2
Ymax1 <- max(dnorm(Xmedia1, Xmedia1, Xdt1))
Ymax2 <- max(dnorm(Xmedia2, Xmedia2, Xdt2))
Xmin <- min(Xmin1, Xmin2)
Xmax <- max(Xmax1, Xmax2)
poly <- data.frame(xs = seq(Xmedia2+1.5*Xdt2, Xmax2, length.out = n)) |>
transform(
y1 = dnorm(xs, Xmedia1, Xdt1),
y2 = dnorm(xs, Xmedia2, Xdt2)
) |>
with(data.frame(X = c(xs, rev(xs)), Y = c(y1, rev(y2))))
ggplot(data.frame(X = c(Xmin, Xmax)), aes(x = X)) +
geom_hline(yintercept = 0, colour = "grey", linewidth = 1) +
geom_polygon(aes(X, Y), data = poly,
fill = "red", alpha = 0.5) +
stat_function(fun = dnorm,
args = c(Xmedia1, Xdt1),
linewidth = 1,
colour = "grey") +
stat_function(fun = dnorm,
args = c(Xmedia2, Xdt2),
linewidth = 1,
colour = "black") +
geom_segment(aes(x = Xmedia1, y = 0, xend = Xmedia1, yend = Ymax1),
linetype = "dashed",
linewidth = 0,
colour = "grey") +
geom_segment(aes(x = Xmedia2, y = 0, xend = Xmedia2, yend = Ymax2),
linetype = "dashed",
linewidth = 0,
colour = "black") +
####################################################################
# stat_function(fun = dnorm,
# args = c(Xmedia2, Xdt2),
# xlim = c(Xmedia2+1.5*Xdt2,Xmax2),
# geom = "area",
# fill = "red",
# alpha = 0.5) +
# stat_function(fun = dnorm,
# args = c(Xmedia1, Xdt1),
# xlim = c(Xmedia1,Xmax1),
# geom = "area",
# fill = NA,
# alpha = 0.01) +
##################################################################
theme(
line = element_blank(),
axis.line.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
legend.position = "none",
panel.grid = element_blank(),
panel.background = element_rect(fill = "lightgray", colour = NA),
) +
xlim(c(Xmin, Xmax))
}
Note: I chose to move the geom_polygon to much earlier in the plot-stack so that the dnorm-lines would be "on top" of the red area. Whether this is important depends on your context and rendering engine.

R ggplot2, display the number of subjects at the bottom (outside) of the plot [duplicate]

Is there a quick way to add a table to my ggplot2 graph? I would like this table to have the value of each line at the same breakpoints as specified in scale_x_continuous(), but with the percentage (%) symbol next to them. My end goal is to create something like the image below. However, I don't know how to add the table.
The following block of code just makes two lines in ggplot2 and should be adequate to provide me with an example:
require(ggplot2)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) + geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') + scale_x_continuous(breaks = seq(0,100,10))
df.plot
A similar question was asked here, but the given answer is more of a workaround and wouldn't look good for a table with 2 rows. I am going to mess around with the clues provided by Brian Diggs, but I figured I would post this in case anyone has already done something like this. Any help would be greatly appreciated!
Edit: Thanks to #baptiste for helping me figure this out. I posted my own response below that finished what he started.
Here's a basic example of the strategy used by learnr:
require(ggplot2)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) +
geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') +
scale_x_continuous(breaks = seq(0,100,10))
# make dummy labels for the table content
df$lab <- month.abb[ceiling((df$a+1)/10)]
df.table <- ggplot(df, aes(x = a, y = 0,
label = lab, colour = b)) +
geom_text(size = 3.5) +
theme_minimal() +
scale_y_continuous(breaks=NULL)+
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank())
gA <- ggplotGrob(df.plot)
gB <- ggplotGrob(df.table)[6,]
gB$heights <- unit(1,"line")
require(gridExtra)
gAB <- rbind(gA, gB)
grid.newpage()
grid.draw(gAB)
Here is a script that creates the general table that I set out to make. Notice that I included table titles by changing the names under scale_y_continuous for each row.
require(ggplot2)
require(gridExtra)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) +
geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') +
scale_x_continuous(breaks = seq(0,100,10))
# make dummy labels for the table content
lab.df <- data.frame(lab1 = letters[11:20],
lab2 = letters[1:10])
df.table1 <- ggplot(lab.df, aes(x = lab1, y = 0,
label = lab1)) +
geom_text(size = 5, colour = "red") +
theme_minimal() +
scale_y_continuous(breaks=NULL, name = "Model Lift") +
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_text(angle = 0, hjust = 5))
df.table2 <- ggplot(lab.df, aes(x = lab2, y = 0,
label = lab2)) +
geom_text(size = 5, colour = "blue") +
theme_minimal() +
scale_y_continuous(breaks=NULL, name = "Random")+
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_text(angle = 0, hjust = 3.84))
# silly business to align the two plot panels
gA <- ggplotGrob(df.plot)
gB <- ggplotGrob(df.table1)
gC <- ggplotGrob(df.table2)
maxWidth = grid::unit.pmax(gA$widths[2:3], gB$widths[2:3], gC$widths[2:3])
gA$widths[2:3] <- as.list(maxWidth)
gB$widths[2:3] <- as.list(maxWidth)
gC$widths[2:3] <- as.list(maxWidth)
grid.arrange(gA, gB, gC, ncol=1, heights=c(10, .3, .3))

Mean per group on a bubble plot with ggplot

I have a dataset with a lot of overlapping points and used ggplot to create a bubble plot to show that data. I need to add bars on my plot for the means of each group on the x axis (values can be 0, 1, or 2). I have tried to use geom_errorbar but haven't been able to get it to work with my data. Any help/suggestions would be greatly appreciated.
The following is my code and a script to generate fake data that is similar:
y <- seq(from=0, to=3.5, by=0.5)
x <- seq(from=0, to=2, by=1)
xnew <- sample(x, 100, replace=T)
ynew <- sample(y, 100, replace=T)
data <- data.frame(xnew,ynew)
data2 <- aggregate(data$xnew, by=list(x=data$xnew, y=data$ynew), length)
names(data2)[3] <- "Count"
ggplot(data2, aes(x = x, y = y)) +
geom_point(aes(size=Count)) +
labs(x = "Copies", y = "Score") +
aes(ymax=..y.., ymin=..y..) +
scale_x_continuous(breaks = seq(0, 2, 1)) +
scale_y_continuous(breaks = seq(0, 3, 0.5)) +
theme(legend.position = "bottom", legend.direction = "horizontal",
axis.line = element_line(size=1, colour = "black"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.text.x = element_text(colour="black", size = 10),
axis.text.y = element_text(colour="black", size = 10))
I am not entirely sure that I understand your question correctly. It seems to me that in addition to the bubbles, you want to visualise the mean value of y for each value of x as a bar of some kind. (You mention error bars, but it seems that this is not a requirement, but just what you have tried. I will use geom_col() instead.)
I assume that you want to weigh the mean over y by the counts, i.e., sum(y * Count) / sum(Count). You can create a data frame that contains these values by using dplyr:
data2_mean
## # A tibble: 3 × 2
## x y
## <dbl> <dbl>
## 1 0 1.833333
## 2 1 1.750000
## 3 2 2.200000
When creating the plot, I use data2 as the data set for geom_point() and data2_mean as the data set for geom_col(). It is important to put the bars first, since the bubbles should be on top of the bars.
ggplot() +
geom_col(aes(x = x, y = y), data2_mean, fill = "gray60", width = 0.7) +
geom_point(aes(x = x, y = y, size = Count), data2) +
labs(x = "Copies", y = "Score") +
scale_x_continuous(breaks = seq(0, 2, 1)) +
scale_y_continuous(breaks = seq(0, 3, 0.5)) +
theme(legend.position = "bottom", legend.direction = "horizontal",
axis.line = element_line(size=1, colour = "black"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.text.x = element_text(colour="black", size = 10),
axis.text.y = element_text(colour="black", size = 10))
Everything that I changed compared to your code comes before scale_x_continuous(). This produces the following plot:
Is this what you're after? I first calculated the group-level means using the dplyr package and then added line segments to your plot using geom_segment:
library(ggplot2)
library(dplyr)
data2 <- data2 %>% group_by(x) %>% mutate(mean.y = mean(y))
ggplot(data2, aes(x = x, y = y)) +
geom_point(aes(size=Count)) +
labs(x = "Copies", y = "Score") +
aes(ymax=..y.., ymin=..y..) +
scale_x_continuous(breaks = seq(0, 2, 1)) +
scale_y_continuous(breaks = seq(0, 3, 0.5)) +
theme(legend.position = "bottom", legend.direction = "horizontal",
axis.line = element_line(size=1, colour = "black"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.text.x = element_text(colour="black", size = 10),
axis.text.y = element_text(colour="black", size = 10)) +
geom_segment(aes(y = mean.y, yend = mean.y, x = x -0.25, xend = x + 0.25))

Prevent geom_points and their corresponding labels from overlapping

Thanks for the suggested duplicate, this is however not only about the labels, but is also about adjusting the points themselves so they do not overlap.
have a quick look at the plot below...
I need the coloured points, and their corresponding labels, to never overlap. They should be clustered together and all visible, perhaps with some indication that they are spaced and not 100% accurate, perhaps some sort of call out? Open to suggestions on that.
I've tried adding position = 'jitter' to both geom_point and geom_text, but that doesn't seem to be working (assume it is only for small overlaps?)
Ideas?
# TEST DATA
srvc_data <- data.frame(
Key = 1:20,
X = sample(40:80, 20, replace = T),
Y = sample(30:65, 20, replace = T)
)
srvc_data$Z <- with(srvc_data,abs(X-Y))
t1<-theme(
plot.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(size=.4)
)
main_plot <- ggplot(srvc_data, aes(x = X, y = Y),xlim=c(0,100), ylim=c(0,100)) +
t1 +
theme_bw() +
labs(x="X", y="Y") +
scale_x_continuous(limits = c(0, 100)) +
scale_y_continuous(limits = c(0, 100)) +
geom_abline(intercept = 0, slope = 1, colour="blue", size=34, alpha=.1)+
geom_abline(intercept = 0, slope = 1, colour="black", size=.2, alpha=.5,linetype="dashed")+
geom_point(size = 7, aes(color = Z), alpha=.7) +
scale_color_gradient("Gap %\n",low="green", high="red")+
coord_fixed()+
geom_text(aes(label=Key,size=6),show_guide = FALSE)
main_plot
Produces this plot (of course with your random data it will vary)
Thanks in advance.
Here's your plot with ggrepel geom_text_repel:
library(ggrepel)
# TEST DATA
set.seed(42)
srvc_data <- data.frame(
Key = 1:20,
X = sample(40:80, 20, replace = T),
Y = sample(30:65, 20, replace = T)
)
srvc_data$Z <- with(srvc_data,abs(X-Y))
t1<-theme(
plot.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(size=.4)
)
ggplot(srvc_data, aes(x = X, y = Y),xlim=c(0,100), ylim=c(0,100)) +
t1 +
theme_bw() +
labs(x="X", y="Y") +
scale_x_continuous(limits = c(0, 100)) +
scale_y_continuous(limits = c(0, 100)) +
geom_abline(intercept = 0, slope = 1, colour="blue", size=34, alpha=.1)+
geom_abline(intercept = 0, slope = 1, colour="black", size=.2, alpha=.5,linetype="dashed")+
geom_point(size = 7, aes(color = Z), alpha=.7) +
scale_color_gradient("Gap %\n",low="green", high="red")+
coord_fixed()+
geom_text_repel(aes(label=Key,size=6),show_guide = FALSE)

How can I add a table to my ggplot2 output?

Is there a quick way to add a table to my ggplot2 graph? I would like this table to have the value of each line at the same breakpoints as specified in scale_x_continuous(), but with the percentage (%) symbol next to them. My end goal is to create something like the image below. However, I don't know how to add the table.
The following block of code just makes two lines in ggplot2 and should be adequate to provide me with an example:
require(ggplot2)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) + geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') + scale_x_continuous(breaks = seq(0,100,10))
df.plot
A similar question was asked here, but the given answer is more of a workaround and wouldn't look good for a table with 2 rows. I am going to mess around with the clues provided by Brian Diggs, but I figured I would post this in case anyone has already done something like this. Any help would be greatly appreciated!
Edit: Thanks to #baptiste for helping me figure this out. I posted my own response below that finished what he started.
Here's a basic example of the strategy used by learnr:
require(ggplot2)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) +
geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') +
scale_x_continuous(breaks = seq(0,100,10))
# make dummy labels for the table content
df$lab <- month.abb[ceiling((df$a+1)/10)]
df.table <- ggplot(df, aes(x = a, y = 0,
label = lab, colour = b)) +
geom_text(size = 3.5) +
theme_minimal() +
scale_y_continuous(breaks=NULL)+
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank())
gA <- ggplotGrob(df.plot)
gB <- ggplotGrob(df.table)[6,]
gB$heights <- unit(1,"line")
require(gridExtra)
gAB <- rbind(gA, gB)
grid.newpage()
grid.draw(gAB)
Here is a script that creates the general table that I set out to make. Notice that I included table titles by changing the names under scale_y_continuous for each row.
require(ggplot2)
require(gridExtra)
df <- data.frame(a = seq(0, 90, 10), b = seq(10, 100, 10))
df.plot <- ggplot(data = df, aes(x = seq(1, 100, 10))) +
geom_line(aes(y = a), colour = 'red') +
geom_line(aes(y = b), colour = 'blue') +
scale_x_continuous(breaks = seq(0,100,10))
# make dummy labels for the table content
lab.df <- data.frame(lab1 = letters[11:20],
lab2 = letters[1:10])
df.table1 <- ggplot(lab.df, aes(x = lab1, y = 0,
label = lab1)) +
geom_text(size = 5, colour = "red") +
theme_minimal() +
scale_y_continuous(breaks=NULL, name = "Model Lift") +
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_text(angle = 0, hjust = 5))
df.table2 <- ggplot(lab.df, aes(x = lab2, y = 0,
label = lab2)) +
geom_text(size = 5, colour = "blue") +
theme_minimal() +
scale_y_continuous(breaks=NULL, name = "Random")+
theme(panel.grid.major = element_blank(), legend.position = "none",
panel.border = element_blank(), axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_text(angle = 0, hjust = 3.84))
# silly business to align the two plot panels
gA <- ggplotGrob(df.plot)
gB <- ggplotGrob(df.table1)
gC <- ggplotGrob(df.table2)
maxWidth = grid::unit.pmax(gA$widths[2:3], gB$widths[2:3], gC$widths[2:3])
gA$widths[2:3] <- as.list(maxWidth)
gB$widths[2:3] <- as.list(maxWidth)
gC$widths[2:3] <- as.list(maxWidth)
grid.arrange(gA, gB, gC, ncol=1, heights=c(10, .3, .3))

Resources