2 Exploratory Data Analysis
(ns assignment.eda
(:require
[aerial.hanami.templates :as ht]
[clojure.math.combinatorics :as combo]
[fastmath.stats :as stats]
[scicloj.kindly.v4.kind :as kind]
[scicloj.ml.dataset :as ds]
[scicloj.noj.v1.vis.hanami :as hanami]))Load data
(defonce liver-disease
(ds/dataset "data/bupa.csv"
{:key-fn (fn [colname]
(-> colname
(clojure.string/replace #"\.|\s" "-")
clojure.string/lower-case
keyword))}))2.1 Tables and nature of data
First seven of data
(ds/head liver-disease 7)data/bupa.csv [7 7]:
| :mcv | :alkphos | :sgpt | :sgot | :gammagt | :drinks | :selector |
|---|---|---|---|---|---|---|
| 85 | 92 | 45 | 27 | 31 | 0.0 | 1 |
| 85 | 64 | 59 | 32 | 23 | 0.0 | 2 |
| 86 | 54 | 33 | 16 | 54 | 0.0 | 2 |
| 91 | 78 | 34 | 24 | 36 | 0.0 | 2 |
| 87 | 70 | 12 | 28 | 10 | 0.0 | 2 |
| 98 | 55 | 13 | 17 | 17 | 0.0 | 2 |
| 88 | 62 | 20 | 17 | 9 | 0.5 | 1 |
Descriptive statistics of columns
(ds/info liver-disease)data/bupa.csv: descriptive-stats [7 11]:
| :col-name | :datatype | :n-valid | :n-missing | :min | :mean | :max | :standard-deviation | :skew | :first | :last |
|---|---|---|---|---|---|---|---|---|---|---|
| :mcv | :int16 | 345 | 0 | 65.0 | 90.15942029 | 103.0 | 4.44809597 | -0.38843312 | 85.0 | 98.0 |
| :alkphos | :int16 | 345 | 0 | 23.0 | 69.86956522 | 138.0 | 18.34767034 | 0.75366677 | 92.0 | 99.0 |
| :sgpt | :int16 | 345 | 0 | 4.0 | 30.40579710 | 155.0 | 19.51230891 | 3.06349864 | 45.0 | 57.0 |
| :sgot | :int16 | 345 | 0 | 5.0 | 24.64347826 | 82.0 | 10.06449375 | 2.29307245 | 27.0 | 45.0 |
| :gammagt | :int16 | 345 | 0 | 5.0 | 38.28405797 | 297.0 | 39.25461617 | 2.86609356 | 31.0 | 65.0 |
| :drinks | :float64 | 345 | 0 | 0.0 | 3.45507246 | 20.0 | 3.33783526 | 1.54381943 | 0.0 | 20.0 |
| :selector | :int16 | 345 | 0 | 1.0 | 1.57971014 | 2.0 | 0.49432233 | -0.32438319 | 1.0 | 1.0 |
2.1.1 What is column :selector?
(set (:selector liver-disease))#{1 2}:selector takes two value only, 0 and 1. Let’s see descriptive statistics of each group.
(ds/info (tech.v3.dataset/filter liver-disease #(= (:selector %) 1)))data/bupa.csv: descriptive-stats [7 11]:
| :col-name | :datatype | :n-valid | :n-missing | :min | :mean | :max | :standard-deviation | :skew | :first | :last |
|---|---|---|---|---|---|---|---|---|---|---|
| :mcv | :int16 | 145 | 0 | 78.0 | 90.63448276 | 99.0 | 3.86906139 | -0.13253898 | 85.0 | 98.0 |
| :alkphos | :int16 | 145 | 0 | 23.0 | 71.97931034 | 138.0 | 18.59079408 | 0.62377127 | 92.0 | 99.0 |
| :sgpt | :int16 | 145 | 0 | 10.0 | 31.20689655 | 103.0 | 15.77792786 | 1.88282131 | 45.0 | 57.0 |
| :sgot | :int16 | 145 | 0 | 5.0 | 22.78620690 | 57.0 | 7.73806088 | 1.14900370 | 27.0 | 45.0 |
| :gammagt | :int16 | 145 | 0 | 5.0 | 31.54482759 | 203.0 | 33.22502807 | 2.83802686 | 31.0 | 65.0 |
| :drinks | :float64 | 145 | 0 | 0.0 | 3.54137931 | 20.0 | 3.92972268 | 1.80063118 | 0.0 | 20.0 |
| :selector | :int16 | 145 | 0 | 1.0 | 1.00000000 | 1.0 | 0.00000000 | 1.0 | 1.0 |
(ds/info (tech.v3.dataset/filter liver-disease #(= (:selector %) 2)))data/bupa.csv: descriptive-stats [7 11]:
| :col-name | :datatype | :n-valid | :n-missing | :min | :mean | :max | :standard-deviation | :skew | :first | :last |
|---|---|---|---|---|---|---|---|---|---|---|
| :mcv | :int16 | 200 | 0 | 65.0 | 89.8150 | 103.0 | 4.80481072 | -0.40881586 | 85.0 | 96.0 |
| :alkphos | :int16 | 200 | 0 | 37.0 | 68.3400 | 134.0 | 18.06199263 | 0.86583251 | 64.0 | 69.0 |
| :sgpt | :int16 | 200 | 0 | 4.0 | 29.8250 | 155.0 | 21.84491650 | 3.31719988 | 59.0 | 53.0 |
| :sgot | :int16 | 200 | 0 | 8.0 | 25.9900 | 82.0 | 11.28880354 | 2.36380090 | 32.0 | 43.0 |
| :gammagt | :int16 | 200 | 0 | 8.0 | 43.1700 | 297.0 | 42.51846894 | 2.80462310 | 23.0 | 203.0 |
| :drinks | :float64 | 200 | 0 | 0.0 | 3.3925 | 12.0 | 2.84166661 | 0.84994446 | 0.0 | 12.0 |
| :selector | :int16 | 200 | 0 | 2.0 | 2.0000 | 2.0 | 0.00000000 | 2.0 | 2.0 |
The data looks similar in terms of summary statistics per column in either selector equals 1 or 2, above. Three columns I am focusing on are, mean, standard deviation, and skew. Mean, standard deviation, and skew are similar for both groups in :mcv and :alkphos. The other four columns deviate in some way from the other :selector groups in either mean, standard deviation, and/or skew.
2.2 Plots
(def cols-of-interest
(remove #{:selector} (ds/column-names liver-disease)))2.2.1 Linearity with response and histogram of regressor.
^kind/vega
(let [dataset liver-disease
make-plot (fn [field]
(-> dataset
(hanami/vconcat {} ;can switch to hconcat
[(hanami/plot dataset ht/point-chart
{:X field :XSCALE {:zero false}
:Y :drinks :YSCALE {:zero false}})
(hanami/histogram dataset field {:nbins 20})])))]
(->> (map make-plot cols-of-interest) (hanami/hconcat {} {})))(comment ;make plot alternative were I can change height and width but not nbins
(let [dataset liver-disease]
(-> dataset
(hanami/hconcat {}
[(hanami/plot dataset ht/point-chart
{:HEIGHT 125 :WIDTH 175
:X :mcv :XSCALE {:zero false}
:Y :drinks :YSCALE {:zero false}})
(hanami/plot dataset ht/bar-chart
{:HEIGHT 125 :WIDTH 175
:X field :YAGG :count})]))))2.2.2 Pairs-plots.
^kind/vega
(let [data (ds/rows liver-disease :as-maps)
column-names cols-of-interest]
{:data {:values data}
:repeat {:column column-names
:row column-names}
:spec {:height 100 :width 100
:mark "circle"
:encoding {:x {:field {:repeat "column"} :type "quantitative" :scale {:zero false}}
:y {:field {:repeat "row"} :type "quantitative" :scale {:zero false}}}}})(let [combos (combo/combinations cols-of-interest 2)]
(for [[x y] combos]
(assoc {} [x y] (stats/correlation (get liver-disease x) (get liver-disease y)))))({[:mcv :alkphos] 0.04410299969572895}
{[:mcv :sgpt] 0.14769504803185451}
{[:mcv :sgot] 0.1877651531698029}
{[:mcv :gammagt] 0.2223144936927525}
{[:mcv :drinks] 0.3126795966511246}
{[:alkphos :sgpt] 0.07620760528013328}
{[:alkphos :sgot] 0.1460565465048546}
{[:alkphos :gammagt] 0.13314039838436367}
{[:alkphos :drinks] 0.10079605839698665}
{[:sgpt :sgot] 0.7396748677149187}
{[:sgpt :gammagt] 0.5034352544029691}
{[:sgpt :drinks] 0.20684793492452233}
{[:sgot :gammagt] 0.5276259280455446}
{[:sgot :drinks] 0.2795877736826015}
{[:gammagt :drinks] 0.3412239586693896})We can see pearson correlation between each pair of variables of interest. Correlations closer to |1| represent variables that have a strong relationship to each other. For example, Looking at both the pairs-plot and the correlations, we see :sgpt :sgot are the most related variables and are related in the positive direction. Below we focus on the correlation of regressors on the response only.
(let [combos (combo/combinations cols-of-interest 2)]
(for [[x y] combos
:when (or (= :drinks x) (= :drinks y))]
(assoc {} [x y] (stats/correlation (get liver-disease x) (get liver-disease y)))))({[:mcv :drinks] 0.3126795966511246}
{[:alkphos :drinks] 0.10079605839698665}
{[:sgpt :drinks] 0.20684793492452233}
{[:sgot :drinks] 0.2795877736826015}
{[:gammagt :drinks] 0.3412239586693896})(comment ;another way to make histograms for diagonals
(let [data (ds/rows liver-disease :as-maps)
column-names cols-of-interest]
{:data {:values data}
:repeat {:column column-names}
:spec {:mark "bar"
:encoding {:x {:field {:repeat "column"} :type "quantitative"}
:y {:aggregate "count"}}}})
(stats/correlation-matrix (ds/columns liver-disease) :spearman))2.2.3 Box-plots.
^kind/vega
(let [data (ds/rows liver-disease :as-maps)
column-names (remove #{:selector} (ds/column-names liver-disease))]
{:data {:values data}
:repeat {:column column-names}
:spec {:width 60 :mark "boxplot"
:encoding {:y {:field {:repeat "column"} :type "quantitative" :scale {:zero false}}}}})Looking at the box-plots, the circle points are outliers, viz. points outside Q1 - 1.5IQR or Q3 + 1.5IQR. Below we count the number of outliers per column.
(let [columns cols-of-interest]
(for [column columns]
(assoc {} column (count (stats/outliers (get liver-disease column))))))({:mcv 2} {:alkphos 9} {:sgpt 28} {:sgot 24} {:gammagt 27} {:drinks 5})(comment
; created permutation of columns of interest. Wanted combinations, below
(for [x cols-of-interest
y (rest cols-of-interest)
:when (not= x y)]
[x y]))source: src/assignment/eda.clj