Wiggling lines when animating ggplot choropleth with gganimate - r

I have a problem when using gganimate to animate a choropleth map made with ggplot2.
A selection of my data is available here and the code is:
library(tidyverse)
library(gganimate)
part_data <- readRDS(file = "part_data.Rds")
p <- part_data %>%
ggplot(aes(x = long, y = lat, group = group)) +
geom_polygon(aes(fill = confirmed), color = "grey70", size = 0.05) +
coord_map() +
scale_fill_distiller(trans = "log10", direction = 1, palette = "YlOrRd", na.value = "white") +
transition_time(time = date)
animate(p,
fps = 3,
duration = 5,
renderer = gifski_renderer("countyevolution.gif"),
width = 1200, height = 750, res = 100)
which yields this GIF:
Perhaps it's difficult to see but if you look closely you'll see that the borders between the counties are wiggling. This does not happen when I use transition_manual without transitions between the dates, so it must come from these transitions. But why?
Is it possible to somehow tell gganimate to keep the borders the same and only render the fill for each frame? Or can I somehow else make the border less obvious? I tried decreasing the size, but that does not seem to make a difference. Also, the borders seem a bit jagged.

This also happened to em recently: animated plots that contain maps or other static elements become wiggly or blink. I realized the wiggle happens because gganimate is also animating the map lines in each step of the animation. This explains why using other transition modes or removing transition animations solve the blinking.
As stated in this question, to exclude a ggplot layer from being animated you need to add a data argument. This way, the map or plot in the background will remain static, and the rest of the plot will animate.

Related

In r ggplot, is it possible to adjust the spacing between boxplots on the x axis?

Say you want a simple boxplot, without any subgroups:
some_data %>%
ggplot(aes(x=factor_1,
y=some_outcome)) +
geom_boxplot(width=0.2,notch = TRUE)
How do you adjust the space between the two boxes, without changing the box width? So I just want to move the boxes closer or further away.
No online resourse seems to tackle this specific question. They either show how to change the box width, or they discuss the grouped boxplots that use the fill argument and how to adjust the spacing between the grouped boxes using position_dodge.
Thanks a lot!
You need to remember that you are drawing a plot into a plotting window with a fixed width. If you want the boxes to be closer together but remain the same size, and you don't want to change the size of the plotting window, then something needs to take up the extra space you have created. This can be achieved either by expanding the x-axis range of the plotting panel itself, or the margins around the plotting panel.
Let's start with a concrete example of data so we can run your code:
library(ggplot2)
library(dplyr)
set.seed(1)
some_data <- data.frame(factor_1 = c("0", "1"),
some_outcome = rnorm(200))
Now we can use your plotting code to get a reasonable replica of your plot:
some_data %>%
ggplot(aes(x=factor_1,
y=some_outcome)) +
geom_boxplot(width = 0.2, notch = TRUE)
Now, we can move the boxes closer together by expanding the x-axis range like this:
some_data %>%
ggplot(aes(x=factor_1,
y=some_outcome)) +
geom_boxplot(width = 0.67, notch = TRUE) +
scale_x_discrete(expand = c(2, 2))
You can see that the extra space is taken up with some additional room in the panel on either side of the boxes.
An alternative is to make the panel narrower by increasing the margins around it, thereby allowing whitespace to take up the extra room:
some_data %>%
ggplot(aes(x=factor_1,
y=some_outcome)) +
geom_boxplot(width = 0.5, notch = TRUE) +
theme(plot.margin = margin(10, 150, 10, 150))
I think the second option looks nicer. However, you would get a similar look if you just make your plotting window the correct size, then use your original plotting code (with appropriately selected width parameter)
# Drag plotting window to correct size, then run:
some_data %>%
ggplot(aes(x=factor_1,
y=some_outcome)) +
geom_boxplot(width = 0.4, notch = TRUE)

Bounding position for geom_text()

