I try to use geom_density_2d_filled as background in my animation:
library(tidyverse)
library(gganimate)
mtcars_ <- mtcars
mtcars_ <- rename(mtcars_, mpg_ = mpg, disp_ = disp)
gg <- ggplot(mtcars, aes(x = mpg, y = disp)) + geom_density_2d_filled(data = mtcars_, aes(x = mpg_, y = disp_)) + geom_line() + theme(legend.position = "none")
gg
anim <- gg + transition_reveal(mpg) + shadow_wake(1)
anim
but I have no background in this case.
When I use geom_density_2d instead of geom_density_2d_filled everything is ok. What am I doing wrong?
I think transition_reveal is looking for an mpg variable for each layer.
If you add
mtcars_$mpg = min(mtcars$mpg)
it will plot with the background, but MUCH slower because that layer takes more computing per frame than the line. If that's a problem you might look at an alternative, like saving the background to an image and printing the image as a bottom layer.
Related
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))))
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).
I am trying to identify why I have a purple line appearing along the x axis that is the same color as "Prypchan, Lida" from my legend. I took a look at the data and do not see any issues there.
ggplot(LosDoc_Ex, aes(x = LOS)) +
geom_density(aes(colour = AttMD)) +
theme(legend.position = "bottom") +
xlab("Length of Stay") +
ylab("Distribution") +
labs(title = "LOS Analysis * ",
caption = "*exluding Residential and WSH",
color = "Attending MD: ")
Usually I'd wait for a reproducible example, but in this case, I'd say the underlying explanation is really quite straightforward:
geom_density() creates a polygon, not a line.
Using a sample dataset from ggplot2's own package, we can observe the same straight line below the density plots, covering the x-axis & y-axis. The colour of the line simply depends on which plot is on top of the rest:
p <- ggplot(diamonds, aes(carat, colour = cut)) +
geom_density()
Workaround 1: You can manually calculate the density values yourself for each colour group in a new data frame, & plot the results using geom_line() instead of geom_density():
library(dplyr)
library(tidyr)
library(purrr)
diamonds2 <- diamonds %>%
nest(-cut) %>%
mutate(density = map(data, ~density(.x$carat))) %>%
mutate(density.x = map(density, ~.x[["x"]]),
density.y = map(density, ~.x[["y"]])) %>%
select(cut, density.x, density.y) %>%
unnest()
ggplot(diamonds2, aes(x = density.x, y = density.y, colour = cut)) +
geom_line()
Workaround 2: Or you can take the data generated by the original plot, & plot that using geom_line(). The colours would need to be remapped to the legend values though:
lp <- layer_data(p)
if(is.factor(diamonds$cut)) {
col.lev = levels(diamonds$cut)
} else {
col.lev = sort(unique(diamonds$cut))
}
lp$cut <- factor(lp$group, labels = col.lev)
ggplot(lp, aes(x = x, y = ymax, colour = cut)) +
geom_line()
There are two simple workarounds. First, if you only want lines and no filled areas, you can simply use geom_line() with the density stat:
library(ggplot2)
ggplot(diamonds, aes(x = carat, y = stat(density), colour = cut)) +
geom_line(stat = "density")
Note that for this to work, we need to set the y aesthetic to stat(density).
Second, if you want the area under the lines to be filled, you can use geom_density_line() from the ggridges package. It works exactly like geom_density() but draws a line (with filled area underneath) rather than a polygon.
library(ggridges)
ggplot(diamonds, aes(x = carat, colour = cut, fill = cut)) +
geom_density_line(alpha = 0.2)
Created on 2018-12-14 by the reprex package (v0.2.1)
I am starting to get familiar with gganimate, but I want to extend my gifs further.
For instance, I can throw a frame on one variable in gganimate but what if I want to animate the process of adding entirely new layers/geoms/variables?
Here's a standard gganimate example:
library(tidyverse)
library(gganimate)
p <- ggplot(mtcars, aes(x = hp, y = mpg, frame = cyl)) +
geom_point()
gg_animate(p)
But what if I want the gif to animate:
# frame 1
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point()
# frame 2
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl)))
# frame 3
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl), size = wt))
# frame 4
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl), size = wt)) +
labs(title = "MTCARS")
How might this be accomplished?
You can manually add a frame aesthetic to each layer, though it will include the legends for all of the frames immediately (Intentionally, I believe, to keep ratios/margins, etc. correct:
saveAnimate <-
ggplot(mtcars, aes(x = hp, y = mpg)) +
# frame 1
geom_point(aes(frame = 1)) +
# frame 2
geom_point(aes(color = factor(cyl)
, frame = 2)
) +
# frame 3
geom_point(aes(color = factor(cyl), size = wt
, frame = 3)) +
# frame 4
geom_point(aes(color = factor(cyl), size = wt
, frame = 4)) +
# I don't think I can add this one
labs(title = "MTCARS")
gg_animate(saveAnimate)
If you want to be able to add things yourself, and even see how legends, titles, etc. move things around, you may need to step back to a lower-level package, and construct the images yourself. Here, I am using the animation package which allows you to loop through a series of plots, with no limitations (they need not be related at all, so can certainly show things moving the plot area around. Note that I believe this requires ImageMagick to be installed on your computer.
p <- ggplot(mtcars, aes(x = hp, y = mpg))
toSave <- list(
p + geom_point()
, p + geom_point(aes(color = factor(cyl)))
, p + geom_point(aes(color = factor(cyl), size = wt))
, p + geom_point(aes(color = factor(cyl), size = wt)) +
labs(title = "MTCARS")
)
library(animation)
saveGIF(
{lapply(toSave, print)}
, "animationTest.gif"
)
The gganimate commands in the earlier answers are deprecated as of 2021 and won't accomplish OP's task.
Building on Mark's code, you can now simply create a static ggplot object with multiple layered geoms and then add the gganimate::transition_layers function to create an animation that transitions from layer to layer within the static plot. Tweening functions like enter_fade() and enter_grow() control how elements change into and out of frames.
library(tidyverse)
library(gganimate)
anim <- ggplot(mtcars, aes(x = hp, y = mpg)) +
# Title
labs(title = "MTCARS") +
# Frame 1
geom_point() +
# Frame 2
geom_point(aes(color = factor(cyl))) +
# Frame 3
geom_point(aes(color = factor(cyl), size = wt)) +
# gganimate functions
transition_layers() + enter_fade() + enter_grow()
# Render animation
animate(anim)
the animation package doesn't force you to specify frames in the data. See the example at the bottom of this page here, where an animation is wrapped in a big saveGIF() function. You can specify the duration of individual frames and everything.
The drawback to this is that, unlike the nice gganimate functions, the basic frame-by-frame animation wont hold the plot dimensions/legend constant. But if you can hack your way into displaying exactly what you want for each frame, the basic animation package will serve you well.
On the below plot, I'd like to create an empty space on the x-axis at position 3. In other word, I'd like that the dots for wt>=3 and the axis scale when wt>=3 are shifted to the right side by some arbitrary chosen value. Does it make sense?
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
I could simply modify the my data and add 0.5 to each value of mpg where wt>=3 but it doesn't solve the issue with the x-axis.
I might rephrase my question saying that on the below graph I'd like that the vertical line does not overlap the data and therefore all the data (and also the x-axis) should be shifted to the left by the thickness of the vertical line.
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + geom_vline(xintercept=3, size=30)
I was thinking about facet_wrap or viewport. Maybe something like adding a constant to each value of mpg where wt>=3 and then, manually set the values for the x-axis.
Not entirely sure what you are looking for, and I get confused by watching the weird axis on my own plot... Something like this?
mtcars$wt2 <- with(mtcars, ifelse(wt > 3, wt+1, wt))
ggplot(mtcars, aes(x = wt2, y = mpg)) +
geom_point() +
annotate("rect", xmin = 3, xmax = 4, ymin = 0, ymax = 35, alpha = 0.2) +
scale_x_continuous(breaks = round(mtcars$wt2), label = round(mtcars$wt))
Similar to # Frank, and tested...
x <- mtcars
x$group <- ""
x[x$wt<3,]$group <- "1.LIGHT"
x[x$wt>=3,]$group <- "2.HEAVY"
library(ggplot2)
library(grid) #for unit(...)
ggplot(x, aes(x=wt,y=mpg)) +
geom_point() +
facet_grid(~group,scales="free",space="free") +
theme(panel.margin = unit(2, "lines"))
Produces this:
what about something like this?
mtcars <- transform(mtcars, split = ifelse(wt >= 3, "b", "a" ))
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
facet_wrap(~split, scales = "free_x")