How to Get Multiple Ggplot2 Scale_Fill_Gradientn with Same Scale

How to get multiple ggplot2 scale_fill_gradientn with same scale?

Use limit in scale_gradientn:

p1 <- ggplot(as.data.frame(d1))
p1 <- p1 + geom_tile(aes(x = x, y = y, fill = val))

p2 <- ggplot(as.data.frame(d2), aes(x = x, y = y, fill = val))
p2 <- p2 + geom_tile(aes(x = x, y = y, fill = val))
library(gridExtra)

p1 <- p1 + scale_fill_gradientn(colours = myPalette(4), limits=c(0,4))
p2 <- p2 + scale_fill_gradientn(colours = myPalette(4), limits=c(0,4))
grid.arrange(p1, p2)

Sample Image

The grid.arrange stuff is just to avoid me having to copy/paste two pictures.

How to combine multiple ggplot2 objects on the same scale

Check out facet_wrap.

library(ggplot2)

mymatrix1 <- matrix(data = 0, nrow = 105, ncol =2)
mymatrix2 <- matrix(data = 0, nrow = 108, ncol =2)
mymatrix3 <- matrix(data = 0, nrow = 112, ncol =2)

mymatrix1[,1] <- sample(0:1, 105, replace= TRUE)
mymatrix1[,2] <- rnorm(105, 52, 2)
mymatrix2[,1] <- sample(0:1, 108, replace= TRUE)
mymatrix2[,2] <- rnorm(108, 60, 2)
mymatrix3[,1] <- sample(0:1, 112, replace= TRUE)
mymatrix3[,2] <- rnorm(112, 70, 2)

mydata <- list(mymatrix1, mymatrix2,mymatrix3)

for(i in 1:3){
mydata[[i]] <- cbind(mydata[[i]], i)
colnames(mydata[[i]]) <- c("class", "readcount", "group")
}

mydata <- as.data.frame(do.call(rbind, mydata))

## fixed scales
p <- qplot(class, readcount, data = mydata, geom="boxplot", fill = factor(class)) +
geom_jitter(position=position_jitter(w=0.1,h=0.1)) +
scale_x_continuous(breaks=c(0,1), labels=c("0", "1")) +
facet_wrap(~ group)

## free scales
p + facet_wrap(~ group, scales = "free")

Sample Image
Sample Image

creating 2 separate ggplots with same color scale

The problem is that you have reversed the fill scale with trans = "reverse", so the limits also need to be inverted:

as.data.frame(r1, xy=TRUE) %>%drop_na() %>% 
ggplot(aes(x=x, y=y)) +
geom_raster(aes(fill = test)) +
scale_fill_gradientn(
colours = hcl.colors(10, "YlGnBu"),trans = "reverse",
breaks =c(505, 1100, 1600), limits = c(2000, 0))

Sample Image

as.data.frame(r2, xy=TRUE) %>%drop_na() %>% 
ggplot(aes(x=x, y=y)) +
geom_raster(aes(fill = test)) +
scale_fill_gradientn(
colours = hcl.colors(10, "YlGnBu"),trans = "reverse",
breaks =c(505, 1100, 1600), limits = c(2000, 0))

Sample Image

Using more than one `scale_fill_` in `ggplot2`

One way to get around the limitation is to map to color instead (as you already hinted to). This is how:

We keep the underlying raster plot, and then add:

plot +
stat_density_2d( data = bar,
mapping = aes( x = x, y = y, col = ..level.. ),
geom = "path", size = 2 ) +
scale_color_gradientn(
colours = rev( brewer.pal( 7, "Spectral" ) )
) + xlim( 0, 10 ) + ylim( 0, 10 )

This gives us:

Sample Image

This is not entirely satisfying, mostly because the scales have quite a bit of perceptive overlap (I think). Playing around with different scales can definitely gives us a better result:

plot <- ggplot() +
geom_tile( data = foo,
mapping = aes( x = Var1, y = Var2, fill = value ) ) +
viridis::scale_fill_viridis(option = 'A', end = 0.9)

plot +
stat_density_2d( data = bar,
mapping = aes( x = x, y = y, col = ..level.. ),
geom = "path", size = 2 ) +
viridis::scale_color_viridis(option = 'D', begin = 0.3) +
xlim( 0, 10 ) + ylim( 0, 10 )

Sample Image

Still not great in my opinion (using multiple color scales is confusing to me), but a lot more tolerable.

match fill gradient across different plots

Add limits to each plot:

ggd1 <- ggplot(d1, aes(x=x,y=y)) + 
geom_tile(aes(fill=num)) +
scale_fill_gradient(low = "green", high = "blue", limits=c(1, 30))
ggd2 <- ggplot(d2, aes(x=x,y=y)) +
geom_tile(aes(fill=num)) +
scale_fill_gradient(low = "green", high = "blue", limits=c(1, 30))
grid.arrange(ggd1,ggd2)

Sample Image

How to specify low and high and get two scales on two ends using scale_fill_gradient

You can try adding a white midpoint to scale_fill_gradient2:

gg <- ggplot(nba.s, aes(variable, Name))
gg <- gg + geom_tile(aes(fill = rescale), colour = "white")
gg <- gg + scale_fill_gradient2(low = "darkgreen", mid = "white", high = "darkred")
gg <- gg + labs(x="", y="")
gg <- gg + theme_bw()
gg <- gg + theme(panel.grid=element_blank(), panel.border=element_blank())
gg

Sample Image

But, you'll have the most flexibility if you follow the answer in the SO post you linked to and use scale_fill_gradientn.

