diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c6f82e3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: clojure-data-cookbook CI + +on: [push] + +jobs: + build-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + # To install LaTeX to build PDF book + tinytex: true + # uncomment below and fill to pin a version + # version: SPECIFIC-QUARTO-VERSION-HERE + + - name: Prepare Java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + - name: Install Clojure tools + uses: DeLaGuardo/setup-clojure@11.0 + with: + cli: latest # Clojure CLI based on tools.deps + + # Optional step: + - name: Cache Clojure dependencies + uses: actions/cache@v3 + with: + path: | + ~/.m2/repository + ~/.gitlibs + ~/.deps.clj + # List all files containing dependencies: + key: cljdeps-${{ hashFiles('deps.edn') }} + restore-keys: cljdeps- + +# - name: Execute tests +# run: clojure -M:dev:test + + - name: Prepare dependencies + run: clojure -P -Sthreads 1 + + - name: Build Markdown + run: clojure -M:dev -m scicloj.claykind.main --verbose + + - name: Build the book + run: cd book && quarto render + + - name: Deploy + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: cd book && quarto publish gh-pages diff --git a/.gitignore b/.gitignore index b20c25b..f8e363a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ pom.xml.asc .dir-locals.el # ignore built website on main branch, only see on gh-pages -public/ \ No newline at end of file +public/ diff --git a/book/.gitignore b/book/.gitignore index dc8a160..0f95096 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1,2 +1,3 @@ /.quarto/ /_book/ +/qmd/ diff --git a/book/_quarto.yml b/book/_quarto.yml index d4d8bc1..57cbea6 100644 --- a/book/_quarto.yml +++ b/book/_quarto.yml @@ -3,18 +3,21 @@ project: format: html: - theme: cosmo + theme: + light: flatly + dark: darkly book: title: "Clojure Data Cookbook" + favicon: images/icon.svg chapters: - index.qmd - chapter_1_intro/1_1_welcome.md - chapter_1_intro/1_2_why_clojure.md - chapter_1_intro/1_3_set_up.md - - chapter_2_input_output/2_1_loading_data/index.qmd - - chapter_2_input_output/2_2_messy_data/index.qmd - - chapter_2_input_output/2_3_exporting_data/index.qmd - - chapter_3_data_manipulation/3_data_manipulation/index.qmd - - chapter_4_data_visualisation/4_2_graphs/index.qmd - - chapter_4_data_visualisation/noj_examples/index.qmd + - qmd/2-1-loading-data.md + - qmd/2-2-messy-data.md + - qmd/2-3-exporting-data.md + - qmd/3-data-manipulation.md + - qmd/4-2-graphs.md + - qmd/noj-examples.md diff --git a/book/chapter_2_input_output/2_3_exporting_data.clj b/book/chapter_2_input_output/2_3_exporting_data.clj index 376fa07..f53049a 100644 --- a/book/chapter_2_input_output/2_3_exporting_data.clj +++ b/book/chapter_2_input_output/2_3_exporting_data.clj @@ -59,8 +59,8 @@ ;; To do that call (comment - (clerk/build! {:paths "path/to/files..." - :index "book/index.clj"})) + (clerk-setup/build! {:paths "path/to/files..." + :index "book/index.clj"})) ;; More information in Clerk's docs: https://book.clerk.vision/#static-building diff --git a/book/chapter_4_data_visualisation/4_2_graphs.clj b/book/chapter_4_data_visualisation/4_2_graphs.clj index d3f9ca3..e5e2bbc 100644 --- a/book/chapter_4_data_visualisation/4_2_graphs.clj +++ b/book/chapter_4_data_visualisation/4_2_graphs.clj @@ -2,16 +2,10 @@ (ns chapter-4-data-visualisation.4-2-graphs (: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] - [hiccup.core :as hiccup] - [clojure2d.color :as color] [tablecloth.api :as tc] [scicloj.kind-clerk.api :as kind-clerk])) diff --git a/book/chapter_4_data_visualisation/4_2_graphs.md b/book/chapter_4_data_visualisation/4_2_graphs.md new file mode 100644 index 0000000..94ca5b7 --- /dev/null +++ b/book/chapter_4_data_visualisation/4_2_graphs.md @@ -0,0 +1,148 @@ +---format: + html: {toc: true, toc-depth: 4, theme: spacelab} +highlight-style: solarized +code-block-background: true +embed-resources: false +execute: {freeze: true} +--- + + + + + +```clojure +(ns chapter-4-data-visualisation.4-2-graphs + (: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] + [hiccup.core :as hiccup] + [clojure2d.color :as color] + [tablecloth.api :as tc])) +``` + +
Hello, Noj.
" vis/raw-html) diff --git a/book/chapter_4_data_visualisation/noj_examples.md b/book/chapter_4_data_visualisation/noj_examples.md new file mode 100644 index 0000000..dd2cb07 --- /dev/null +++ b/book/chapter_4_data_visualisation/noj_examples.md @@ -0,0 +1,393 @@ +---format: + html: {toc: true, toc-depth: 4, theme: spacelab} +highlight-style: solarized +code-block-background: true +embed-resources: false +execute: {freeze: true} +--- + + + + + +# Graphs + +## Bar graphs + +```clojure +(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])) +``` + +> **stdout** +> +> ..instrumented #'scicloj.metamorph.ml/model +> ..instrumented #'scicloj.metamorph.ml/explain +> ..instrumented #'scicloj.metamorph.ml/evaluate-pipelines +> ..instrumented #'scicloj.metamorph.ml/train +> ..instrumented #'scicloj.metamorph.ml/thaw-model +> ..instrumented #'scicloj.metamorph.ml/default-loss-fn +> ..instrumented #'scicloj.metamorph.ml/predict +> Register model: :smile.regression/ordinary-least-square +> Register model: :smile.regression/elastic-net +> Register model: :smile.regression/lasso +> Register model: :smile.regression/ridge +> Register model: :smile.regression/gradient-tree-boost +> Register model: :smile.regression/random-forest + +> **stderr** +> +> WARNING: test already refers to: #'clojure.core/test in namespace: scicloj.kindly.v4.kind, being replaced by: #'scicloj.kindly.v4.kind/test +> WARNING: seq already refers to: #'clojure.core/seq in namespace: scicloj.kindly.v4.kind, being replaced by: #'scicloj.kindly.v4.kind/seq +> WARNING: vector already refers to: #'clojure.core/vector in namespace: scicloj.kindly.v4.kind, being replaced by: #'scicloj.kindly.v4.kind/vector +> WARNING: set already refers to: #'clojure.core/set in namespace: scicloj.kindly.v4.kind, being replaced by: #'scicloj.kindly.v4.kind/set +> WARNING: map already refers to: #'clojure.core/map in namespace: scicloj.kindly.v4.kind, being replaced by: #'scicloj.kindly.v4.kind/map + +Hello, Noj.
" + vis/raw-html) +``` + + + +```clojure +(-> [:svg {:height 210 + :width 500} + [:line {:x1 0 + :y1 0 + :x2 200 + :y2 200 + :style "stroke:rgb(255,0,0);stroke-width:2"}]] + hiccup/html + vis/raw-html) +``` + + + +## Visualizing datases with Hanami + +Noj offers a few convenience functions to make [Hanami](https://github.com/jsa-aerial/hanami) plotting work smoothly with [Tablecloth](https://scicloj.github.io/tablecloth/) and [Kindly](https://scicloj.github.io/kindly/). + +```clojure +(def random-walk + (let [n 20] + (-> {:x (range n) + :y (->> (repeatedly n #(- (rand) 0.5)) + (reductions +))} + tc/dataset))) +``` + +q?1:L>=q?0:NaN}function pb(q){function L(Bb,Qb,bc=0,vc=Bb.length){if(bc>>1;0>Ga(Bb[dd],Qb)?bc=dd+1:vc=dd}while(bc jb(q(Bb),Qb),vb=(Bb,Qb)=>q(Bb)-Qb):(S=q===jb||q===eb?q:kb,vb=Ga=q);return{left:L,center:function(Bb,Qb,bc=0,vc=Bb.length){vc=L(Bb,Qb,bc,vc-1);return vc>bc&&vb(Bb[vc-1],Qb)>-vb(Bb[vc],Qb)?vc-1:vc},right:function(Bb,Qb,bc=0,vc=Bb.length){if(bc >>1;0>=Ga(Bb[dd],Qb)?bc=dd+1:vc=dd}while(bc =Kf?10:Ga>=eh?5:Ga>=fh?2:1;let Bb;0>vb?(Bb=Math.pow(10,-vb)/Ga,vb=Math.round(q*Bb), +Ga=Math.round(L*Bb),vb/Bb L&&--Ga,Bb=-Bb):(Bb=Math.pow(10,vb)*Ga,vb=Math.round(q/Bb),Ga=Math.round(L/Bb),vb*BbL&&--Ga);return GaS?wb(q,L,2*S):[vb,Ga,Bb]}function Ib(q,L,S){L=+L;q=+q;S=+S;if(!(0 =vb))return[];q=Bb-vb+1;L=Array(q);if(Ga)if(0>Qb)for(Ga=0;GaQb)for(Ga=0;Gaq?1/-q:q)}function sb(q,L,S=rb){if((Ga=q.length)&&!isNaN(L=+L)){if(0>=L||2>Ga)return+S(q[0],0,q);if(1<=L)return+S(q[Ga-1],Ga-1,q);var Ga;L*=Ga-1;Ga=Math.floor(L);var vb=+S(q[Ga],Ga,q);q=+S(q[Ga+1],Ga+1,q);return vb+(q-vb)*(L-Ga)}}function Na(q,L,S){q=+q;L=+L;S=2>(vb=arguments.length)?(L=q,q=0,1):3>vb?1:+S;for(var Ga= +-1,vb=Math.max(0,Math.ceil((L-q)/S))|0,Bb=Array(vb);++Ga>8&15|L>>4&240,L>>4&15|L&240,(L&15)<<4|L&15,1):8===S?t(L>>24&255,L>>16&255,L>>8&255,(L&255)/255):4===S?t(L>>12&15|L>>8&240,L>>8&15|L>>4&240,L>>4&15|L&240,((L&15)<<4|L&15)/255):null):(L=Dg.exec(q))?new na(L[1], +L[2],L[3],1):(L=th.exec(q))?new na(255*L[1]/100,255*L[2]/100,255*L[3]/100,1):(L=Xg.exec(q))?t(L[1],L[2],L[3],L[4]):(L=gh.exec(q))?t(255*L[1]/100,255*L[2]/100,255*L[3]/100,L[4]):(L=zg.exec(q))?$a(L[1],L[2]/100,L[3]/100,1):(L=uh.exec(q))?$a(L[1],L[2]/100,L[3]/100,L[4]):vh.hasOwnProperty(q)?E(vh[q]):"transparent"===q?new na(NaN,NaN,NaN,0):null}function E(q){return new na(q>>16&255,q>>8&255,q&255,1)}function t(q,L,S,Ga){0>=Ga&&(q=L=S=NaN);return new na(q,L,S,Ga)}function C(q){q instanceof Ta||(q=M(q)); +if(!q)return new na;q=q.rgb();return new na(q.r,q.g,q.b,q.opacity)}function ja(q,L,S,Ga){return 1===arguments.length?C(q):new na(q,L,S,null==Ga?1:Ga)}function na(q,L,S,Ga){this.r=+q;this.g=+L;this.b=+S;this.opacity=+Ga}function Xa(){return`#${Wa(this.r)}${Wa(this.g)}${Wa(this.b)}`}function wa(){const q=xa(this.opacity);return`${1===q?"rgb(":"rgba("}${pa(this.r)}, ${pa(this.g)}, ${pa(this.b)}${1===q?")":`, ${q})`}`}function xa(q){return isNaN(q)?1:Math.max(0,Math.min(1,q))}function pa(q){return Math.max(0, +Math.min(255,Math.round(q)||0))}function Wa(q){q=pa(q);return(16>q?"0":"")+q.toString(16)}function $a(q,L,S,Ga){0>=Ga?q=L=S=NaN:0>=S||1<=S?q=L=NaN:0>=L&&(q=NaN);return new I(q,L,S,Ga)}function ba(q){if(q instanceof I)return new I(q.h,q.s,q.l,q.opacity);q instanceof Ta||(q=M(q));if(!q)return new I;if(q instanceof I)return q;q=q.rgb();var L=q.r/255,S=q.g/255,Ga=q.b/255,vb=Math.min(L,S,Ga),Bb=Math.max(L,S,Ga),Qb=NaN,bc=Bb-vb,vc=(Bb+vb)/2;bc?(Qb=L===Bb?(S-Ga)/bc+6*(S vc?Bb+vb:2-Bb-vb,Qb*=60):bc=0 vc?0:Qb;return new I(Qb,bc,vc,q.opacity)}function ta(q,L,S,Ga){return 1===arguments.length?ba(q):new I(q,L,S,null==Ga?1:Ga)}function I(q,L,S,Ga){this.h=+q;this.s=+L;this.l=+S;this.opacity=+Ga}function F(q){q=(q||0)%360;return 0>q?q+360:q}function D(q){return Math.max(0,Math.min(1,q||0))}function y(q,L,S){return 255*(60>q?L+(S-L)*q/60:180>q?S:240>q?L+(S-L)*(240-q)/60:L)}function A(q){if(q instanceof W)return new W(q.l,q.a,q.b,q.opacity);if(q instanceof ya)return Sa(q); +q instanceof na||(q=C(q));var L=da(q.r),S=da(q.g),Ga=da(q.b),vb=ma(.2225045*L+.7168786*S+.0606169*Ga);if(L===S&&S===Ga)var Bb=L=vb;else Bb=ma((.4360747*L+.3850649*S+.1430804*Ga)/.96422),L=ma((.0139322*L+.0971045*S+.7141733*Ga)/.82521);return new W(116*vb-16,500*(Bb-vb),200*(vb-L),q.opacity)}function K(q,L,S,Ga){return 1===arguments.length?A(q):new W(q,L,S,null==Ga?1:Ga)}function W(q,L,S,Ga){this.l=+q;this.a=+L;this.b=+S;this.opacity=+Ga}function ma(q){return q>$h?Math.pow(q,1/3):q/Gh+Qh}function O(q){return q> +wh?q*q*q:Gh*(q-Qh)}function Y(q){return 255*(.0031308>=q?12.92*q:1.055*Math.pow(q,1/2.4)-.055)}function da(q){return.04045>=(q/=255)?q/12.92:Math.pow((q+.055)/1.055,2.4)}function z(q){if(q instanceof ya)return new ya(q.h,q.c,q.l,q.opacity);q instanceof W||(q=A(q));if(0===q.a&&0===q.b)return new ya(NaN,0 q.l?0:NaN,q.l,q.opacity);var L=Math.atan2(q.b,q.a)*Eg;return new ya(0>L?L+360:L,Math.sqrt(q.a*q.a+q.b*q.b),q.l,q.opacity)}function Z(q,L,S,Ga){return 1===arguments.length?z(q):new ya(q,L, +S,null==Ga?1:Ga)}function ya(q,L,S,Ga){this.h=+q;this.c=+L;this.l=+S;this.opacity=+Ga}function Sa(q){if(isNaN(q.h))return new W(q.l,0,0,q.opacity);var L=q.h*Rh;return new W(q.l,Math.cos(L)*q.c,Math.sin(L)*q.c,q.opacity)}function Qa(q,L,S,Ga){if(1===arguments.length){var vb=q;if(vb instanceof db)vb=new db(vb.h,vb.s,vb.l,vb.opacity);else{vb instanceof na||(vb=C(vb));var Bb=vb.g/255,Qb=vb.b/255,bc=(hh*Qb+vb.r/255*-1.7884503806-3.5172982438*Bb)/(hh+-1.7884503806-3.5172982438);Qb-=bc;var vc=(1.97294*(Bb- +bc)- -.29227*Qb)/-.90649;Qb=(Bb=Math.sqrt(vc*vc+Qb*Qb)/(1.97294*bc*(1-bc)))?Math.atan2(vc,Qb)*Eg-120:NaN;vb=new db(0>Qb?Qb+360:Qb,Bb,bc,vb.opacity)}}else vb=new db(q,L,S,null==Ga?1:Ga);return vb}function db(q,L,S,Ga){this.h=+q;this.s=+L;this.l=+S;this.opacity=+Ga}function X(q,L,S,Ga,vb){var Bb=q*q,Qb=Bb*q;return((1-3*q+3*Bb-Qb)*L+(4-6*Bb+3*Qb)*S+(1+3*q+3*Bb-3*Qb)*Ga+Qb*vb)/6}function Da(q){var L=q.length-1;return function(S){var Ga=0>=S?S=0:1<=S?(S=1,L-1):Math.floor(S*L),vb=q[Ga],Bb=q[Ga+1];return X((S- +Ga/L)*L,0 (S%=1)?++S:S)*L);return X((S-Ga/L)*L,q[(Ga+L-1)%L],q[Ga%L],q[(Ga+1)%L],q[(Ga+2)%L])}}function cc(q,L){return function(S){return q+S*L}}function Wb(q,L,S){return q=Math.pow(q,S),L=Math.pow(L,S)-q,S=1/S,function(Ga){return Math.pow(q+Ga*L,S)}}function xb(q,L){var S=L-q;return S?cc(q,180 S?S-360*Math.round(S/360):S):Ah(isNaN(q)?L:q)}function nb(q){return 1===(q=+q)? +jc:function(L,S){return S-L?Wb(L,S,q):Ah(isNaN(L)?S:L)}}function jc(q,L){var S=L-q;return S?cc(q,S):Ah(isNaN(q)?L:q)}function zc(q){return function(L){var S=L.length,Ga=Array(S),vb=Array(S),Bb=Array(S),Qb;for(Qb=0;Qb