June 2018 POTM: [magick]-ally add images to your figures

4 weeks 1 day ago - 4 weeks 1 day ago #144 by skdunnigan
This is my first SWMPrats post, so bear with me and excuse me for any errors in formatting!

As I was creating a basic visualization of sucralose and acetaminophen data we have collected from our Pellicer Creek SWMP site for an upcoming meeting, I thought it would be neat to be able to have our logo embedded onto the plot for when we share our data with others.

After doing a bit of googling, I learned about this great package in R called 'magick'- and it is magical. It is a package that simplifies image processing in R and has lots of functionality. I ended up just using it to combine images.

Step One: Make your plot

I used an excel spreadsheet with our nutrient data in it. The spreadsheet is set up in wide format (Hadley Wickham has a great paper explaining data set structures) and is only comprised of a few rows of data since we only collect data on sucralose and acetaminophen in our first grab sample at Pellicer Creek and only have this data since January 2016.

I used the ‘readxl’ and ‘ggplot2’ packages to read in the data and create the figure. This is a fairly simplistic plot, but I’ll highlight a few of my favorite things in the code below.

First, after reading in the data file, I always convert datetimestamps into POSIXct format. This line of code is something I learned from a POTM that Kim did back in the day and ggplot2 seems to like it.

Second, the units for sucralose and acetaminophen are in micrograms per liter. Inserting the Greek letter mu into an axis is something I also recently learned how to do. Before creating your plot, you want to set up the title of your y-axis. To do this, you make it into a value using the expression() function.

Third, if you like to assign specific colors to factors in your plot, or if you want to be able to control your legend a bit more, I recommend inserting colour = ‘variable name’ in to your aes() commands in the ggplot2 plotting functions. You’ll see that I have colour = ‘Sucralose’ in some of those lines of code. Later, you can then use the ‘scale_colour_manual()’ function to assign actual colors to those names you used in the plotting functions.

# --------------------------------------------
# Set working directory. This is where your files will be saved.
## Be sure to change this if you have moved the file locations!
# --------------------------------------------
setwd("Z:/Research/SWMP/SWMP Nutrients/FCOLI")

# --------------------------------------------
# Read in data file
# --------------------------------------------
dat <- read_excel("Z:/Research/SWMP/SWMP Nutrients/FCOLI/2016.17-gtmpcnut1.1samples.xlsx")

# change the timestamp to POSIXct format (this works better with ggplot)
dat$TimeStamp <- as.POSIXct(dat$DateTimeStamp, format = "%m/%d/%Y %H:%M", tz = 'America/Regina')

# --------------------------------------------
# create plot for sucralose and acetaminophen
# --------------------------------------------

# create axis title
plot1_y_title <- expression(paste(mu*"g/L"))

plot1 <-ggplot(data = dat, aes(x = TimeStamp))+
  geom_point(aes(y = SUCRA, colour='Sucralose'), size=3)+ 
  geom_line(aes(y = SUCRA, colour='Sucralose'), size=1)+ 
  geom_point(aes(y = ACETA, colour='Acetaminophen'), size=3)+
  geom_line(aes(y = ACETA, colour='Acetaminophen'), size=1)+
  geom_hline(aes(yintercept = 0.01), linetype='dashed', size=1)+
  geom_hline(aes(yintercept = 0.008), linetype='solid', size=1)+
  theme(legend.title = element_blank(),  
        legend.position = "bottom",
        legend.text = element_text(size=12),
        axis.title.x = element_blank(), 
        axis.ticks = element_line(),
        plot.caption = element_text(size=6, face='italic'),
        axis.text.x = element_text(angle = 90, vjust=0.3, size=10),
        axis.text.y = element_text(size=10),
        panel.grid.minor = element_blank())+
  scale_y_continuous(expand = c(0,0)) + 
  labs(x='', y=plot1_y_title,  
       caption = 'Created by Shannon Dunnigan',
       title='Pellicer Creek',
       subtitle='GTM Research Reserve SWMP station')+
  scale_colour_manual(name='', values=c('Sucralose'='#47A49D', 'Acetaminophen'='#D9582E')) + 
  scale_x_datetime(date_breaks = '1 month', date_minor_breaks = '2 weeks', date_labels='%y-%b') 