EDIT (to show an example from the comment discussion)

# change the "by" for more granular levels

green_seq <- seq(-5,-2.000001, by=0.1)
red_seq <- seq(2.00001, 5, by=0.1)

nba.s$cuts <- factor(as.numeric(cut(nba.s$rescale,
c(green_seq, -2, 2, red_seq), include.lowest=TRUE)))

# find "white"
white_level <- as.numeric(as.character(unique(nba.s[nba.s$rescale >= -2 & nba.s$rescale <= 2,]$cuts)))
all_levels <- sort(as.numeric(as.character(unique(nba.s$cuts))))

num_green <- sum(all_levels < white_level)
num_red <- sum(all_levels > white_level)

greens <- colorRampPalette(c("#006837", "#a6d96a"))
reds <- colorRampPalette(c("#fdae61", "#a50026"))

gg <- ggplot(nba.s, aes(variable, Name))
gg <- gg + geom_tile(aes(fill = cuts), colour = "white")
gg <- gg + scale_fill_manual(values=c(greens(num_green),
"white",
reds(num_red)))
gg <- gg + labs(x="", y="")
gg <- gg + theme_bw()
gg <- gg + theme(panel.grid=element_blank(), panel.border=element_blank())
gg <- gg + theme(legend.position="bottom")
gg

Sample Image

The legend is far from ideal, but you can potentially exclude it or work around it through other means.

Distinct scale_fill_gradient for each group in ggplot2

I came up with the workaround below as a curiosity, but I don't think it's really good practice, as far as data visualisation goes. Having a single varying gradient in a density chart is shaky enough; having multiple different ones won't be any better. Please don't use it.

plot

Preparation:

ggplot(iris, aes(Sepal.Length, as.factor(Species))) +
geom_density_ridges_gradient()
# plot normally & read off the joint bandwidth from the console message (0.181 in this case)

# split data based on the group variable, & define desired gradient colours / midpoints
# in the same sequential order.
split.data <- split(iris, iris$Species)
split.grad.low <- c("blue", "red", "yellow") # for illustration; please use prettier colours
split.grad.high <- cols
split.grad.midpt <- c(4.5, 6.5, 7) # for illustration; please use more sensible points

# create a separate plot for each group of data, specifying the joint bandwidth from the
# full chart.
split.plot <- lapply(seq_along(split.data),
function(i) ggplot(split.data[[i]], aes(Sepal.Length, Species)) +
geom_density_ridges_gradient(aes(fill = ..x..),
bandwidth = 0.181) +
scale_fill_gradient2(low = split.grad.low[i], high = split.grad.high[i],
midpoint = split.grad.midpt[i]))

Plot:

# Use layer_data() on each plot to get the calculated values for x / y / fill / etc,,
# & create two geom layers from each, one for the gradient fill & one for the ridgeline
# on top. Add them to a new ggplot() object in reversed order, because we want the last
# group to be at the bottom, overlaid by the others where applicable.
ggplot() +
lapply(rev(seq_along(split.data)),
function(i) layer_data(split.plot[[i]]) %>%
mutate(xmin = x, xmax = lead(x), ymin = ymin + i - 1, ymax = ymax + i - 1) %>%
select(xmin, xmax, ymin, ymax, height, fill) %>%
mutate(sequence = i) %>%
na.omit() %>%
{list(geom_rect(data = .,
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill)),
geom_line(data = .,
aes(x = xmin, y = ymax)))}) +

# Label the y-axis labels based on the original data's group variable
scale_y_continuous(breaks = seq_along(split.data), labels = names(split.data)) +

# Use scale_fill_identity, since all the fill values have already been calculated.
scale_fill_identity() +
labs(x = "Sepal Length", y = "Species")

Note that this method won't create a fill legend. If desired, it's possible to retrieve the fill legends from the respective plots in split.plot via get_legend & add them to the plot above via plot_grid (both functions from the cowplot package), but that's like adding frills to an already weird visualisation choice...

ggplot/ggtern manually mapping multiple gradients with scale_fill_gradient

The whole point of the grammar of graphics (basis for ggplot, and hence ggtern) is to make aesthetic mappings in such a way that they are non-confusing for the reader, this is why the same mapping is shared amongst the facets.

The only way that I am aware, for you to achieve something along the lines of what you are seeking, is to plot each facet individually, then combine them. See the code below:

library(ggtern)
my.data <- read.table("~/Desktop/ggtern_sample.txt",header=T) #Edit as required

plots <- list()
for(x in sort(unique(my.data$TypeID))){
df.sub <- my.data[which(my.data$TypeID == x),]
scales_gradient = if(x %% 2 == 0){
scale_fill_gradient(low="green",high = "red")
}else if(x == 5){
scale_fill_gradient(low="#FED9A9",high = "#F08600")
}else{
scale_fill_gradient(low="blue",high = "magenta")
}

#Build the plot for this subset
base <- ggtern(data = df.sub, aes(x=x, y=y, z=z, label = ID)) +
stat_density2d(method = "lm", fullrange = T,
n = 100, geom = "polygon",
aes(fill = ..level..,
alpha = ..level..))+
scale_alpha(guide='none') +
coord_tern(L="x",T="y",R="z") +
theme_anticlockwise() +
scales_gradient +
geom_text(color="black", size = 3)+
facet_wrap(~ TypeID, ncol = 2)

#Add to the list
plots[[length(plots)+1]] = base
}

#Add to multiplot
ggtern.multi(plotlist = plots,cols=2)

This produces the following:

Sample



Related Topics



Leave a reply



Submit