I am making several instances of a tilted bar chart. As the sizes of count and the differences in percent vary, part of one of the labels (count) is pushed outside the bar in some instances. I need the labels to be entirely inside the bar in all instances. If not repositioned to fit inside the bar, I need the labels to be centered as is.
The code is:
library(tidyverse)
library(ggplot2)
data <- tibble(type = c('Cat', 'Dog'),
group = c('Pets', 'Pets'),
count = c(10000, 990000),
percent = c(1, 99))
ggplot(data, aes(x = group, y = percent, fill = type)) +
geom_bar(stat = 'identity',
position = position_stack(reverse = TRUE)) +
coord_flip() +
geom_text(aes(label = count),
position = position_stack(vjust = 0.5,
reverse = TRUE))
Use hjust="inward":
ggplot(data, aes(x = group, y = percent, fill = type)) +
geom_bar(stat = 'identity', position = position_stack(reverse = TRUE)) +
coord_flip() +
geom_text(aes(label = count), hjust = "inward", position = position_stack(vjust = 0.5, reverse = TRUE))
One thing key to note here is that plots in ggplot are drawn differently depending on the graphics device resolution, width, and height settings. This is why plots look a bit different depending on the computer you use to plot them. If I take your default graph and save different aspect ratios, this becomes evident:
width=3, height=5
width=7, height=5
The aspect ratio and resolution change the plot. You can also see this for yourself within R studio by just resizing the plot viewer window.
With that being said, there are some options to adjust your plot to be less likely to clip text out of bounds:
Rotate your text or rotate your plot back to horizontal bars. For long text labels, they are going to work out better with horizontal bars anyway.
geom_text_repel from the ggrepel package. Direct replacement of geom_text puts your labels in the plot area, and you can use min.segment.length= to specify the minimum line length as well as force= and direction= to play with positioning. Again, works better if you flip back your chart.
Use the expand= argument applied to scale_y_continuous. Try adding scale_y_continuous(expand=c(0.25,0.25)) to your plot, for example. Note that since your coordinate system is flipped, you have to specify "y" to expand "x". This expands the plot area around the geoms.
Change the output width= and height= and resolution when exporting your plots. As indicated above, this is the simple solution.
There are probably other suggestions, but that's mine.

Save plot as SVG removing stroke on filled symbols

I have been using filled symbols in ggplot2 and never had any problems exporting to png. However, when I export to svg, for some shapes (triangles and diamonds) one side of the symbol has no stroke.
This initially occurred when I was plotting some maps, so I have tried to replicate the issue with just simple example plots - it's still happening. I can't fathom what is causing one side of the symbols to disappear like this. Is this something I am failing to specify when exporting as svg? Or is there a bug somewhere? Any help would be much appreciated.
Here's an example:
And the code that created this image:
library(tidyverse)
plot =
data_frame(x = 1:5, y = 1:5, group = c("tri", "sq", "tri", "sq", "dia")) %>%
ggplot(aes(x,y, shape = group)) +
geom_point(fill = "red", colour = "black", size = 4) +
scale_shape_manual(values = c(23,22,24)) +
theme_bw()
plot %>% ggsave("test.svg", ., height = 10, width = 10, units = "cm")
Note: I have tried using svglite() directly to export - same thing happens.
This seems like a bug in svglite:
Missing edges in svg file for some point characters when background color is set
First reported on ggplot2 github:
ggsave missing edges with some shapes in svg format

Colour just the top border of geom_bar

A chart with a lot of bars looks very squished, for instance -
ggplot(data.frame(x = 1:1000, y = (rnorm(1000)), fill = sample(c('a','b','c'), 1000, replace = T)), aes(x, y, fill = fill)) + geom_bar(stat = 'identity')
I have a chart for a similar dataset which I feel I will be able to make more sense out of by just colouring the top border of the bar. I'm unable to achieve this. The closest I can do is incorporate a geom_step but this also adds vertical lines where the y value changes and this crowds the chart even more. geom_point sizes aren't necessarily synced with the separation on the x axis so they spill over to the side for small x values. The only sure shot solution I'm able to think of is to actually manipulate the data such that I'm able to draw geom_segments to do my work for me. Is there any other way
PS: I need to stick to this format for reasons.
You could use geom_errorbar() and set ymin= and ymax= to your y values. Then you can play with width= and size= to get the look you need.
ggplot(data.frame(x = 1:1000, y = (rnorm(1000)), fill = sample(c('a','b','c'), 1000, replace = T))) +
geom_errorbar(aes(x=x,ymin=y,ymax=y,color=fill),size=0.5,width=3)

R - Add legend to ggmap when no data is available

I am trying to figure out how to display a map including the legend with ggmap/ggplot.
I have gotten so far:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_blank() +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]'))+
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
This generates the correct map in the correct bounding box. But even thought I have specified: "scale_colour_manual" and "scale_size_manual" with "drop = FALSE", no legend is appearing. How can I have the legend shown when no data is to be displayed?
The overall intention is to create a single map of a given interval in a time series. Now the problem is that some intervals have no data and so the map is displayed without a scale. If the map does not have a scale the dimensions of the map are different making it impossible to create a movie out of the different maps. That is why I need to be able to create a map WITHOUT data but WITH the legend showing.
Thank you.
Taking Jaap's comment into account, that I have to call a legend in aes I have been able to achieve what I want with following code:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_point(aes(x=0,y=0, color=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE), size=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE))) +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]')) +
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
I know this is not the most elegant way, but it works for now.
I basically make a dummy point outside of the bounding box to be displayed. I then give the point a value, which is cut according to the breaks I want and then colored and sized accordingly. Just remember to put the values of x and y in aes outside of the bounding box.
Better solutions are welcome.

Resources