ggplot2 vs cowplot, Error in FUN("text"[[1L]], ...) : - r

I'm trying to use cowplot to combine some ggplot2 plots. It should be straightforward, but something in my R or Rstudio surly is broken. What I don't know. I can get it to work with grid.arrange, but the output in my rmarkdown file does not come out as nicely. I broke down my code to the minimum amount to recreate the error, and out of rmarkdown
library(ggplot2)
library(Hmisc)
library(cowplot)
x <- c(1, 8, 9)
y <- c(1, 5, 9)
supply1 <- data.frame(bezier(x, y, evaluation = 500))
g1 <- ggplot(x = 0:10, y = 0:10, geom = "blank") +
geom_path(data = supply1, aes(x = x, y = y), size = 1, colour = "BLUE")
g2 <- ggplot(x = 0:10, y = 0:10, geom = "blank") +
geom_path(data = supply1, aes(x = x+1.5, y = y+1.5), size = 1, colour = "RED")
plot_grid(g1, g2,
ncol = 2,
nrow = 1)
I get this error:
Error in FUN("text"[[1L]], ...) :
Theme element 'text' has NULL property: margin, debug
I have to detach cowplot, but can get something close with gridExtra using this code:
library(ggplot2)
library(Hmisc)
library(gridExtra)
x <- c(1, 8, 9)
y <- c(1, 5, 9)
supply1 <- data.frame(bezier(x, y, evaluation = 500))
g1 <- ggplot(x = 0:10, y = 0:10, geom = "blank") +
geom_path(data = supply1, aes(x = x, y = y), size = 1, colour = "BLUE")
g2 <- ggplot(x = 0:10, y = 0:10, geom = "blank") +
geom_path(data = supply1, aes(x = x+1.5, y = y+1.5), size = 1, colour = "RED")
grid.arrange(g1,g2,
ncol = 2,
nrow = 1)
This code outputs:
grid.arrange plot
Turns out I get the "Error in FUN message" if I try to make any ggplot with both the ggplot2 and cowplot libraries loaded. R 3.1.3, RStudio 0.99.903, cowplot 0.4.0, ggplot2 2.1.0
I have reinstalled everything at least twice, and get the same error situation on a different computer. I can get it to work in a limited fashion. If I wait to call the cowplot library after all other code is run except the plot_grid() chunk, then it will knit and give me the cowplot output. I can not recreate this in a R script only in Rmarkdown, but then I have to have it be the final chunk of the markdown, any ggplot attempts after it will cause the knit to fail.
Short term I used grid.arrange() and just lived with the results, long term I would like to have cowplot as an option.
Any ideas or suggestions?

Apparently it's a bug that has been fixed since R 3.3.1 so upgrade to this version or newer and it should go away.

Related

gridExtra::marrangegrob() disregards ggbreak::scale_y_break()

I'm creating multiple ggplot2 plots with a "broken" Y axis using ggbreak::scale_y_break, and exporting these to a single PDF document using gridExtra::marrangegrob() and ggsave(). It turns out that when saving, the broken Y axis is in fact ignored. Here is some synthetic sample code:
library(tidyverse)
suppressMessages(library(ggbreak))
library(gridExtra)
d <- tibble(val = c(rep(1, 10000), rep(2, 100), rep(3, 50)))
plt <- d |> ggplot(mapping = aes(x = val)) +
geom_histogram(bins = 3, color = "white") + theme_light() +
scale_y_break(breaks = c(150, 9950))
plt
plts <- list(plt, plt)
ggsave(filename = "test.pdf", plot = marrangeGrob(grobs = plts, nrow = 1, ncol = 1))
The plot plt looks as follows:
In the PDF, however, it looks like this:
What am I doing wrong? Is this a bug, and if so, in which of the involved packages? Thanks!

ggplot generates jagged/broken lines in specific cases