ggsave("plot1.png", dpi = 300, width = 7, height = 6)

Okay, now onto the good stuff…

Step Two: Adding the logo...

For this code, I used the 'magick', and 'magrittr' packages, which can all be downloaded from CRAN. Before running the code, I already produced the plot (from above) using another script and saved it as a .png file, which I cleverly named 'plot1.png'.

With the 'image_read' function from 'magick', images can be read in directly from a file path, URL, or even as a raw vector with image data. Once the image has been read into R, there are a variety of things you can do with it. I have merely scaled down the image for the purposes of my plot, but you could cut, edit, apply filters and effects, etc.

library(magrittr) # For piping the logo

# ----------------------------------------------
# This script is to add GTM logos into figures
# ----------------------------------------------
# Load the image we want to use
logo_raw <- image_read(
  "Z:/GTM shared pictures/Logos/GTM Logo Dix.Hite All Files/PNG/GTM - General.png")
# ----------------------------------------------
# add images onto the first plot       ---------
# ----------------------------------------------

# Call back the first plot
plot1.1 <- image_read("plot1.png")
# Scale down the logo (and other things)
## This is the cool part because you can do a lot to the
## image/logo before adding it to your final product
logo <- logo_raw %>% 
# Combine your plot with your logo
## The offset is what determines where your image will go on
## top of the plot
out <- image_composite(plot1.1, logo, offset = "+10+1580")
# save the plot you created!
image_write(out, "plot1.1.png")

Boom! Just a few lines of code (without my comments) and you have a logo on your plot!

Remember how I said that images can be read in from a URL? By using the great features of 'magick', I have indicated a potential cause of the spike in sucralose below...

Wingardium leviosa" indeed, Mr. Potter.

And here is one of my favorite parts...

It works for .gif files, too! The animation component of .gifs means that we will need to set them as frames in an animation.

# -----------------------------------------------
# we can also add a gif image
# -----------------------------------------------

logo_raw2 <- image_read(

# Background image
background <- image_background(plot1.1, "white", flatten = TRUE)
# Combine and flatten frames
frames <- image_composite(background, logo_raw2, offset = "+1080+820")
# Turn frames into animation
animation <- image_animate(frames, fps = 5)
image_write(animation, "wand.gif")

See? Isn't it magical?

If you feel like trying out the 'magick' package, send along some of your creations! Daniel P. Hadley's use of Vincent Vega in a graph is certainly one of my favorites...
Last edit: 4 weeks 1 day ago by skdunnigan.

Please Log in to join the conversation.

4 weeks 1 day ago - 4 weeks 1 day ago #145 by Kim_Cressman
Shannon, this is awesome! Thanks for contributing a POTM!

POSIXct is great. I've learned an easier way to get things into it - using the lubridate package. There are different functions depending on how your data's already formatted, but they're all intuitive. For ours, which started off looking like "2/11/2016 09:45", it would be:
dat$TimeStamp <- mdy_hm(dat$DateTimeStamp, tz = 'America/Regina')

lubridate makes it a little easier because you don't have to fuss with telling R what format your date started off in, and remembering if month is %M with a capital or %m with lower case, etc. Not strictly necessary, but nice.

If what you had was a date in ISO format ("2014-05-01"), you'd use

If you had it in "05-01-2014" format, you'd go with

lubridate is magical (see what I did there? It was actually unintentional at first) in how it recognizes things. Here's the package vignette:
Last edit: 4 weeks 1 day ago by Kim_Cressman. Reason: more formatting
The following user(s) said Thank You: skdunnigan

Please Log in to join the conversation.

4 weeks 20 hours ago #146 by skdunnigan
Thanks, Kim! I'll use the lubridate package next time! :D

Please Log in to join the conversation.

4 weeks 20 hours ago #147 by Marcus Beck
Great post Shannon, thanks for contributing! I second Kim's recommendation for lubridate. For years I was using the base R functions for date formatting. They certainly get the job done but lubridate is much, much more intuitive.
The following user(s) said Thank You: skdunnigan

Please Log in to join the conversation.

Time to create page: 0.298 seconds