library(tidyverse)
13 Data Viz
13.1 Axis Breaks
13.1.1 {ggbreak}
<- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
df
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
::scale_y_break(c(5, 490)) ggbreak
13.1.2 {ggforce}::facet_zoom
<- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
df
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
::facet_zoom(ylim = c(0, 5)) ggforce
13.2 Annotation outside panel
The one time I’ve done this is to add a axis break symbol for when the y axis does not start at zero. In addition to adding the symbol, you also have to set the ggplotGrob to not clip items outside the plot panel.
<- mpg %>%
base ggplot(aes(x = cty, y = hwy)) +
geom_point() +
lims(x = c(0, NA)) +
theme_minimal() +
theme(axis.line = element_line(color = "grey35", size = 0.25))
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
ℹ Please use the `linewidth` argument instead.
<- base +
p.zoomin annotate("text"
x = -Inf, y = -Inf #bottom left corner of plot
, label = "\u2e17" #symbol ⸗ (diag double hyphen)
, color = "grey35" #same color as axis.line
, size = 4.5 #adjust based on out plot size
, vjust = -0.25 #adjust further up
,
)
#get grob instructions
<- ggplotGrob(p.zoomin)
g.zoomin
#turn panel clipping off
$layout$clip[g.zoomin$layout$name == "panel"] = "off"
g.zoomin
#draw final
::ggdraw(
cowplot::add_sub(
cowplot
g.zoomin"'broken axis' symbol, also extra note under plot"
, size = 12
, x = 0
, hjust = 0
, #, y = 1.25 #same line as x axis title
))
13.3 Secondary Legend
13.3.1 NA Legend
Added automatically if NA is part of discrete legend, but is NOT included automatically for continuous legend.
set.seed(20220713)
<- urbnmapr::get_urbn_map(map = "states", sf = TRUE) %>%
states_sf #set new CRS
::st_transform(states_sf, crs = 4326) %>%
sfmutate(
val_dis = sample(c("A", "B", "C"), nrow(.), TRUE)
val_con = sample(-50:50 , nrow(.), TRUE)
, %>%
) mutate(
val_dis = ifelse(state_abbv == "MN", NA, val_dis)
val_con = ifelse(state_abbv == "MN", NA, val_con)
, )
old-style crs object detected; please recreate object with a recent sf::st_crs()
ggplot(states_sf) + geom_sf(aes(fill = val_dis))
ggplot(states_sf) + geom_sf(aes(fill = val_con))
Add NA
legend to continuous plot:
ggplot(states_sf) +
geom_sf(aes(fill = val_con, color = "")) +
scale_fill_continuous(na.value = "grey40") +
scale_color_manual(values = NA) +
guides(
color = guide_legend("NA", override.aes = list(fill = "grey40"))
fill = guide_colorbar(order = 1)
, )
13.3.2 2+ Special Values
library(cowplot)
Attaching package: 'cowplot'
The following object is masked from 'package:lubridate':
stamp
#special value for 'suppressed' data
<- states_sf %>%
states_sf mutate(val_con = ifelse(state_abbv == "CA", 999, val_con))
## Main Plot
<- ggplot(states_sf) +
p.main geom_sf( # non-special values, typical cont. color bar
data = filter(states_sf, val_con != 999 & !is.na(val_con))
aes(fill = val_con), color = NA
, +
) geom_sf( # NA states (missing data)
data = filter(states_sf, is.na(val_con))
fill = "grey30", color = NA
, +
) geom_sf( # Special values (suppressed data)
data = filter(states_sf, val_con == 999)
fill = "grey80", color = NA
, +
) geom_sf( # add borders for all
fill = NA, color = "white"
)
## Dummy Plot for 2nd Legend
<- tibble(val = c(0, 1), type = c("Missing", "Suppressed")) %>%
p.missup ggplot() +
geom_bar(aes(val, fill = type)) +
scale_fill_manual(name = NULL
values = c("grey30", "grey80")
, guide = guide_legend( byrow = TRUE)
, +
) theme(legend.spacing.y = unit(5, "pt"))
## Pull out legends and combine
<- cowplot::get_legend(p.main)
leg.main <- cowplot::get_legend(p.missup)
leg.sec <- cowplot::plot_grid(leg.main, leg.sec, ncol = 1, align = "v")
leg.final
## Remvoe main legend from plot
<- p.main + theme(legend.position = "none")
p.final
## Combined everything
::plot_grid(p.final, leg.final, nrow = 1, rel_widths = c(4, 1)) cowplot
OUTSTANDING
- how to minimize space between main legend and secondary legend
grid::grid.draw(rbind(leg.main, leg.sec))
binds together without huge space, but need to figure out how to add to another ggplot item
13.4 hjust/vjust
Why do I always forget the direction of these?
hjust: 0 = left-aligned, 0.5=center, 1 = right-aligned
vjust: 0 = top-aligned, 0.5=middle, 1 = bottom-aligned
<- expand.grid(
hjustvjust hjust=c(0, 0.5, 1),
vjust=c(0, 0.5, 1),
angle=c(0, 45, 90),
text="TEXT")
ggplot(hjustvjust, aes(x=hjust, y=vjust)) +
geom_point() +
geom_text(aes(label=text, angle=angle, hjust=hjust, vjust=vjust)) +
facet_grid(~angle) +
scale_x_continuous(breaks=c(0, 0.5, 1), expand=c(0, 0.2)) +
scale_y_continuous(breaks=c(0, 0.5, 1), expand=c(0, 0.2)) +
labs(
title="Various values of angle, hjust and vjust in ggplot \n"
y ="vjust \n"
, x =" \n hjust"
, +
) theme_bw() +
theme(
axis.text = element_text(color="black", face="bold" , size=12)
axis.title = element_text(color="black", face="plain", size=24)
, plot.title = element_text(color="black", face="bold" , size=18)
, )
13.5 Math Expressions
13.5.1 Use quote()
ggplot(mpg, aes(displ, hwy))+geom_point()+
ggtitle(
quote(
^ 2 - frac(1, 10) + sum(n[i], i==1, N)
alpha #end quote
) #end of ggtitle )
13.5.2 Use TeX()
from the latex2exp
package
- must be in a string
- must be denoted as math mode with dollar signs
- must include 2 backslashes for \(\LaTeX\) commands
library(latex2exp)
ggplot(mpg, aes(displ, hwy))+geom_point()+
ggtitle(TeX(
"$\\alpha^2 - \\frac{1}{10} + \\sum_{i}^N n_i$"
#end TeX
) #end ggtitle )
13.6 Facets: scales/space
Another option is facet_wrap()
or facet_grid()
, which can works if the axes are the same for the different variables you want to compare, but be careful as facets are supposed to be comparing items with the same measurements.
<- tidyr::pivot_longer(
tidy.df c(class, drv)
mpg, names_to = "category"
, values_to = "type"
, )
scales = "free"
: removes types from the axis if that category has 0 cars of that type
ggplot(tidy.df, aes(type)) +
geom_bar() + coord_flip() +
facet_wrap(~category, ncol = 1, scales = "fixed") +
labs(title = 'facet_wrap: scales = "fixed"')
ggplot(tidy.df, aes(type)) +
geom_bar() + coord_flip() +
facet_wrap(~category, ncol = 1, scales = "free") +
labs(title = 'facet_wrap: scales = "free"')
space = "free"
: spaces based on number of obs (i.e. number of bars); rather than giving each facet equal sizing, ONLY available for facet_grid
ggplot(tidy.df, aes(type)) +
geom_bar() + coord_flip() +
facet_grid(category ~ ., scales = "fixed" , space = "fixed") +
labs(title = 'facet_grid: scales = "fixed" and space = "fixed"')
ggplot(tidy.df, aes(type)) +
geom_bar() + coord_flip() +
facet_grid(category ~ ., scales = "free" , space = "free") +
labs(title = 'facet_grid: scales = "free" and space = "free"')
13.7 Align Axes
Sometimes I’m working on two different types of plots (like a bar chart and a scatter plot) that happen to have the same x-axis. I want to line up these axes so that when the plots are stacked the values correspond to the same date.
13.7.1 gridExtra::grid.arrange()
and cowplot::plot_grid()
# two different bar charts
<- ggplot(mpg, aes(class))+geom_bar()+coord_flip()+ylim(0, 109)
A <- ggplot(mpg, aes(drv))+geom_bar()+coord_flip()+ylim(0, 109) B
Using grid.arrange
command from the gridExtra
package does not line up axes.
#axes don't line up
::grid.arrange(A, B, ncol=1) gridExtra
grid.draw
command from the grid
package to left align graph edges .The cowplot::plot_grid()
function allows you to line up plots by a specific axis.
#make plots into Grobs (grid graphical object)
<- ggplotGrob(A)
gA <- ggplotGrob(B)
gB ::grid.draw(rbind(gA, gB))
grid::plot_grid(A, B, ncol = 1, align = "v") cowplot
13.7.2 Mixed Geoms
Scatter plots and bar charts will not line up automatically, even when using the grid.draw
/plot_grid
command detailed above. This is because their default limits are different given that the bar chart is centered on the value and the scatter plot is a single point on the value.
#work with smaller subset of economics (ggplot2)
<- "2014-06-01"
startdate <- economics %>%
economics_small filter(date >= as.Date(startdate)) %>%
arrange(date)
<- ggplot(economics_small, aes(date, unemploy))+
A geom_bar(stat="identity")+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
<- ggplot(economics_small, aes(date, uempmed))+
B geom_point()+geom_line()+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)
<- ggplotGrob(A)
gA <- ggplotGrob(B)
gB #grid::grid.draw(rbind(gA, gB))
::plot_grid(A, B, ncol = 1, align = "v") cowplot
In order to line the up there a a couple of options.
13.7.2.1 Fix xlim
If you make the limit the first x-value, the bar chart will not show up (remember it’s centered over the value).
<- ggplot(economics_small, aes(date, unemploy))+
A geom_bar(stat="identity")+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)+
xlim(as.Date(startdate), NA)
<- ggplot(economics_small, aes(date, uempmed))+
B geom_point()+geom_line()+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)+
xlim(as.Date(startdate), NA)
<- ggplotGrob(A) gA
Warning: Removed 1 rows containing missing values (`geom_bar()`).
<- ggplotGrob(B)
gB #grid::grid.draw(rbind(gA, gB))
::plot_grid(A, B, ncol = 1, align = "v") cowplot
Warning: Removed 1 rows containing missing values (`geom_bar()`).
This can be fixed by adding a half unit to the x-axis (i.e. having the lower limit be half-unit lower than smallest x-value). In this case the unit is a month, so a half-unit would be ~15 days.
<- .5*(economics_small$date[2] - economics_small$date[1])
HalfUnit HalfUnit
Time difference of 15 days
<- ggplot(economics_small, aes(date, unemploy))+
A geom_bar(stat="identity")+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)+
xlim(as.Date(startdate)-HalfUnit, NA)
<- ggplot(economics_small, aes(date, uempmed))+
B geom_point()+geom_line()+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)+
xlim(as.Date(startdate)-HalfUnit, NA)
<- ggplotGrob(A)
gA <- ggplotGrob(B)
gB #grid::grid.draw(rbind(gA, gB))
::plot_grid(A, B, ncol = 1, align = "v") cowplot
13.7.2.2 Shift Bar Chart
Bar charts are automatically centered over the x-value. Bar charts (and any geom object) can be shifted by usingposition - position_nudge())
. The shift needs to be half a unit on the x-axis, again here it is monthly data so a half unit would be ~15 days.<- ggplot(economics_small, aes(date, unemploy))+
A geom_bar(stat="identity", position = position_nudge(x = as.vector(HalfUnit)))+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)
<- ggplot(economics_small, aes(date, uempmed))+
B geom_point()+geom_line()+
geom_vline(xintercept = as.Date(startdate), color="red", size=2)
<- ggplotGrob(A)
gA <- ggplotGrob(B)
gB #grid::grid.draw(rbind(gA, gB))
::plot_grid(A, B, ncol = 1, align = "v") cowplot
13.8 Default ggplot2
13.8.1 Colors
Default colors can be recreated using scales::hue_pal()
and can be shown using scales::show_col()
::show_col(scales::hue_pal()(4)) scales
13.9 Geom Defaults
Look up geom’s default aes using: GeomName$default_aes$
Example: geom_text()
$default_aes GeomText
Aesthetic mapping:
* `colour` -> "black"
* `size` -> 3.88
* `angle` -> 0
* `hjust` -> 0.5
* `vjust` -> 0.5
* `alpha` -> NA
* `family` -> ""
* `fontface` -> 1
* `lineheight` -> 1.2
13.10 Links
new sections
- Stack Overflow | Consistently center ggplot title across PANEL not PLOT
- Stack Overflow | customize ggplot2 axis labels with different colors
shape/line in ggplot
- SAPE | ggplot2 Quick Reference: size
- SAPE | ggplot2 Quick Reference: shape
- Albert’s Blog | Point Shape Options in ggplot
layout
- Baptiste Augie, Jul 2019 | Laying out multiple plots on a page
- Datanovia | How To Easily Customize GGPlot Legend for Great Graphics
- R project | tableGrob - Displaying tables as grid graphics
- Statistics Globe | Remove Border of ggplot2 geom_label Text Annotation in R
- {gridExtra}::tableGrob
- Stack Overflow | ggplot2: change colour of font and background in facet strip?
other