I recently encountered several cases where the ggplot produced jagged lines. In the following example, I generate dense time-course data with the package fda and draw two line plots. The first plot gives the black line and the other plot displays the same line except that we use different colors to denote the signs of the values. In the end, I export the plots as eps files and open them in Adobe Illustrator.
# install.packages("fda")
# dir.create("tmp")
library(dplyr)
library(tidyr)
library(ggplot2)
library(fda)
times_sparse <- seq(0, 10, 0.5)
times <- seq(0, 10, 0.02)
basis <- create.bspline.basis(
rangeval = c(0, 10), norder = 4,
breaks = times_sparse
)
nbasis <- basis$nbasis
set.seed(2501)
coeff <- rnorm(nbasis, sd = 0.1)
y <- eval.fd(times, fd(coeff, basis)) |> as.numeric()
dat <- data.frame(t = times, y = y) |>
mutate(pos = factor((y > 0) * 1, levels = c(1, 0)))
### first plot: 1 colors, smooth lines
ggplot(dat) +
geom_line(aes(x = t, y = y)) +
theme_bw() +
theme(panel.grid = element_blank())
# ggsave("tmp/line1a.eps", device = "eps",
# width = 6, height = 6)
### second plot: 2 colors, jagged lines
ggplot(dat) +
geom_line(aes(x = t, y = y, color = pos, group = 1)) +
theme_bw() +
theme(panel.grid = element_blank())
# ggsave("tmp/line1b.eps", device = "eps",
# width = 6, height = 6)
In the screenshots which display the zoomed-in line, we observe that the line in the first plot is smooth, while the line in the second plot is jagged. How can I fix the problem?
Here's my system info:
# R version 4.1.1 (2021-08-10)
# Platform: x86_64-w64-mingw32/x64 (64-bit)
# Running under: Windows 10 x64 (build 22000)
Note: My goal is to generate an eps/pdf file of something like the second plot from R. Other methods that achieve the same goal are appreciated.
You should add lineend = "round" to your geom_line
ggplot(dat) +
geom_line(aes(x = t, y = y, color = pos, group = 1), lineend = "round") +
theme_bw() +
theme(panel.grid = element_blank())
It will look nice via export -> save as PDF and windows() -> save as... too.
An example (2400%):

Labelling with scale_fill_stepsn(), "Breaks and labels are different lengths"

