9  Graphs with Noj

9.1 Bar graphs

(ns chapter-4-data-visualisation.noj-examples
  (:require [tablecloth.api :as tc]
            [aerial.hanami.common :as hc]
            [aerial.hanami.templates :as ht]
            [scicloj.noj.v1.vis.hanami.templates :as vht]
            [scicloj.noj.v1.vis :as vis]
            [scicloj.noj.v1.stats :as stats]
            [scicloj.noj.v1.datasets :as datasets]
            [tech.v3.datatype :as dtype]
            [tech.v3.datatype.functional :as fun]
            [scicloj.kindly.v4.kind :as kind]
            [hiccup.core :as hiccup]
            [clojure2d.color :as color]
            [scicloj.kind-clerk.api :as kind-clerk]))
(kind-clerk/setup!)
:ok

9.2 Raw html

(-> "<p>Hello, <i>Noj</i>.</p>"
    kind/html)

Hello, Noj.

(kind/html
 "
<svg height=100 width=100>
<circle cx=50 cy=50 r=40 stroke='purple' stroke-width=3 fill='floralwhite' />
</svg> ")

9.3 Visualizing datases with Hanami

Noj offers a few convenience functions to make Hanami plotting work smoothly with Tablecloth and Kindly.

(def random-walk
  (let [n 20]
    (-> {:x (range n)
         :y (->> (repeatedly n #(- (rand) 0.5))
                 (reductions +))}
        tc/dataset)))

9.3.1 A simple plot

We can plot a Tablecloth datasete using a Hanami template:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200}))

Let us look inside the resulting vega-lite space. We can see the dataset is included as CSV:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200})
    kind/pprint)
{:encoding
 {:y {:field "y", :type "quantitative"},
  :x {:field "x", :type "quantitative"}},
 :mark {:type "circle", :size 200, :tooltip true},
 :width 400,
 :background "floralwhite",
 :height 300,
 :data
 {:values
  "x,y\n0,0.25915143611932323\n1,0.07679044186868467\n2,-0.16838373926426764\n3,-0.3472917379109737\n4,-0.4185674782284593\n5,-0.3275712090765166\n6,0.06499031613330208\n7,-0.12473464521100663\n8,0.24581959605889236\n9,0.3872343668945971\n10,0.20630731645770806\n11,0.4283007097190942\n12,0.8577253018355132\n13,1.029799282228336\n14,1.500296189747702\n15,1.802090709990422\n16,1.675173594897049\n17,1.5406670970402527\n18,1.5912246361060238\n19,1.7546356050436023\n",
  :format {:type "csv"}}}

9.3.2 Additional Hanami templates

The scicloj.noj.v1.vis.hanami.templates namespace add Hanami templates to Hanami’s own collection.

(-> datasets/mtcars
    (vis/hanami-plot vht/boxplot-chart
                     {:X :gear
                      :XTYPE :nominal
                      :Y :mpg}))

9.3.3 Layers

(-> random-walk
    (vis/hanami-layers
     {:TITLE "points and a line"}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"})]))

9.3.4 Concatenation

(-> random-walk
    (vis/hanami-vconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))
(-> random-walk
    (vis/hanami-hconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))

9.3.5 Linear regression

(-> datasets/mtcars
    (stats/add-predictions :mpg [:wt]
                           {:model-type :smile.regression/ordinary-least-square})
    (vis/hanami-layers {}
                       [(vis/hanami-plot nil
                                         ht/point-chart
                                         {:X :wt
                                          :Y :mpg
                                          :MSIZE 200
                                          :HEIGHT 200
                                          :WIDTH 200})
                        (vis/hanami-plot nil
                                         ht/line-chart
                                         {:X :wt
                                          :Y :mpg-prediction
                                          :MSIZE 5
                                          :MCOLOR "purple"
                                          :YTITLE :mpg})]))

9.3.6 Histogram

(-> datasets/iris
    (vis/hanami-histogram :sepal-width
                          {:nbins 10}))

9.3.7 Combining a few things together

The following is inspired by the example at Plotnine’s main page. Note how we add regression lines here. We take care of layout and colouring on our side, not using Vega-Lite for that.

(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> datasets/mtcars
      (tc/group-by :gear {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (stats/add-predictions :mpg [:wt]
                                         {:model-type :smile.regression/ordinary-least-square})
                  (tc/select-columns [:gear :wt :mpg :mpg-prediction])
                  (vis/hanami-layers {:TITLE (str "grear=" group-name)}
                                     [(vis/hanami-plot nil
                                                       ht/point-chart
                                                       {:X :wt
                                                        :Y :mpg
                                                        :MSIZE 200
                                                        :MCOLOR (pallete i)
                                                        :HEIGHT 200
                                                        :WIDTH 200})
                                      (vis/hanami-plot nil
                                                       ht/line-chart
                                                       {:X :wt
                                                        :Y :mpg-prediction
                                                        :MSIZE 5
                                                        :MCOLOR (pallete i)
                                                        :YTITLE :mpg})]
                                     ))))
           (vis/hanami-vconcat nil {}))))

A similar example with histograms:

(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> datasets/iris
      (tc/group-by :species {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (vis/hanami-histogram :sepal-width
                                        {:nbins 10}))))
           (vis/hanami-vconcat nil {}))))

Scatterplots and regression lines again, this time using Vega-Lite for layout and coloring (using its “facet” option).

(-> datasets/mtcars
    (tc/group-by [:gear])
    (stats/add-predictions :mpg [:wt]
                           {:model-type :smile.regression/ordinary-least-square})
    (tc/ungroup)
    (tc/select-columns [:gear :wt :mpg :mpg-prediction])
    (vis/hanami-layers {}
                       [(vis/hanami-plot nil
                                         ht/point-chart
                                         {:X :wt
                                          :Y :mpg
                                          :MSIZE 200
                                          :COLOR "gear"
                                          :HEIGHT 100
                                          :WIDTH 200})
                        (vis/hanami-plot nil
                                         ht/line-chart
                                         {:X :wt
                                          :Y :mpg-prediction
                                          :MSIZE 5
                                          :COLOR "gear"
                                          :YTITLE :mpg})])
    ((fn [spec]
       {:facet {:row {:field "gear"}}
        :spec (dissoc spec :data)
        :data (:data spec)}))
    kind/vega-lite)
:bye
:bye
source: book/chapter_4_data_visualisation/noj_examples.clj