I was recently attempting to plot some filled points with an additional white border around the standard black border. I was unable to find a posted solution that did not rely on custom point shapes but still plotted all components of each point together.
After far too much time, I came up with the following solution. This solution works by duplicating each point and plotting a slightly larger white point behind it. This is similar to the solutions listed here (R ggplot2: How to draw geom_points that have a solid color and a transparent stroke and are colored depending on color?). However, my solution allows both borders to overlap adjacent points rather than plotting one set of borders behind the other.
Reproducible Example:
## Create test dataframe from built in mpg data frame and add scatter
test_mpg = mpg
test_mpg$displ = test_mpg$displ + runif(length(test_mpg$displ), min = 0, max = 0.5) # Add random values between 0 and 0.5 to test_mpg$displ
test_mpg$cty = test_mpg$cty + runif(length(test_mpg$displ), min = 0, max = 5) # Add random values between 0 and 5 to test_mpg$cty
### Add second border color
## Step 1: Create new dataset with duplicate rows.
rowind = seq(1,nrow(test_mpg)) # Create array of row indices
rowind2 = rep(rowind, each = 2, times = 1) # Create new array with doubled row indices (i.e. 1,2,3 becomes 1,1,2,2,3,3)
newdata = test_mpg[rowind2,] # Create new dataset with doubled rows
newdata$odd_row = seq_len(nrow(newdata)) %% 2 # Create column indicating even and odd rows. Odd rows = 1 and even rows = 0
## Step 2: Create columns with alternating aesthetics
# Create point size aesthetic column with alternating values
newdata$pointsize = NA # Create new column
newdata[newdata$odd_row == 1,]$pointsize = 2.0 # Set odd numbered rows to the desired outer point size. This is plotted behind the main point and determines the amount of secondary border visible.
newdata[newdata$odd_row == 0,]$pointsize = 1.5 # Set even numbered rows to the desired main point size
# Create point color aesthetic column with alternating values
newdata$pointcolor = NA # Create new column
newdata[newdata$odd_row == 1,]$pointcolor = "white" # Set odd numbered rows to the desired outer border color.
newdata[newdata$odd_row == 0,]$pointcolor = "black" # Set even numbered rows to the desired main point border color
## Step 3: Plot your data
# Plot your data with point size set to newdata$pointsize and point color set to newdata$pointcolor. These should be set outside of the aes()
test = ggplot(newdata, aes(displ, cty, fill = drv, shape = drv)) +
scale_fill_manual(values=c("green","blue","yellow")) +
scale_shape_manual(values= c(23, 24, 25)) +
geom_point(alpha = 1, size = newdata$pointsize, stroke = 0.75, color = newdata$pointcolor) +
theme_bw()
test