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

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)

Related

Is there an R (ggplot or other) to make a line/segment width increase

I want to draw this type of line/segment in R.
The ggforce::geom_link2() function can interpolate aesthetics between two points on a line.
library(ggplot2)
library(ggforce)
df <- data.frame(
x = c(1, 2), y = c(1, 2),
width = c(1, 2)
)
ggplot(df, aes(x, y)) +
geom_link2(aes(size = width),
lineend = "round")
Created on 2021-08-27 by the reprex package (v1.0.0)
The answer is, I think, "Yes, of course, because ggplot2 is amazing and you can do anything" and, at the same time, "unfortunately it's going to be at least a little bit painful".
Here is my best approximation of your image using only points and lines:
library(ggplot2)
x <- seq(1, 10, length = 200)
y <- - 2 * x
dat <- data.frame(x, y)
ggplot(dat, aes(x, y, size = x ** 2)) +
geom_line(show.legend = FALSE) +
geom_point(aes(size = x ** 2 * 0.7),
data = dat[c(1, 200),],
show.legend = FALSE) +
theme_void()
And the result looks pretty decent, in my opinion.
The greatest advantage of this technique is that it allows you to color your line, and get beautiful graphs like this!

breaks at integer powers of ten on ggplot2 log10 axes

Transforming ggplot2 axes to log10 using scales::trans_breaks() can sometimes (if the range is small enough) produce un-pretty breaks, at non-integer powers of ten.
Is there a general purpose way of setting these breaks to occur only at 10^x, where x are all integers, and, ideally, consecutive (e.g. 10^1, 10^2, 10^3)?
Here's an example of what I mean.
library(ggplot2)
# dummy data
df <- data.frame(fct = rep(c("A", "B", "C"), each = 3),
x = rep(1:3, 3),
y = 10^seq(from = -4, to = 1, length.out = 9))
p <- ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(~ fct, scales = "free_y") # faceted to try and emphasise that it's general purpose, rather than specific to a particular axis range
The unwanted result -- y-axis breaks are at non-integer powers of ten (e.g. 10^2.8)
p + scale_y_log10(
breaks = scales::trans_breaks("log10", function(x) 10^x),
labels = scales::trans_format("log10", scales::math_format(10^.x))
)
I can achieve the desired result for this particular example by adjusting the n argument to scales::trans_breaks(), as below. But this is not a general purpose solution, of the kind that could be applied without needing to adjust anything on a case-by-case basis.
p + scale_y_log10(
breaks = scales::trans_breaks("log10", function(x) 10^x, n = 1),
labels = scales::trans_format("log10", scales::math_format(10^.x))
)
Should add that I'm not wed to using scales::trans_breaks(), it's just that I've found it's the function that gets me closest to what I'm after.
Any help would be much appreciated, thank you!
Here is an approach that at the core has the following function.
breaks = function(x) {
brks <- extended_breaks(Q = c(1, 5))(log10(x))
10^(brks[brks %% 1 == 0])
}
It gives extended_breaks() a narrow set of 'nice numbers' and then filters out non-integers.
This gives us the following for you example case:
library(ggplot2)
library(scales)
#> Warning: package 'scales' was built under R version 4.0.3
# dummy data
df <- data.frame(fct = rep(c("A", "B", "C"), each = 3),
x = rep(1:3, 3),
y = 10^seq(from = -4, to = 1, length.out = 9))
ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(~ fct, scales = "free_y") +
scale_y_continuous(
trans = "log10",
breaks = function(x) {
brks <- extended_breaks(Q = c(1, 5))(log10(x))
10^(brks[brks %% 1 == 0])
},
labels = math_format(format = log10)
)
Created on 2021-01-19 by the reprex package (v0.3.0)
I haven't tested this on many other ranges that might be difficult, but it should generalise better than setting the number of desired breaks to 1. Difficult ranges might be those just in between -but not including- powers of 10. For example 11-99 or 101-999.

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)

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

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.

How to have background fill in the plot in R

So, I've a t-dist plot created in R using curve and adding on the polygons onto that. It gives me a basic looking plot.
What I need is a more good looking plot where
X-axis starts from -6
Y-axis starts from 0
Background of the plot(except under the curve) is filled with some color which I need
I think I need to use the ggplot2 package for this, so answers based on ggplot2 usage is what I need. Or any answer that would return me that output is appreciated.
Here is my code
curve(dt(x, df = 7), from = -6, to = 6)
x <- seq(-1.96, -6, len = 100)
y <- dt(x, 7)
x1 <- seq(1.96, 6, len = 100)
y1 <- dt(x1, 7)
polygon(c(x1[1], x1, x1[100]), c(dt(-6, 7), y1, dt(6, 7)),
col = "#b14025", border = "black")
polygon(c(x[1], x, x[100]), c(dt(-6, 7), y, dt(6, 7)),
col = "#b14025", border = "black")
First Image is the current Output
Second Image is what I think it should look like
Here is one way to obtain a similar result using the ggplot2 package:
library(ggplot2)
dt_tails <- function(x){
y <- dt(x,7)
y[abs(x) < 1.96] <- NA
return(y)
}
dt_7 <- function(x) dt(x,7)
p <- ggplot(data.frame(x=c(-6,6)),aes(x=x)) +
stat_function(fun=dt_7, geom="area", fill="white", colour="black")
p <- p + stat_function(fun=dt_tails, geom="area", fill='#b14025')
p <- p + theme(panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
panel.background=element_rect(fill="#eae9c8") )
plot(p)
Since you expected a ggplot answer, just add + theme(panel.background = element_rect(fill = "yellow")) to your plot or what ever color you like.
I finally managed to do it with the base plotting functions only.
For Shading the area outside curve: I just added one more polygon tracing the area outside the curve.
For fixing the graph to start at the required X and Y, I used another parameter of plot function xaxs & yaxs from this Link
Here is my attached code
curve(dt(x, df = 7), from = -6, to = 6,xaxs="i",yaxs="i",ylim=c(0,0.4))
t = seq(-6,6,len = 100)
yt = dt(t,7)
x <- seq(-1.96, -6, len = 100)
y <- dt(x, 7)
x1 <- seq(1.96, 6, len = 100)
y1 <- dt(x1, 7)
polygon(x = c(-6,-6,t,6,6),
y = c(0.4,0,yt,0,0.4),
col = "#eae9c8",
border = "black")
polygon(x = c(x1[1], x1, x1[100]),
y = c(dt(-6, 7), y1, dt(6, 7)),
col = "#b14025",
border = "black")
polygon(x = c(x[1], x, x[100]),
y = c(dt(-6, 7), y, dt(6, 7)),
col = "#b14025",
border = "black")
Here is the attached output

Resources