This is the plot I have:
I used this code (including sample data):
# dummy data
df_test <- data.frame(long = rep(447030:447050, 21),
lat = rep(5379630:5379650, each=21),
z = rnorm(21*21))
# plot
ggplot(df_test) +
geom_tile(aes(x=long, y = lat, fill = z)) +
scale_fill_stepsn(
limits = c(-3, 3), breaks = seq(-3, 3, 1), # labels = seq(-3, 3, 1),
colors = c("#ff6f69", "grey90", "#00aedb"))
I would like the legend to show the maximum and minimum value (-3, +3). But when I uncomment the label-code labels = seq(-3, 3, 1), I get an error:
Error: Breaks and labels are different lengths"
Is this a bug or am I misusing the function? (aka: Is it a bug or a feature?) Either way: Do you guys know any workaround / solution for this issue? Maybe something with override.aes() (I am not really good with that function)?
R version: 4.1.0 | ggplot2 version: 3.3.5
(Maybe related: Breaks and labels of different lengths scale_size_binned)
Edit: If I install ggplot2 version 3.3.3, the last box in the legend is bigger somehow (which I don't like either).
This is just a workaround for what I think might be a bug, but you might tweak the breaks a little bit to add/subtract a very small value:
library(ggplot2)
# dummy data
df_test <- data.frame(long = rep(447030:447050, 21),
lat = rep(5379630:5379650, each=21),
z = rnorm(21*21))
# plot
smallvalue <- 10 * .Machine$double.eps
ggplot(df_test) +
geom_tile(aes(x=long, y = lat, fill = z)) +
scale_fill_stepsn(
limits = c(-3, 3),
breaks = c(-3 + smallvalue, -2:2, 3 - smallvalue),
labels = seq(-3, 3, 1),
colors = c("#ff6f69", "grey90", "#00aedb")
)
Created on 2021-08-06 by the reprex package (v1.0.0)
EDIT:
Alternatively, you can set the inner breaks and use a function for the labels argument.
library(ggplot2)
# dummy data
df_test <- data.frame(long = rep(447030:447050, 21),
lat = rep(5379630:5379650, each=21),
z = rnorm(21*21))
# plot
smallvalue <- 10 * .Machine$double.eps
ggplot(df_test) +
geom_tile(aes(x=long, y = lat, fill = z)) +
scale_fill_stepsn(
limits = c(-3, 3),
breaks = -2:2,
labels = function(x) {x}, # Just display the breaks
show.limits = TRUE,
colors = c("#ff6f69", "grey90", "#00aedb")
)
Created on 2021-08-06 by the reprex package (v1.0.0)

r: ggplot2 and shiny: how to make text more readable without using geom_label?

I use shiny to create some reactive plots. When I use geom_text to put the intercepts of geom_vlines next to the lines, I can hardly read the text because of the colors of the plot. I have tried with various colours, none work well.
When I use geom_label instead of geom_text from the {ggplot2} package, my plots take much longer to load. The time basically triples. I have read the article on geom_label and it says that it takes longer to create the plot.
So my question is, how could I make text more readable on the plot without using geom_label and thus slowing down the time to create the plot? Does anybody have any ideas? I know there are solutions, but which one is the ideal one in terms of the time it takes to create the plot. Thank you!
EDIT
Here is an example. I can not change the colors of the plot or text. I could change the position along the y axis of the text.
set.seed(1)
df <- data.frame(numbers = rnorm(1000, 1000, 500))
p123 <- ggplot(data = df, aes(x = numbers))+
geom_histogram(bins = 15, fill = "#000D62")+
geom_vline(xintercept = mean(df$numbers)*2.5)+
geom_text(label = paste0("value = ", round(mean(df$numbers)*2.5, 0),
"€"), x = mean(df$numbers)*2.5, y = 4,
size = 4, colour = "#FFBA18")+
labs(x = "Numbers", y = "number of observations")
plot(p123)
Option 1
One option is to replicate the geom_text() layer and put a copy of it below in white and bold to serve as a makeshift dropshadow. I don't know if that would actually improve your performance, but it does technically avoid using geom_label(). Also I've found that it can be used with plotly::ggplotly() which is not true of geom_label().
library(tidyverse)
# sim data
set.seed(1)
df <- data.frame(numbers = rnorm(1000, 1000, 500))
# base plot
p <- ggplot(data = df, aes(x = numbers)) +
geom_histogram(bins = 15, fill = "#000D62") +
geom_vline(xintercept = mean(df$numbers) * 2.5) +
labs(x = "Numbers", y = "number of observations")
## with plain ggplot2 using two geom_text layers
p +
geom_text(label = paste0("value = ", round(mean(df$numbers) * 2.5, 0), "€"),
x = mean(df$numbers) * 2.5, y = 4, size = 4,
colour = "white", fontface = "bold") +
geom_text(label = paste0("value = ", round(mean(df$numbers) * 2.5, 0), "€"),
x = mean(df$numbers) * 2.5, y = 4, size = 4,
colour = "#FFBA18")
Option 2
Another option is to use the {shadowtext} package which directly addresses this issue.
## with shadowtext library
library(shadowtext)
p +
geom_shadowtext(
label = paste0("value = ", round(mean(df$numbers) * 2.5, 0), "€"),
x = mean(df$numbers) * 2.5, y = 4, size = 4, colour = "#FFBA18")
Created on 2022-05-18 by the reprex package (v2.0.1)

How can I overlay points and lines onto a contour plot with ggplot2?

I want to annotate a contour plot with particular points that I want to highlight (where these points are stored in a different data set). When I try, I get an error:
Error: Aesthetics must either be length one, or the same length as the dataProblems:z
However, when I tried to make a reproducible example, I get a different error:
Error in eval(expr, envir, enclos) : object 'z' not found
The code for the reproducible example is below:
library(mnormt)
library(dplyr)
library(ggplot2)
f <- function(x, y) {
dmnorm(x = c(x, y),
mean = c(0, 0),
varcov = diag(2))
}
f <- Vectorize(f)
xmesh <- seq(from = -3, to = 3, length.out = 100)
ymesh <- seq(from = -3, to = 3, length.out = 100)
dummy <- expand.grid(x = xmesh, y = ymesh)
dummy$z <- f(dummy$x, dummy$y)
stuff <- data_frame(x = c(0, 0, 1),
y = c(0, -1, -1),
point = c("O", "P", "Q"))
dummy %>%
ggplot(aes(x = x, y = y, z = z)) +
stat_contour(aes(color = ..level..)) +
labs(color = "density") +
geom_point(data = stuff, mapping = aes(x = x, y = y, color = point))
ggplot passes the aes from the first ggplot call to the rest of the geoms, unless told otherwise. So the error is telling you that it cannot find z inside stuff, and it still thinks that the z should be z from the initial call.
There are a range of ways to fix this, I think the easiest way to fix it is to give each geom its data separately:
ggplot() +
stat_contour(data = dummy, aes(x = x, y = y, z = z, color = ..level..)) +
labs(color = "density") +
geom_point(data = stuff, aes(x = x, y = y, fill = factor(point)), pch = 21)
NB. you also have a problem where colour cannot be mapped in two different geoms, so I've fixed it using pch and fill.

Resources