library(pacman)
p_load(ggplot2, dplyr, extrafont)
Introduction
What do you do when you live in an area that hardly snows and you don’t have room for a Christmas tree to display in your house? The answer - subject yourself to your wildest imagination through art. And by art, I mean visualization through technical means.
I’ll be honest. I have very little artistic talents; however, I do have one bit of knowledge up my sleeve - programming in R. So what did I do to get that snowy Christmas setting I dreamed of earlier? I decided to use R code to draw a holiday card, using nothing more than the ggplot2
library, lots of trial and error, and a creative, artistic vision (as I drafted physically below prior to coding).
As ambitious and counterproductive this endeavor seemed to be (since I could’ve done it faster using Microsoft Paint), I actually learned a lot about coding in R through this project, which I’ll explore at the end of this article (and perhaps, subsequent ones). For now, here’s how I drew the holiday card above using RStudio.
Drawing the Holiday Card
To continue further with this article, you would need to have the libraries ggplot2
, dplyr
, pacman
, and extrafont
installed on your computer.
A quick way to go about it would be to install pacman
and run the code chunk below.
Optionally, if you want to see the code here to follow along (and get the .tff files to get the Segue Script
font), you can see my GitHub for this code here: https://github.com/Ken-Vu/Drawing-a-Holiday-Card-with-ggplot2
With the libraries installed, let’s start building a holiday card (much of which involved some trail and error and sketching to plan out the coordinates for the items on it).
A. Adding the Christmas Tree
To begin, we make the parts of the tree, which consists of the tree’s body as well as the trunk. Note that the coordinates here (as with any point locations moving forward) have been planned out and tweaked through countless trial and error to get right.
<- tribble(
xmas_tree ~x, ~y,
# left side of tree base
-6, 5,
-3, 10,
-4.5, 10,
-2, 15,
# top of tree
-3, 15,
0, 20,
3, 15,
# right side of tree base
2, 15,
4.5, 10,
3, 10,
6, 5
)
# coordinates for the tree trunk
<- tribble(
xmas_trunk ~x, ~y,
-1, 5,
1, 5,
1, 0,
-1, 0
)
Let’s include some Christmas ornaments and sashes to decorate the tree with as well.
# making christmas sashes
<- tribble(
xmas_sashes ~x, ~y, ~xend, ~yend,
-1.5, 17.6, 2.0, 16.6,
-2.5, 14.0, 3.5, 12.0,
-3.5, 9.4, 5.0, 6.8
)
<- tribble(
xmas_ornaments ~x, ~y,
-6, 5,
-4.5, 10,
-3, 15,
3, 15,
4.5, 10,
6, 5,
-1, 15,
0.25, 13,
1.75, 14,
-1, 9,
0.75, 7,
2.5, 8,
-3.5, 7,
-2.5, 11.7,
0.5, 17.8
)
Then, we put all the tree parts together to make a well-decorated tree. Let’s also add a nice yellow star at the top of the tree to make it more festive.
Here, we use geom_polygon()
to create shapes where each of the coordinates in a given data set serve as end points of different corners of this shape, geom_point()
to plot a set of coordinates (which by default are dots, but can be modified to be any shape, including a star), and geom-curve()
to plot a curved line going from one point to the next.
<- ggplot() +
xmas_ggplot # adding tree
geom_polygon(data = xmas_tree,
mapping = aes(x, y),
fill = "#1f6132") +
geom_polygon(data = xmas_trunk,
mapping = aes(x, y),
fill = "#413506"
+
)
# adding Christmas star
geom_point(data = data.frame(x = 0, y = 20.25),
mapping = aes(x, y),
size = 15,
shape="\u2605",
color = "#f4d457"
+
)
geom_point(data = xmas_ornaments,
mapping = aes(x,y),
color = "#800000",
size = 3) +
# adding sashes to wrap around xmas tree
geom_curve(data = xmas_sashes,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
size = 3,
color = "darkgrey",
# how curved the point is
curvature = 0.35,
# how transparent the shape/point is
# alpha = 1 means it'll be solid,
# alpha = 0 means very transparent
alpha = 0.8) +
# use this instead of xlim() and ylim() as those two aforementioned
# functions will discard points that're out of range
coord_cartesian(ylim = c(0, 35), xlim = c(-15, 15)) +
labs(caption = "Made with RStudio")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
xmas_ggplot
B. Adding background details
Change background colors of plot
Let’s make the plotting area black to reflect a night sky as well as create a “golden frame” to put this picture in.
We can also remove the axis titles, axis text, gridlines, and tick marks.
<- xmas_ggplot +
xmas_ggplot theme(
# background of black sky
panel.background = element_rect(fill = "black"),
# removing misc plotting elements
axis.title = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank(),
panel.grid = element_blank(),
# adding golden frame around photo
plot.background = element_rect(fill = "#5a410c"),
# removing plotting legend
legend.position = "none"
) xmas_ggplot
Adding snow
Let’s randomly generate some snow so we can add a level. We’ll use the random number generator based on the uniform distribution to get random coordinates we’ll plot as a scattered group of 300 white closed circles, which’ll create a “snowing” effect on our Christmas drawing.
# TRIVIA: the seed number is the release date
# of the song "Last Christmas" by Wham! (aka December 3, 1984)
set.seed(120384)
<- data.frame(
snow_points x = runif(n = 300, min = -20, max = 17),
y = runif(n = 300, min = -2, max = 36)
)
<- xmas_ggplot +
xmas_ggplot geom_point(data = snow_points,
mapping = aes(x, y),
color = "white"
) xmas_ggplot
We can also add a snowy ground to the landscape to complete it. Here, we set up the points to get snowy ground with some slopes.
<- tribble(
xmas_snowground ~x, ~y,
-30, 2.5,
-10, 0.5,
-5, 1.5,
0, 0.2,
5, 0.8,
12, 0.4,
30, 1
)
Here, we use geom_ribbon()
to shade in the area below the curve generated by geom_smooth()
, which plots a smooth curved line through the data points in the tribble xmas_snowground
to create a bumpy, snowy ground that enhances the flat ground made with geom_hline()
(a horizontal line generator). geom_ribbon()
allows you to shade in areas under a set of points or a line where you can control where the shading begins and where the shading ends using arguments such as xmax
, xmin
, ymax
, ymin
, etc.
Ex. ymax=y
in the aes()
function indicates you want the boundaries of the shaded area to depend on the values of the coordinates for the y-axis for a data set with coordinates for the x-axis and the y-axis. Here, the y-coordinates come from the tribble for setting the slopes of the snowy ground (i.e., xmas_snowground
).
<- xmas_ggplot +
xmas_ggplot# plotting an uneven snowy ground
geom_smooth(data = xmas_snowground,
mapping = aes(x = x, y = y),
color = "white",
se = F) +
geom_ribbon(data = xmas_snowground,
aes(x = x, ymax=y),
xmin = -Inf,
xmax = Inf,
ymin = 0,
fill="white") +
# adding a flat horizontal line for the snowy ground
geom_hline(yintercept = seq(0.5, -50, -0.01),
color = "white",
fill = "white")
Warning in geom_hline(yintercept = seq(0.5, -50, -0.01), color = "white", :
Ignoring unknown parameters: `fill`
xmas_ggplot
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
Write a holiday greeting
No holiday card’s fully complete without a holiday greeting. Let’s create one as a text label on the plot.
Before any labeling, we can import and prepare the Segoe Script
fonts in the two .tff files in our project folder using the font_import
and loadFonts
functions from the extraFont
package. My GitHub repository has the two .tff files in the project folder that you can use and put in your working directory.
Note that you should only run the following two code chunks below once as it’s quite taxing to run; it can take a couple of minutes to load.
If you’re interested, you can read the documentation here for the extraFont
package for more details on how it works and how it may differ depending on your machine’s operating system: https://cran.r-project.org/web/packages/extrafont/index.html.
font_import(paths = getwd(), prompt = F)
loadfonts()
Let’s add a holiday greeting as well as stylize the caption with geom_text()
for plotting labels onto your graph and the theme()
layer for adjusting visual aspects of your plots.
<- xmas_ggplot +
xmas_ggplot
# adding holiday greeting
geom_text(aes(label = "Happy Holidays!",
x = 0, y = 29),
color = "#f4d457",
size = 16,
family = "Segoe Script") +
# adjusting the caption text
theme(plot.caption = element_text(family = "Segoe Script", face = "bold"))
xmas_ggplot
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
(Optional) C: Do you wanna build a snowman?
If you’re savvy enough, you can add a snowman to this picture to finish it off. It’s a common Christmas dream for holiday celebrators as well as fans of the movie Frozen.
We can use tribbles to create coordinates and data for the snowman’s physical body, eyes, chest, and arms.
# coordinates for three snowballs forming snowman's body
<- tribble(
snowman_body ~x_val, ~y_val,
10, 3,
10, 8,
10, 12,
)
<- tribble(
snowman_parts ~x_val, ~y_val,
# eyes
9.55, 12.85,
10.45, 12.85,
# central body dots
10, 9.15,
10, 8,
10, 6.85
)
# storing data for snowman arms
<- tribble(
snowman_arm1 ~x, ~y, ~xend, ~yend,
9, 9, 7, 11
)
<- tribble(
snowman_arm2 ~x, ~y, ~xend, ~yend,
11, 9, 13, 11
)
# data for snowman's mouth
<- tribble(
snowman_mouth ~x, ~y, ~xend, ~yend,
9.25, 11.25, 10.75, 11.25
)
Then, we use the geom_segment()
function to draw the arms of the snowman by specifying the endpoints of the line segments we want to draw. Additionally, we use geom_point()
to plot the eyes, chest dots, and nose of the snowman as well as geom_curve()
for drawing the snowman’s mouth.
<- xmas_ggplot +
xmas_ggplot
# draw arms
geom_segment(data = snowman_arm1,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
color = "#9b7115",
linewidth = 1.6,
lineend = "round") +
geom_segment(data = snowman_arm2,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
color = "#9b7115",
linewidth = 1.6,
lineend = "round") +
# plotting snowman's body
geom_point(data = snowman_body[1,],
mapping = aes(x_val, y_val),
size = 25,
color = "white") +
geom_point(data = snowman_body[2,],
mapping = aes(x_val, y_val),
size = 19,
color = "white") +
geom_point(data = snowman_body[3,],
mapping = aes(x_val, y_val),
size = 16,
color = "white") +
# plotting the eyes and body dots of the snowman
geom_point(data = snowman_parts,
mapping = aes(x_val, y_val),
color = "black",
size = 1.6
+
)
# plotting the nose
geom_point(data = data.frame(x = 10, y = 11.85),
mapping = aes(x, y),
color = "darkorange",
size = 1.6) +
# draw the mouth
geom_curve(data = snowman_mouth,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
linetype = 2)
xmas_ggplot
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
As a final touch, let’s add a hat and scarf onto our fellow snowman here for extra warmth and comfort for the holidays.
We’ll do the same thing as before and use tribbles and data frames to store coordinates needed to draw parts of the hat.
# coordinates for top of the hat
<- tribble(
snowman_hat_top ~x0, ~x1, ~y0, ~y1,
9.25, 10.75, 14.5, 17
)
# two tribbles for coordinates for parts of the scarf
<- tribble(
snowman_scarf ~x, ~y, ~xend, ~yend,
9, 10, 11, 10
)
<- tribble(
snowman_scarf2 ~x, ~y, ~xend, ~yend,
9.25, 10, 8, 6
)
<- xmas_ggplot +
xmas_ggplot
# hat base
geom_segment(
data = data.frame(x = 8.5, y = 14,
xend = 11.5, yend = 14),
mapping = aes(x = x, y = y,
xend = xend, yend = yend),
color = "#2c2c2c",
lineend = "round",
size = 2
+
)
# hat top
geom_rect(
data = snowman_hat_top,
mapping = aes(xmin = x0, ymin = y0,
xmax = x1, ymax = y1),
fill = "#2c2c2c"
+
)
# red hat band
geom_segment(
data = data.frame(x = 9.25, y = 14.45,
xend = 10.75, yend = 14.45),
mapping = aes(x = x, y = y,
xend = xend, yend = yend),
color = "#691916",
lineend = "round",
size = 2
+
) # draw the scarf
geom_curve(data = snowman_scarf2,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
linewidth = 3,
color = "#2b74b0",
curvature = -0.25) +
geom_curve(data = snowman_scarf,
mapping = aes(x = x, y = y,
xend = xend,
yend = yend),
linewidth = 3,
color = "#1f639b")
+
xmas_ggplot labs(alt = "A drawing of a snowy ground featuring a black background with falling snow, a snowman at the right of the picture with a black top hat and blue scarf, and a green Christmas tree with a brown trunk, red, blue, and green ornaments hanging off it, light grey sashes wrapped around the tree, and a yelow star at the top of the tree. The text at the top reads (in cursive), 'Happy Holidays!' as the photo is wrapped in a light brown frame with a black cursive caption at the bottom right thatreads, 'Made with RStudio'.")
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
If you want, you can save it to your computer by running this code once. It’ll be in your working directory wherever you run this code.
ggsave("holiday_card.png", plot = xmas_ggplot,
width = 8, height = 5)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
Conclusion
Creating a holiday card using RStudio started out as a passion project of mine to find ways to express holiday cheer and combine it with RStudio in some way; I took inspiration from all the wonderful visuals I’ve seen other programmers made in the past. I wondered if programming had potential to explore art creation (in a literal sense as I did here).
Along the way, I found out how challenging and practical using R to create drawings would be; I spent days sketching a rough draft of the holiday’s card layout, scrolling through numerous articles and Stackoverflow posts on ggplot2
functions, and rerunning and retyping code chunks over and over again. In a more practical sense, I would’ve saved more time and reduced a lot of head scratching moments by completing the drawing with application Microsoft Paint instead, which would only take maybe less than an hour versus a couple of days with RStudio.
At the same time, I ended up surprisingly picking up a lot of extra familiarity and useful understanding of ggplot2
through this project. I learned about using the coord_cartesian()
function to keep ggplot2
graphs from throwing out out of range points (which xlim()
and ylim()
would do). I leaned about creating shapes and objects besides data points using functions such as geom_polygon()
, geom_curve()
, and geom_ribbon()
. I also learned about how to engineer solutions on the fly when things never went as expected, knowing what I want in the end to keep myself focused.
Overall, it’s been a fun ride creating a holiday card using the power of RStudio and ggplot2
. I hope you found this blog meaningful to you and I wish you all a great Christmas season and a wonderful new year!
Take care and stay turned for more from The R Files!
Resources
I recommend the following resources for enhancing your exploration and experimentation with the ggplot2
library.
A. Books
R for Data Science: This book (now in it’s second edition) is a classic and covers some of the bare essentials needed to work with and display all kinds of data as well as strategies for writing clean code. Chapters 10-12 are relevant for those focused on data visualization in general as well as Chapters 2-9 for generally good practices for writing and maintaining clean code. You can read it online here: https://r4ds.hadley.nz/
ggplot2: Elegant Graphics for Data Analysis: This is a good book that explains the grammar of graphics of
ggplot2
and how it works under the surface. Chapters 3-5, 8, 9, 10-14, and 17 are some chapters I recommend for understanding more of the basics ofggplot2
’s plotting functions, the steps for makingggplot2
graphs in general, and some of the ways in which the plot aesthetics are made and how they can be modified. You can read the work-in-progress version online here: https://ggplot2-book.org/Data Visualization with R by Rob: It’s a direct guide on building plots with
ggplot2
along with best practices for data visualizations in general. Chapters 3-6, 11, and 14 directly focus onggplot2
, its wide array of customization for the aesthetics of its plots, and best practices for creating effective and visually sound graphs. Chapter 10 is also interesting if you’d like to explore other graphs besides the conventional 2D bar plots and scatterplots you see often inggplot2
, such as dumbbell plots and heat maps. Currently, it’s only available as an online bookdown, but a book version is reportedly in the works of being available on Amazon soon.
B. Links
- ggplot2 Reference: Here’s the official documentation of the
ggplot2
package, which contains a list of all the major functions you can use in this library for your plotting and visualization purposes (includinggeom_point()
,geom_curvature()
, and many others).