scale_alpha_continuous on log scale - r

I am trying to use scale_alpha_continuous in ggplot on a log scale to generate a figure with lines whose transparency (and color) varies depending on a given value on a log scale, given the wide distribution.
Using the code below, I am able to change the color based on a log scale by adding trans="log" but how do I do the same for the transparency (alpha)?
p+scale_color_continuous(trans = "log",low="red", high="black")+ scale_alpha_continuous(range = c(0.1, 1))
Many thanks!

You can add trans = "log" to the scale_alpha_continuous() also.
library(ggplot2)
# here is some simulated data
set.seed(2020)
df <- data.frame(X = rnorm(100),
Y = runif(100),
Z = rexp(100, rate = 0.0000001))
# Original plot
df %>%
ggplot(aes(x = X, y = Y, color = Z, alpha = Z)) +
geom_point(size = 5)
# Log scales
df %>%
ggplot(aes(x = X, y = Y, color = Z, alpha = Z)) +
geom_point(size = 5) +
scale_alpha_continuous(trans = "log", breaks = c(4e5, 4e6, 4e7)) +
scale_color_continuous(trans = "log", breaks = c(4e5, 4e6, 4e7))

Related

Reverse legend in geom_contour

I'm struggling to find the right solution to reverse the legend, so that red is at the bottom and greens at the top. Here's a simple example.
library(ggplot2)
library(dplyr)
x = seq(0.01,1,0.01)
y = seq(0.01,1,0.01)
df <- expand.grid(x = x, y = y)
df <- df %>% mutate(z = x*y/(1 + x))
ggplot(df, aes(x = x, y = y, z = z)) +
geom_contour_filled(bins = 10) +
geom_contour(bins = 20, colour = "grey") +
scale_fill_manual(values = rainbow(20))
Couple of issues:
You're using 20 colours to describe 10 bins.
You're using the entire rainbow for a red-green gradient.
Suggested fix is to use the end and rev arguments of the rainbow() function.
library(ggplot2)
library(dplyr)
x = seq(0.01,1,0.01)
y = seq(0.01,1,0.01)
df <- expand.grid(x = x, y = y)
df <- df %>% mutate(z = x*y/(1 + x))
ggplot(df, aes(x = x, y = y, z = z)) +
geom_contour_filled(bins = 10) +
geom_contour(bins = 20, colour = "grey") +
scale_fill_manual(values = rainbow(10, end = 0.4, rev = TRUE))
Created on 2022-05-15 by the reprex package (v2.0.1)
Aside from that, you might want to consider to take a palette that has better visual properties than a rainbow. For example, can you really discriminate the 2nd-4th green bins visually? A close palette with better (but not perfect) properties is viridisLite::turbo(10, begin = 0.5).

How can I add annotation in ggplotly animation?

