We'll be using the ClojisR library to visualize data in Clojure. This library is a Clojure-R interop and it allows us to call R functions on R objects in Clojure.
Before starting ensure that you have the following installed on your computer:
- JDK 1.8 or later
- Clojure 1.9.0 or later
- R
- The Rserve R package (
install.packages("Rserve",,"http://rforge.net")
) Tested with Rserve version 1.8.6. Earlier versions are known to have a bug. - ClojisR library:
- Tidyverse: A collection of open source R packages. It contains the ggplot2 package, which we'll be using during this tutorial.
First, we require the necessary namespaces.
(require
'[clojisr.v1.r :as r :refer
[r eval-r->java r->java java->r
java->clj java->naive-clj
clj->java r->clj clj->r ->code r+
colon]]
'[clojisr.v1.require :refer
[require-r]]
'[clojisr.v1.robject :as robject]
'[clojisr.v1.session :as session]
'[clojisr.v1.rserve :as rserve]
'[tech.ml.dataset :as dataset]
'[clojisr.v1.applications.plotting :refer
[plot->svg plot->file
plot->buffered-image]])
Then we make sure that we're using the Rserve backend (in case we were using Renjin instead earlier), and that there are no R sessions currently running. This is typically not needed if you just started working. Here, we do it just in case.
(rserve/set-as-default!)
(r/discard-all-sessions)
Finally, we use require-r
to bring functions and data from R packages to Clojure.
(require-r '[ggplot2 :as gg])
The following tutorial contains translated code samples from the 'Data Visualization' chapter of R for Data Science book.
ggplot2 comes with a selection of built-in datasets which we'll be using to illustrate various visualisations.
Let's start with the mpg
data frame which shows the fuel economy data from 1999 to 2008 for 38 popular models of cars.
You can see a small portion of the dataset by typing gg/mpg
in the REPL.
# A tibble: 234 x 11
manufacturer model displ year cyl trans drv cty hwy fl class
<chr> <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
1 audi a4 1.8 1999 4 auto(l… f 18 29 p comp…
2 audi a4 1.8 1999 4 manual… f 21 29 p comp…
3 audi a4 2 2008 4 manual… f 20 31 p comp…
4 audi a4 2 2008 4 auto(a… f 21 30 p comp…
5 audi a4 2.8 1999 6 auto(l… f 16 26 p comp…
6 audi a4 2.8 1999 6 manual… f 18 26 p comp…
7 audi a4 3.1 2008 6 auto(a… f 18 27 p comp…
8 audi a4 quat… 1.8 1999 4 manual… 4 18 26 p comp…
9 audi a4 quat… 1.8 1999 4 auto(l… 4 16 25 p comp…
10 audi a4 quat… 2 2008 4 manual… 4 20 28 p comp…
# … with 224 more rows
Let's go ahead and plot our first visualization. Assuming you have a results folder in you Clojure project folder, the following code will create a scatterplot with displ
on the x-axis and hwy
on the y-axis and store it as an image inside the results folder.
(plot->file "results/basic_scatterplot.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point)))
Let’s turn this code into a reusable template for making graphs with ggplot2 in Clojure. To make a graph, replace the bracketed sections in the code below with a file name, a dataset, a collection of mappings, or a geom function.
(plot->file "<DESTINATION_FILE>"
(r+ (gg/ggplot <DATA> (gg/aes <MAPPINGS>))
(<GEOM_FUNCTION>)))
The rest of this tutorial will show you how to complete and extend this template to make different types of graphs.
(plot->file "results/color_aes_scatterplot.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy
:color 'class))
(gg/geom_point)))
(plot->file "results/alpha_aes_scatterplot.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy
:alpha 'class))
(gg/geom_point)))
(plot->file "results/blue_scatterplot.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point :color "blue")))
(plot->file "results/facet_scatterplot.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point)
(gg/facet_wrap '(tilde . class) :nrow 2)))
(plot->file "results/point_geom.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point)))
(plot->file "results/smooth_geom.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_smooth)))
(plot->file "results/linetype_aes_smooth_geom.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy
:linetype 'drv))
(gg/geom_smooth)))
(plot->file "results/group_aes_smooth_geom.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy
:group 'drv))
(gg/geom_smooth)))
(plot->file "results/color_aes_smooth_geom.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy
:color 'drv))
(gg/geom_smooth)))
(plot->file "results/mult_geom_basic.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point)
(gg/geom_smooth)))
(plot->file "results/mult_geom_with_aes.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ
:y 'hwy))
(gg/geom_point (gg/aes :color 'class))
(gg/geom_smooth)))
(plot->file "results/basic_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut))
(gg/geom_bar)))
(plot->file "results/stat_transform_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :y 'depth))
(gg/stat_summary :fun.ymin "min" :fun.ymax "max" :fun.y "median")))
(plot->file "results/color_aes_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :color 'cut))
(gg/geom_bar)))
(plot->file "results/fill_aes_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'cut))
(gg/geom_bar)))
(plot->file "results/fill_aes_for_clarity_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'clarity))
(gg/geom_bar)))
(plot->file "results/identity_pos_clarity_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'clarity))
(gg/geom_bar :alpha 1/5 :position "identity")))
(plot->file "results/identity_pos_no_fill_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :color 'clarity))
(gg/geom_bar :fill "NA" :position "identity")))
(plot->file "results/fill_pos_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'clarity))
(gg/geom_bar :position "fill")))
(plot->file "results/dodge_pos_bar_geom.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'clarity))
(gg/geom_bar :position "dodge")))
(plot->file "results/jitter_pos_point_geom_1.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ :y 'hwy))
(gg/geom_point :position "jitter")))
OR
(plot->file "results/jitter_pos_point_geom_2.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'displ :y 'hwy))
(gg/geom_jitter)))
(plot->file "results/box_plot_basic.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'class :y 'hwy))
(gg/geom_boxplot)))
(plot->file "results/box_plot_switch_axes.jpg"
(r+ (gg/ggplot gg/mpg (gg/aes :x 'class :y 'hwy))
(gg/geom_boxplot)
(gg/coord_flip)))
(plot->file "results/coord_flip.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'cut))
(gg/geom_bar :show.legend "FALSE" :width 1)
(gg/theme :aspect.ratio 1)
(gg/labs :x "NULL" :y "NULL")
(gg/coord_flip)))
(plot->file "results/coord_polar.jpg"
(r+ (gg/ggplot gg/diamonds (gg/aes :x 'cut :fill 'cut))
(gg/geom_bar :show.legend "FALSE" :width 1)
(gg/theme :aspect.ratio 1)
(gg/labs :x "NULL" :y "NULL")
(gg/coord_polar)))
FINAL TEMPLATE
- Code samples translated from R for Data Science, by Hadley Wickham and Garrett Grolemund. Published by O'Reilly Media, Inc. Copyright © 2017 Garrett Grolemund, Hadley Wickham. Used with permission.
- A special thanks to all the contributors of the ClojisR library for building this amazing project.
- The following articles were used as references while writing this tutorial:
-- ClojisR intro
-- A tutorial about generating R code from Clojure
-- Titanic tutorial #0
-- DataVis in Clojure using R
-- ggplot2 reference