Kleopatra Pirpinia / May 06 2021 / Published
Remix of Clojure by Nextjournal
Slider Animations with Clojure
This is an implementation of the Gapminder animation using Clojure and the Clojure viewer API in Nextjournal that allows us to use Plotly Javascript graphing library.
To implement this, we followed the example of the Plotly documentation on animating sliders.
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/data.csv {:mvn/version "1.0.0"}
cheshire/cheshire {:mvn/version "5.10.0"}
;; complient is used for autocompletion
;; add your libs here (and restart the runtime to pick up changes)
compliment/compliment {:mvn/version "0.3.9"}}}
deps.edn
Extensible Data Notation
Data
curl https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv > data.csv
2.4s
Parsing
(require [clojure.data.csv :as csv])
(def data (csv/read-csv (slurp "/data.csv")))
;; Courtesy of the data.csv authors (https://github.com/clojure/data.csv)
(defn csv-data->maps [csv-data]
(map zipmap
(->> (first csv-data) ;; First row is the header
(map keyword) ;; Drop if you want string keys instead
repeat)
(rest csv-data)))
(def parsed-data (csv-data->maps data))
1.5s
Get data by year and continent
(def years (sort (set (map :year parsed-data))))
(def continents (set (map :continent parsed-data)))
(def grouped-by-year (group-by :year parsed-data))
(def grouped-by-year-and-continent
(into {}
(map (fn [year]
{year (group-by :continent (get grouped-by-year year))}) years)))
(defn get-data [year continent]
(get-in grouped-by-year-and-continent [year continent]))
0.2s
(defn trace [year continent]
(let [continent-year-data (get-data year continent)]
{:name continent
:x (map :lifeExp continent-year-data)
:y (map :gdpPercap continent-year-data)
:text (map :country continent-year-data)
:id (map :country continent-year-data)
:mode "markers"
:marker {:size (map :pop continent-year-data)
:sizemode "area"
:sizeref 200000}}))
(defn frame [year]
{:name year
:data (map (trace year %) continents)})
(def frames (map frame years))
(def first-trace (map (trace (first years) %) continents))
0.2s
Slider steps definition
(defn sliderstep [year]
{:method "animate"
:label year
:args [[year] {:mode "immediate"
:transition {:duration 200}
:frame {:duration 300 :redraw false}}]})
(def slidersteps (map sliderstep years))
0.1s
Layout definition
(def layout {:xaxis {:title "Life exp"
:range [30,85]
:showgrid false}
:yaxis {:title "GDP"
:type "log"
:showgrid false}
:hovermode "closest"
:updatemenus [{:x 0
:y 0
:yanchor "top"
:xanchor "left"
:showactive false
:direction "left"
:type "buttons"
:pad {:t 87 :r 10}
:buttons [{:method "animate"
:args [nil,{:mode "immediate"
:fromcurrent true
:transition {:duration 300}
:frame {:duration 500
:redraw false}}]
:label "Play"}
{:method "animate"
:args [[nil] {:mode "immediate"
:transition {:duration 0
:redraw false}}]
:label "Pause"}]}]
:sliders [{:pad {:l 130 :t 55}
:currentvalue {:visible true
:prefix "Year "
:xanchor "right"
:font {:size 20 :color "#666"}}
:steps slidersteps}]})
0.1s
Plot
{:nextjournal/viewer :plotly}
{:data first-trace
:layout layout
:frames frames}
0.7s