I am trying to make a ggplot2 graph in which I have a "classic" dot plot together with a table.
This is an example of the plot I am making:
library(dplyr)
library(tibble)
library(ggplot2)
library(ggpmisc)
mtcars[1:10,] %>%
select(cyl, mpg, wt) -> tb
df <- tibble(x = 5.45, y = 34, tb = list(tb))
# using defaults
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb))
This is the output of the code:
I would however the rows of the table be coloured according to the factor cyl. So every rows which has carb==4 is red, ==6 is green and ==8 is blue.
I don't need the column cyl to appear in the table, so that can be removed (if possible).
Is there a way to do it?
Thanks a lot in advance for your precious help,
Luca
Unlike most of the ggplot ecosystem, you can't just use a fill aesthetic inside geom_table, and instead need to create a table theme which gets passed to table.theme inside geom_table. This means it is somewhat inconvenient to achieve the desired effect, but it is possible with something like the following:
thm <- gridExtra::ttheme_default()
fills <- scales::hue_pal()(3)[as.numeric(factor(df$tb[[1]]$cyl))]
thm$core$bg_params$fill <- fills
df$tb[[1]] <- df$tb[[1]][-1]
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb), table.theme = thm)
It is a bit hacky, but using the table.theme argument, you can supply the table with your own background colors (see Controlling the colors of rows in ggplot table annotation).
First, we construct a vector which contains a required background color for each row. Then, we use those colors in the table.theme argument of geom_table.
library(dplyr)
library(tibble)
library(ggplot2)
library(ggpmisc)
mtcars[1:10,] %>%
select(cyl, mpg, wt) -> tb
df <- tibble(x = 5.45, y = 34, tb = list(tb))
# Construct vector of required background colors (from default ggplot2 theme)
colors <- scales::hue_pal()(n_distinct(tb$cyl))
colors <- colors[factor(tb$cyl)]
# using defaults
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl), fill = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb),
table.theme = gridExtra::ttheme_default(core = list(bg_params=list(fill=colors))))
Related
I would like to map a variable to a continuous color scale, but if the value is a below a certain threshold, it should have a specific color. In this example, I would like to make all car models with an mpg < 20 filled with gray, and everything above with the continuous color scale:
library(ggplot2)
dat <- head(mtcars)
dat$model <- head(colnames(mtcars))
dat$is_low <- ifelse(dat$mpg < 20, TRUE, FALSE)
ggplot(dat, aes(x = model, y = mpg, fill = mpg)) +
geom_col()
Is that even possible? The closest I have seen is this solution but it maps a second variably to scale_color and not to scale_fill_continuous
Not the most elegant, but you could add a layer that's filtered to just the ones you want gray:
ggplot(dat, aes(x = model, y = mpg, fill = mpg)) +
geom_col() +
geom_col(data = dat[dat$is_low,], fill = "gray70")
Add a second layer of geom_col() where you either color each bar grey or skip if it doesn't meet your criteria. You may also want to change the limits of your coloring scale for the first layer.
ggplot(dat, aes(x = model, y = mpg, fill = mpg)) +
geom_col() + geom_col(fill=ifelse(dat$mpg < 20, 'gray', NA))
Is there a way to specify that a custom_annotation only applies to one facet of a ggplot?
For example, if I run the following code
library(tidyverse)
library(grid)
text_grob=grobTree(textGrob("text",x=0.5, y=0.6, rot=90,
gp=gpar(col="red")))
ggplot(mtcars, aes(x=mpg, y =drat))+
geom_point() +
facet_wrap(~cyl) +
annotation_custom(overrep_grob)
I get this
How can I only keep the rightmost red "text" annotation and not add the "text" annotation to the first two facets? Note I can't use geom_text or annotate because I need to make use of textGrob's relative text positioning
egg has geom_custom,
library(ggplot2)
library(grid)
library(egg)
d = data.frame(cyl=6, drat = 4, mpg = 15)
d$grob <- list(textGrob("text",rot=90, hjust = 0, gp=gpar(col="red")))
ggplot(mtcars, aes(x=mpg, y=drat))+
geom_point() +
facet_wrap(~cyl) +
geom_custom(data = d, aes(data = grob), grob_fun = identity)
You could also do this using geom_text by calculating the relative position of the text needed. Note that here, the relative position is slightly different than the one you use above because here I define the relative position as some proportion of the dynamic range. You can choose a different value for rel to get the position you need. I find that this way makes the positioning less arbitrary.
library(tidyverse)
rel_pos <- function(.data, var, rel){
var <- enquo(var)
.data %>%
summarise(x = sum(max(!!var), min(!!var))* rel) %>% .[1, "x"]
}
my_text <- data_frame(mpg = rel_pos(mtcars, mpg, 0.5),
drat = rel_pos(mtcars, drat, 0.6) ,
cyl = 8, lab = "text")
ggplot(mtcars, aes(x=mpg, y =drat))+
geom_point() +
facet_wrap(~cyl)+
geom_text(data = my_text, aes(label = lab), color = "red", angle = 90)
Created on 2018-08-15 by the reprex
package (v0.2.0).
When I try to use ggplotly(), I lose the legend that explains the color scale. If I use color instead of size the legend appears. How do I get the legend to appear when using size?
library(dplyr)
library(plotly)
plot <- mtcars %>%
ggplot(aes(x = mpg, y = cyl, size = disp, frame = hp)) +
geom_point()
ggplotly(plot) %>%
layout(showlegend = T)
I think the issue is that using as size a numeric variable such as disp will create a very large legend e.g.:
p <- ggplot(data=mtcars, aes(x = mpg, y = cyl, colour=factor(disp), size = factor(disp))) +
geom_point()
p <- ggplotly(p)
p
PS: I'm not sure you can use frame and still have the desired legend using ggplotly.
Option 2: Use directly plot_ly e.g.:
plot_ly(mtcars,x = ~ mpg, y= ~ cyl, size = ~disp , type='scatter', mode = 'markers', frame = ~disp) %>%
layout(showlegend = T)
I want to gradient fill a violin plot based on the density of points in the bins (blue for highest density and red for lowest).
I have generated a plot using the following commands but failed to color it based on density (in this case the width of the violin. I also would like to generate box plots with similar coloring).
library("ggplot2")
data(diamonds)
ggplot(diamonds, aes(x=cut,y=carat)) + geom_violin()
to change the colour of the violin plot you use fill = variable, like this:
ggplot(diamonds, aes(x=cut,y=carat)) + geom_violin(aes(fill=cut))
same goes for boxplot
ggplot(diamonds, aes(x=cut,y=carat)) + geom_boxplot(aes(fill=cut))
but whatever value you have has to have the same value for each cut, that is, if you wanted to use for example mean depth/cut as the color variable you would have to code it.
with dplyr group your diamonds by cut and with summarize get the mean depth (or any other variable)
library(dplyr)
diamonds_group <- group_by(diamonds, cut)
diamonds_group <- summarize(diamonds_group, Mean_Price = mean(price))
Then I used diamonds2 as a copy of diamonds to then manipulate the dataset
diamonds2 <- diamonds
I merge both dataframes to get the Mean_Depth as a variable in diamonds2
diamonds2 <- merge(diamonds2, diamonds_group)
And now I can plot it with mean depth as a color variable
ggplot(diamonds2, aes(x=cut,y=carat)) + geom_boxplot(aes(fill=Mean_Price)) + scale_fill_gradient2(midpoint = mean(diamonds2$price))
Just answered this for another thread, but believe it's possibly more appropriate for this thread. You can create a pseudo-fill by drawing many segments. You can get those directly from the underlying data in the ggplot_built object.
If you want an additional polygon outline ("border"), you'd need to create this from the x/y coordinates. Below one option.
library(tidyverse)
p <- ggplot(diamonds, aes(x=cut,y=carat)) + geom_violin()
mywidth <- .35 # bit of trial and error
# all you need for the gradient fill
vl_fill <- data.frame(ggplot_build(p)$data) %>%
mutate(xnew = x- mywidth*violinwidth, xend = x+ mywidth*violinwidth)
# the outline is a bit more convoluted, as the order matters
vl_poly <- vl_fill %>%
select(xnew, xend, y, group) %>%
pivot_longer(-c(y, group), names_to = "oldx", values_to = "x") %>%
arrange(y) %>%
split(., .$oldx) %>%
map(., function(x) {
if(all(x$oldx == "xnew")) x <- arrange(x, desc(y))
x
}) %>%
bind_rows()
ggplot() +
geom_polygon(data = vl_poly, aes(x, y, group = group),
color= "black", size = 1, fill = NA) +
geom_segment(data = vl_fill, aes(x = xnew, xend = xend, y = y, yend = y,
color = violinwidth))
Created on 2021-04-14 by the reprex package (v1.0.0)
I want to add interaction to the fill argument of my plotly figure, as e.g. answered here.
However, although the ggplot figure comes out correctly, the plotly does not. I am using the following versions and show a MWE below:
ggplot2: version 2.0.0.9000, plotly: version 2.0.19
library(plotly)
g <- ggplot(mtcars, aes(x = factor(cyl), y = mpg, fill = interaction(factor(cyl),carb))) + geom_boxplot()
(gg <- ggplotly(g))
Any ideas why g and gg differs above?
Not a complete solution. Include the interaction to your x term:
# use lex.order to obtain correct ordered levels
a <- ggplot(mtcars, aes(x = interaction(cyl, carb, lex.order = T), y = mpg,fill = interaction(cyl, carb, lex.order = T))) +
geom_boxplot()
# check the plot
a
# plotly
ggplotly(a)
actually there is an alternative that will give you exactly what you want.
mtcars$intec <- interaction(factor(cyl),carb)
mtcars %>%
plot_ly(x = cyl, y = mpg, type = "box", color = as.factor(intec), fill=as.factor(intec)) %>%
layout(boxmode = "group")