I am creating animated plotly graph for my assignment in r, where I am comparing several models with various number of observations. I would like to add annotation showing what is the RMSE of the current model - this means I would like to have text that changes together with slider. Is there any easy way how to do that?
Here is my dataset stored on GitHub. There already is created variable with RMSE: data
The base ggplot graphic is as follows:
library(tidyverse)
library(plotly)
p <- ggplot(values_predictions, aes(x = x)) +
geom_line(aes(y = preds_BLR, frame = n, colour = "BLR")) +
geom_line(aes(y = preds_RLS, frame = n, colour = "RLS")) +
geom_point(aes(x = x, y = target, frame = n, colour = "target"), alpha = 0.3) +
geom_line(aes(x = x, y = sin(2 * pi * x), colour = "sin(2*pi*x)"), alpha = 0.3) +
ggtitle("Comparison of performance) +
labs(y = "predictions and targets", colour = "colours")
This is converted to plotly, and I have added an animation to the Plotly graph:
plot <- ggplotly(p) %>%
animation_opts(easing = "linear",redraw = FALSE)
plot
Thanks!
You can add annotations to a ggplot graph using the annotate function: http://ggplot2.tidyverse.org/reference/annotate.html
df <- data.frame(x = rnorm(100, mean = 10), y = rnorm(100, mean = 10))
# Build model
fit <- lm(x ~ y, data = df)
# function finds RMSE
RMSE <- function(error) { sqrt(mean(error^2)) }
library(ggplot2)
ggplot(df, aes(x, y)) +
geom_point() +
annotate("text", x = Inf, y = Inf, hjust = 1.1, vjust = 2,
label = paste("RMSE", RMSE(fit$residuals)) )
There seems to be a bit of a problem converting between ggplot and plotly. However this workaround here shows a workaround which can be used:
ggplotly(plot) %>%
layout(annotations = list(x = 12, y = 13, text = paste("RMSE",
RMSE(fit$residuals)), showarrow = F))
Here's an example of adding data dependent text using the built in iris dataset with correlation as text to ggplotly.
library(plotly)
library(ggplot2)
library(dplyr)
mydata = iris %>% rename(variable1=Sepal.Length, variable2= Sepal.Width)
shift_right = 0.1 # number from 0-1 where higher = more right
shift_down = 0.02 # number from 0-1 where higher = more down
p = ggplot(mydata, aes(variable1,variable2))+
annotate(geom = "text",
label = paste0("Cor = ",as.character(round(cor.test(mydata$variable1,mydata$variable2)$estimate,2))),
x = min(mydata$variable1)+abs(shift_right*(min(mydata$variable1)-max(mydata$variable1))),
y = max(mydata$variable2)-abs(shift_down*(min(mydata$variable2)-max(mydata$variable2))), size=4)+
geom_point()
ggplotly(p) %>% style(hoverinfo = "none", traces = 1) # remove hover on text

Mix color and fill aesthetics in ggplot

I wonder if there is the possibility to change the fill main colour according to a categorical variable
Here is a reproducible example
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = c(rep('a', times = 10),
rep('b', times = 10)),
val = rep(1:10, times = 2))
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(color = grp,
fill = val,
size = val))
Of course it is easy to change the circle colour/shape, according to the variable grp, but I'd like to have the a group in shades of red and the b group in shades of blue.
I also thought about using facets, but don't know if the fill gradient can be changed for the two panels.
Anyone knows if that can be done, without gridExtra?
Thanks!
I think there are two ways to do this. The first is using the alpha aesthetic for your val column. This is a quick and easy way to accomplish your goal but may not be exactly what you want:
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(alpha=val,
fill = grp,
size = val)) + theme_minimal()
The second way would be to do something similar to this post: Vary the color gradient on a scatter plot created with ggplot2. I edited the code slightly so its not a range from white to your color of interest but from a lighter color to a darker color. This requires a little bit of work and using the scale_fill_identity function which basically takes a variable that has the colors you want and maps them directly to each point (so it doesn't do any scaling).
This code is:
#Rescale val to [0,1]
df$scaled_val <- rescale(df$val)
low_cols <- c("firebrick1","deepskyblue")
high_cols <- c("darkred","deepskyblue4")
df$col <- ddply(df, .(grp), function(x)
data.frame(col=apply(colorRamp(c(low_cols[as.numeric(x$grp)[1]], high_cols[as.numeric(x$grp)[1]]))(x$scaled_val),
1,function(x)rgb(x[1],x[2],x[3], max=255)))
)$col
df
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(
fill = col,
size = val)) + theme_minimal() +scale_fill_identity()
Thanks to this other post I found a way to visualize the fill bar in the legend, even though that wasn't what I meant to do.
Here's the ouptup
And the code
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = factor(c(rep('a', times = 10),
rep('b', times = 10)),
levels = c('a', 'b')),
val = rep(1:10, times = 2)) %>%
group_by(grp) %>%
mutate(scaledVal = rescale(val)) %>%
ungroup %>%
mutate(scaledValOffSet = scaledVal + 100*(as.integer(grp) - 1))
scalerange <- range(df$scaledVal)
gradientends <- scalerange + rep(c(0,100,200), each=2)
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(fill = scaledValOffSet,
size = val)) +
scale_fill_gradientn(colours = c('white',
'darkred',
'white',
'deepskyblue4'),
values = rescale(gradientends))
Basically one should rescale fill values (e.g. between 0 and 1) and separate them using another order of magnitude, provided by the categorical variable grp.
This is not what I wanted though: the snippet can be improved, of course, to make the whole thing less manual, but still lacks the simple usual discrete fill legend.

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.

Plotting points and lines separately in R with ggplot

I'm trying to plot 2 sets of data points and a single line in R using ggplot.
The issue I'm having is with the legend.
As can be seen in the attached image, the legend applies the lines to all 3 data sets even though only one of them is plotted with a line.
I have melted the data into one long frame, but this still requires me to filter the data sets for each individual call to geom_line() and geom_path().
I want to graph the melted data, plotting a line based on one data set, and points on the remaining two, with a complete legend.
Here is the sample script I wrote to produce the plot:
xseq <- 1:100
x <- rnorm(n = 100, mean = 0.5, sd = 2)
x2 <- rnorm(n = 100, mean = 1, sd = 0.5)
x.lm <- lm(formula = x ~ xseq)
x.fit <- predict(x.lm, newdata = data.frame(xseq = 1:100), type = "response", se.fit = TRUE)
my_data <- data.frame(x = xseq, ypoints = x, ylines = x.fit$fit, ypoints2 = x2)
## Now try and plot it
melted_data <- melt(data = my_data, id.vars = "x")
p <- ggplot(data = melted_data, aes(x = x, y = value, color = variable, shape = variable, linetype = variable)) +
geom_point(data = filter(melted_data, variable == "ypoints")) +
geom_point(data = filter(melted_data, variable == "ypoints2")) +
geom_path(data = filter(melted_data, variable == "ylines"))
pushViewport(viewport(layout = grid.layout(1, 1))) # One on top of the other
print(p, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
You can set them manually like this:
We set linetype = "solid" for the first item and "blank" for others (no line).
Similarly for first item we set no shape (NA) and for others we will set whatever shape we need (I just put 7 and 8 there for an example). See e.g. http://www.r-bloggers.com/how-to-remember-point-shape-codes-in-r/ to help you to choose correct shapes for your needs.
If you are happy with dots then you can use my_shapes = c(NA,16,16) and scale_shape_manual(...) is not needed.
my_shapes = c(NA,7,8)
ggplot(data = melted_data, aes(x = x, y = value, color=variable, shape=variable )) +
geom_path(data = filter(melted_data, variable == "ylines") ) +
geom_point(data = filter(melted_data, variable %in% c("ypoints", "ypoints2"))) +
scale_colour_manual(values = c("red", "green", "blue"),
guide = guide_legend(override.aes = list(
linetype = c("solid", "blank","blank"),
shape = my_shapes))) +
scale_shape_manual(values = my_shapes)
But I am very curious if there is some more automated way. Hopefully someone can post better answer.
This post relied quite heavily on this answer: ggplot2: Different legend symbols for points and lines

Resources