From f563fd3d5e65a2f028375ba2930157399d4e9a9b Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 30 Jul 2024 14:50:39 +0300 Subject: [PATCH 01/23] updated deps: fastmath 3-SNAPSHOT, etc. --- deps.edn | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/deps.edn b/deps.edn index 57c5f60..4b95139 100644 --- a/deps.edn +++ b/deps.edn @@ -1,19 +1,20 @@ {:paths ["src" "resources"] :deps {org.clojure/clojure {:mvn/version "1.11.1"} scicloj/tablecloth {:mvn/version "7.029.2"} - org.scicloj/kindly {:mvn/version "4-beta4"} - generateme/fastmath {:mvn/version "2.4.0"} + org.scicloj/kindly {:mvn/version "4-beta5"} + generateme/fastmath {:mvn/version "3.0.0-SNAPSHOT"} aerial.hanami/aerial.hanami {:mvn/version "0.20.0"} + org.scicloj/hanamicloth {:mvn/version "1-alpha4-SNAPSHOT"} cljplot/cljplot {:mvn/version "0.0.3"} - scicloj/metamorph.ml {:mvn/version "0.8.2"} - scicloj/scicloj.ml.smile {:mvn/version "7.2"} + scicloj/metamorph.ml {:git/url "https://github.com/scicloj/metamorph.ml" + :git/sha "bd1b51fc1f64a5759d242c2052e115108131416e"} + ;; scicloj/scicloj.ml.smile {:mvn/version "7.2"} scicloj/scicloj.ml.tribuo {:git/url "https://github.com/scicloj/scicloj.ml.tribuo" - :git/sha "1bde7c4" - :git/tag "v0.1.0"} + :git/sha "f4ebf1e1bb78eb99dd35ca886d75b9f65d800e8d"} + org.tribuo/tribuo-regression-sgd {:mvn/version "4.2.0"} clj-python/libpython-clj {:mvn/version "2.025"} scicloj/clojisr {:mvn/version "1.0.0"} - org.scicloj/tempfiles {:mvn/version "1-alpha2"} - kixi/stats {:mvn/version "0.5.5"}} + org.scicloj/tempfiles {:mvn/version "1-alpha2"}} :aliases {:build {:deps {io.github.clojure/tools.build {:mvn/version "0.9.6"} slipset/deps-deploy {:mvn/version "0.2.1"}} From c6e8359098b9bbb5dc842cdb92d93046da19aa11 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 30 Jul 2024 14:51:27 +0300 Subject: [PATCH 02/23] metamorph.ml updated (to make linear regression work) --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 4b95139..12fcb64 100644 --- a/deps.edn +++ b/deps.edn @@ -7,7 +7,7 @@ org.scicloj/hanamicloth {:mvn/version "1-alpha4-SNAPSHOT"} cljplot/cljplot {:mvn/version "0.0.3"} scicloj/metamorph.ml {:git/url "https://github.com/scicloj/metamorph.ml" - :git/sha "bd1b51fc1f64a5759d242c2052e115108131416e"} + :git/sha "bb151eec256f0e4443cdb4f39be8609f6078a4ad"} ;; scicloj/scicloj.ml.smile {:mvn/version "7.2"} scicloj/scicloj.ml.tribuo {:git/url "https://github.com/scicloj/scicloj.ml.tribuo" :git/sha "f4ebf1e1bb78eb99dd35ca886d75b9f65d800e8d"} From c2d01323e134875c777dac3b991f897bd7779433 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 30 Jul 2024 19:11:06 +0300 Subject: [PATCH 03/23] updated OLS interactions tutorial --- notebooks/noj_book/interactions_ols.clj | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/notebooks/noj_book/interactions_ols.clj b/notebooks/noj_book/interactions_ols.clj index 804e25e..a08d84a 100644 --- a/notebooks/noj_book/interactions_ols.clj +++ b/notebooks/noj_book/interactions_ols.clj @@ -9,11 +9,12 @@ [scicloj.metamorph.core :as mm] [scicloj.metamorph.ml :as ml] [scicloj.metamorph.ml.loss :as loss] + [scicloj.metamorph.ml.regression] [tablecloth.api :as tc] [tablecloth.column.api :as tcc] [tablecloth.pipeline :as tcpipe] [tech.v3.dataset.modelling :as modelling] - [scicloj.ml.smile.regression])) + [scicloj.ml.tribuo])) ^:kindly/hide-code (def md @@ -38,10 +39,12 @@ (md "First we build an additive model, which model equation is $$sales = b0 + b1 * youtube + b2 * facebook$$") +(def linear-model-config {:model-type :fastmath/ols}) + (def additive-pipeline (mm/pipeline {:metamorph/id :model} - (ml/model {:model-type :smile.regression/ordinary-least-square}))) + (ml/model linear-model-config))) (md "We evaluate it, ") (def evaluations @@ -53,8 +56,10 @@ $$sales = b0 + b1 * youtube + b2 * facebook$$") {:other-metrices [{:name :r2 :metric-fn fmstats/r2-determination}]})) -(md "and print the result:") -(-> evaluations flatten first :fit-ctx :model ml/thaw-model) +(md "and print the resulting model: +(note that the `:sales` term means the intercept `b0`)") +(md "(note that )") +(-> evaluations flatten first :fit-ctx :model ml/tidy) (md "We have the following metrics:") (md "$RMSE$") @@ -69,7 +74,7 @@ $$sales = b0 + b1 * youtube + b2 * facebook + b3 * (youtube * facebook)$$") (def pipe-interaction (mm/pipeline (tcpipe/add-column :youtube*facebook (fn [ds] (tcc/* (ds :youtube) (ds :facebook)))) - {:metamorph/id :model}(ml/model {:model-type :smile.regression/ordinary-least-square}))) + {:metamorph/id :model} (ml/model linear-model-config))) (md "Again we evaluate the model,") (def evaluations @@ -82,8 +87,8 @@ $$sales = b0 + b1 * youtube + b2 * facebook + b3 * (youtube * facebook)$$") :metric-fn fmstats/r2-determination}]})) -(md "and print it and the performance metrices:") -(-> evaluations flatten first :fit-ctx :model ml/thaw-model) +(md "and print it and the performance metrics:") +(-> evaluations flatten first :fit-ctx :model ml/tidy) (md "As the multiplcation of `youtube*facebook` is as well statistically relevant, it suggests that there is indeed an interaction between these 2 predictor variables youtube and facebook.") From 8f75e3baf0b2d6b35f882f92c38bfcf12cbbec6a Mon Sep 17 00:00:00 2001 From: daslu Date: Mon, 5 Aug 2024 12:01:47 +0300 Subject: [PATCH 04/23] added some Tribuo dependencies --- deps.edn | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps.edn b/deps.edn index 12fcb64..bb53bc4 100644 --- a/deps.edn +++ b/deps.edn @@ -12,6 +12,9 @@ scicloj/scicloj.ml.tribuo {:git/url "https://github.com/scicloj/scicloj.ml.tribuo" :git/sha "f4ebf1e1bb78eb99dd35ca886d75b9f65d800e8d"} org.tribuo/tribuo-regression-sgd {:mvn/version "4.2.0"} + org.tribuo/tribuo-regression-tree {:mvn/version "4.2.0"} + org.tribuo/tribuo-classification-sgd {:mvn/version "4.2.0"} + org.tribuo/tribuo-classification-tree {:mvn/version "4.2.0"} clj-python/libpython-clj {:mvn/version "2.025"} scicloj/clojisr {:mvn/version "1.0.0"} org.scicloj/tempfiles {:mvn/version "1-alpha2"}} From c9fd4837e2b173f0492a99ecd89c321459c3bfc8 Mon Sep 17 00:00:00 2001 From: daslu Date: Mon, 5 Aug 2024 12:02:07 +0300 Subject: [PATCH 05/23] updated docs --- notebooks/noj_book/automl.clj | 20 ++++++++--------- notebooks/noj_book/ml_basic.clj | 39 ++++++++++++++++----------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/notebooks/noj_book/automl.clj b/notebooks/noj_book/automl.clj index 8333ccb..030787d 100644 --- a/notebooks/noj_book/automl.clj +++ b/notebooks/noj_book/automl.clj @@ -8,7 +8,8 @@ ;; (ns noj-book.automl - (:require [noj-book.ml-basic :as ml-basic])) + (:require [noj-book.ml-basic :as ml-basic] + [scicloj.kindly.v4.kind :as kind])) ;; ## The metamorph pipeline abstraction ;; For doing automl, it is very useful to be able to handle the steps @@ -289,19 +290,17 @@ train-ctx (def titanic-k-fold (tc/split->seq ml-basic/numeric-titanic-data :kfold {:seed 12345})) ;; The list of the model types we want to try: (def models [{:model-type :metamorph.ml/dummy-classifier} - {:model-type :smile.classification/random-forest} - {:model-type :smile.classification/logistic-regression} - {:model-type :smile.classification/decision-tree} - {:model-type :smile.classification/ada-boost} {:model-type :scicloj.ml.tribuo/classification - :tribuo-components [{:name "trainer" + :tribuo-components [{:name "logistic" + :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}] + :tribuo-trainer-name "logistic"} + {:model-type :scicloj.ml.tribuo/classification + :tribuo-components [{:name "random-forest" :type "org.tribuo.classification.dtree.CARTClassificationTrainer" :properties {:maxDepth "8" :useRandomSplitPoints "false" :fractionFeaturesInSplit "0.5"}}] - :tribuo-trainer-name "trainer"}]) - - + :tribuo-trainer-name "random-forest"}]) ;; This uses models from Smile and Tribuo, but could be any @@ -365,7 +364,8 @@ train-ctx (-> (make-results-ds evaluation-results-all) (tc/unique-by) (tc/order-by [:mean-accuracy] :desc) - (tc/head)) + (tc/head) + (kind/table)) ;; ## Best practices for data transformation steps in or outside pipeline ;; diff --git a/notebooks/noj_book/ml_basic.clj b/notebooks/noj_book/ml_basic.clj index 2554ce6..d051d05 100644 --- a/notebooks/noj_book/ml_basic.clj +++ b/notebooks/noj_book/ml_basic.clj @@ -11,7 +11,8 @@ (:require [tablecloth.api :as tc] [scicloj.metamorph.ml.toydata :as data] [tech.v3.dataset :as ds] - [camel-snake-kebab.core :as csk])) + [camel-snake-kebab.core :as csk] + [scicloj.metamorph.ml :as ml])) ;; ## Inspect data ;; @@ -148,9 +149,16 @@ split ;; ## Logistic regression ;; Next model to use is Logistic Regression -(require '[scicloj.ml.smile.classification]) +(require '[scicloj.ml.tribuo]) + + + +(def lreg-model (ml/train (:train split) + {:model-type :scicloj.ml.tribuo/classification + :tribuo-components [{:name "logistic" + :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}] + :tribuo-trainer-name "logistic"})) -(def lreg-model (ml/train (:train split) {:model-type :smile.classification/logistic-regression})) (def lreg-prediction (ml/predict (:test split) lreg-model)) @@ -163,7 +171,13 @@ split ;; ## Random forest ;; Next is random forest -(def rf-model (ml/train (:train split) {:model-type :smile.classification/random-forest})) +(def rf-model (ml/train (:train split) {:model-type :scicloj.ml.tribuo/classification + :tribuo-components [{:name "random-forest" + :type "org.tribuo.classification.dtree.CARTClassificationTrainer" + :properties {:maxDepth "8" + :useRandomSplitPoints "false" + :fractionFeaturesInSplit "0.5"}}] + :tribuo-trainer-name "random-forest"})) (def rf-prediction (ml/predict (:test split) rf-model)) @@ -173,22 +187,7 @@ split ;; best so far, 71 % ;; -;; From the logistic regression model we can get via java Interop -;; some model explanations, for example the variable importance. - -(->> - (map - (fn [predictor importance] - (hash-map :predictor (-> predictor str csk/->kebab-case-keyword) - :importance importance)) - - (-> rf-model ml/thaw-model .formula .predictors) - (-> rf-model ml/thaw-model .importance)) - (sort-by :importance) - reverse) - -;; we can see that :sex is more important to predict :survived then -;; :pclass and :embark +;; TODO: Extract feature importance. ;; # Next steps ;; We could now go further and trying to improve the features / the model type From d2e2dbe3937acc28672445c7dc896a363450e958 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:46:55 +0300 Subject: [PATCH 06/23] book cleanup --- notebooks/chapters.edn | 5 - notebooks/noj_book/more_visualization.clj | 106 ------------ notebooks/noj_book/python.clj | 53 ------ .../noj_book/statistical_visualization.clj | 86 ---------- notebooks/noj_book/statistics.clj | 71 -------- notebooks/noj_book/visualization.clj | 160 ------------------ 6 files changed, 481 deletions(-) delete mode 100644 notebooks/noj_book/more_visualization.clj delete mode 100644 notebooks/noj_book/python.clj delete mode 100644 notebooks/noj_book/statistical_visualization.clj delete mode 100644 notebooks/noj_book/statistics.clj delete mode 100644 notebooks/noj_book/visualization.clj diff --git a/notebooks/chapters.edn b/notebooks/chapters.edn index 296f970..5f4e5b2 100644 --- a/notebooks/chapters.edn +++ b/notebooks/chapters.edn @@ -3,10 +3,5 @@ "automl" "interactions_ols" "datasets" - "python" - "statistics" - "visualization" - "statistical_visualization" - "more_visualization" "visualizing_correlation_matrices" "known_issues"] diff --git a/notebooks/noj_book/more_visualization.clj b/notebooks/noj_book/more_visualization.clj deleted file mode 100644 index f500657..0000000 --- a/notebooks/noj_book/more_visualization.clj +++ /dev/null @@ -1,106 +0,0 @@ -;; # More visualization examples (to be deprecated 🛠) - -;; author: Daniel Slutsky - -;; This functionality will soon be replated by [Hanamicloth](https://scicloj.github.io/hanamicloth/). - -(ns noj-book.more-visualization - (:require [aerial.hanami.templates :as ht] - [clojure2d.color :as color] - [noj-book.datasets :as datasets] - [scicloj.kindly.v4.kind :as kind] - [scicloj.noj.v1.stats :as stats] - [scicloj.noj.v1.vis.hanami :as vis.hanami] - [scicloj.noj.v1.vis.stats :as vis.stats] - [tablecloth.api :as tc])) - -;; ## Combining a few things together -;; -;; The following is inspired by the example at Plotnine's [main page](https://plotnine.readthedocs.io/en/stable/). -;; 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 - (vis.stats/linear-regression-plot - :mpg :wt - {:TITLE (str "grear=" group-name) - :X :wt - :MCOLOR (pallete i) - :HEIGHT 200 - :WIDTH 200 - :point-options {:MSIZE 200} - :line-options {:MSIZE 5}})))) - (vis.hanami/vconcat nil {})))) - -;; Alternatively, using a grouped dataset: - -(let [pallete (->> :accent - color/palette - (mapv color/format-hex))] - (-> datasets/mtcars - (tc/map-columns :color [:gear] #(-> % (- 3) pallete)) - (tc/group-by [:gear]) - (vis.stats/linear-regression-plot - :mpg :wt - {:X :wt - :MCOLOR {:expr "datum.color"} - :HEIGHT 200 - :WIDTH 200 - :point-options {:MSIZE 200} - :line-options {:MSIZE 5}}) - (tc/order-by [:gear]))) - -;; 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.stats/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/combined-plot - ht/layer-chart - {} - :LAYER [[ht/point-chart - {:X :wt - :Y :mpg - :MSIZE 200 - :COLOR "gear" - :HEIGHT 100 - :WIDTH 200}] - [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) diff --git a/notebooks/noj_book/python.clj b/notebooks/noj_book/python.clj deleted file mode 100644 index 2a575af..0000000 --- a/notebooks/noj_book/python.clj +++ /dev/null @@ -1,53 +0,0 @@ -;; # Python (experimental 🛠) - -;; author: Daniel Slutsky - -(ns noj-book.python - (:require [libpython-clj2.require :refer [require-python]] - [libpython-clj2.python :refer [py. py.. py.-] :as py] - [scicloj.noj.v1.vis.python :as vis.python] - [tablecloth.api :as tc] - [tablecloth.column.api :as tcc])) - - -;; ## Using Python visualizations - -;; Noj offers methods to include Python plots in [Kindly](https://scicloj.github.io/kindly-noted/kindly) visualizations: the `vis.python/with-pyplot` macro and the `vis.python/pyplot` function. - -;; They are based on the [Parens for Pyplot](http://gigasquidsoftware.com/blog/2020/01/18/parens-for-pyplot/) blog post at Squid's blog. - -(require-python '[numpy :as np] - '[numpy.random :as np.random] - 'matplotlib.pyplot - '[seaborn :as sns] - 'json) - -(def sine-data - (-> {:x (range 0 (* 3 np/pi) 0.1)} - tc/dataset - (tc/add-column :y #(tcc/sin (:x %))))) - -(vis.python/with-pyplot - (matplotlib.pyplot/plot - (:x sine-data) - (:y sine-data))) - -(vis.python/pyplot - #(matplotlib.pyplot/plot - (:x sine-data) - (:y sine-data))) - -;; https://seaborn.pydata.org/tutorial/introduction -(let [tips (sns/load_dataset "tips")] - (sns/set_theme) - (vis.python/pyplot - #(sns/relplot :data tips - :x "total_bill" - :y "tip" - :col "time" - :hue "smoker" - :style "smoker" - :size "size"))) - - -:bye diff --git a/notebooks/noj_book/statistical_visualization.clj b/notebooks/noj_book/statistical_visualization.clj deleted file mode 100644 index 4f4ccaa..0000000 --- a/notebooks/noj_book/statistical_visualization.clj +++ /dev/null @@ -1,86 +0,0 @@ -;; # Statistical Visualization (to be depracated 🛠) - -;; This functionality will soon be replated by [Hanamicloth](https://scicloj.github.io/hanamicloth/). - -;; author: Daniel Slutsky - -(ns noj-book.statistical-visualization - (:require [aerial.hanami.templates :as ht] - [noj-book.datasets :as datasets] - [scicloj.kindly.v4.kind :as kind] - [scicloj.noj.v1.stats :as stats] - [scicloj.noj.v1.vis.hanami :as vis.hanami] - [scicloj.noj.v1.vis.stats :as vis.stats] - [tablecloth.api :as tc])) - -;; ## Linear regression - -(-> datasets/mtcars - (stats/add-predictions :mpg [:wt] - {:model-type :smile.regression/ordinary-least-square}) - (vis.hanami/combined-plot - ht/layer-chart - {:X :wt - :MSIZE 200 - :HEIGHT 200} - :LAYER [[ht/point-chart - {:Y :mpg - :WIDTH 200}] - [ht/line-chart - {:Y :mpg-prediction - :MSIZE 5 - :MCOLOR "purple" - :YTITLE :mpg}]])) - -;; Alternatively: - -(-> datasets/mtcars - (vis.stats/linear-regression-plot - :mpg :wt - {:HEIGHT 200 - :WIDTH 200 - :point-options {:MSIZE 200} - :line-options {:MSIZE 5 - :MCOLOR "purple"}})) - -;; And in a grouped dataset case: - -(-> datasets/mtcars - (tc/group-by [:gear]) - (vis.stats/linear-regression-plot - :mpg :wt - {:HEIGHT 200 - :WIDTH 200 - :point-options {:MSIZE 200} - :line-options {:MSIZE 5 - :MCOLOR "purple"}})) - - -;; ## Histogram - -;; A [histogram](https://en.wikipedia.org/wiki/Histogram) groups values in bins, -;; counts them, -;; and creates a corresponding bar-chart. -;; -;; The `vis.stats/histogram` functions does that behind the scenes, -;; and generates a Vega-Lite spec using Hanami. - -(-> datasets/iris - (vis.stats/histogram :sepal-width - {:nbins 10})) - -(-> datasets/iris - (vis.stats/histogram :sepal-width - {:nbins 10}) - kind/pprint) - -;; The resulting spec can be customized further: - -(-> datasets/iris - (vis.stats/histogram :sepal-width - {:nbins 10}) - ;; varying the resulting vega-lite spec: - (assoc :height 125 - :width 175)) - -:bye diff --git a/notebooks/noj_book/statistics.clj b/notebooks/noj_book/statistics.clj deleted file mode 100644 index ef830a7..0000000 --- a/notebooks/noj_book/statistics.clj +++ /dev/null @@ -1,71 +0,0 @@ -;; # Statistics (experimental 🛠) - -;; author: Daniel Slutsky - -(ns noj-book.statistics - (:require [scicloj.noj.v1.stats :as stats] - [tablecloth.api :as tc])) - -;; ## Example data - -(def iris - (-> "https://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv" - (tc/dataset {:key-fn keyword}) - (tc/rename-columns {:Sepal.Length :sepal-length - :Sepal.Width :sepal-width - :Petal.Length :petal-length - :Petal.Width :petal-width - :Species :species}))) - -;; ## Multivariate regression - -;; The `stats/regression-model` function computes a regressiom model (using `scicloj.ml`) -;; and adds some relevant information such as the `R^2` measure. - -(-> iris - (stats/regression-model - :sepal-length - [:sepal-width :petal-length :petal-width] - {:model-type :smile.regression/elastic-net}) - (dissoc :model-data)) - -(-> iris - (stats/regression-model - :sepal-length - [:sepal-width :petal-length :petal-width] - {:model-type :smile.regression/ordinary-least-square}) - (dissoc :model-data)) - -;; The `stats/linear-regression-model` convenience function -;; uses specifically the `:smile.regression/ordinary-least-square` model type. - -(-> iris - (stats/linear-regression-model - :sepal-length - [:sepal-width :petal-length :petal-width]) - (dissoc :model-data)) - -;; ## Adding regression predictions to a dataset - -;; The `stats/add-predictions` function -;; models a target column using feature columns, -;; adds a new prediction column with the model predictions. - -(-> iris - (stats/add-predictions - :sepal-length - [:sepal-width :petal-length :petal-width] - {:model-type :smile.regression/ordinary-least-square})) - -;; It attaches the model's information -;; to the metadata of that new column. - -(-> iris - (stats/add-predictions - :sepal-length - [:sepal-width :petal-length :petal-width] - {:model-type :smile.regression/ordinary-least-square}) - :sepal-length-prediction - meta - (update :model - dissoc :model-data :predict :predictions)) diff --git a/notebooks/noj_book/visualization.clj b/notebooks/noj_book/visualization.clj deleted file mode 100644 index b7a5820..0000000 --- a/notebooks/noj_book/visualization.clj +++ /dev/null @@ -1,160 +0,0 @@ -;; # Visualization (too be deprecated 🛠) - -;; author: Daniel Slutsky - -;; This functionality will soon be replated by [Hanamicloth](https://scicloj.github.io/hanamicloth/). - -(ns noj-book.visualization - (:require [aerial.hanami.templates :as ht] - [noj-book.datasets :as datasets] - [scicloj.kindly.v4.kind :as kind] - [scicloj.noj.v1.vis.hanami :as vis.hanami] - [tablecloth.api :as tc])) - -;; ## 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/). - -(def random-walk - (let [n 20] - (-> {:x (range n) - :y (->> (repeatedly n #(- (rand) 0.5)) - (reductions +))} - tc/dataset))) - -;; ### 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) - -;; ### More examples - -(-> datasets/mtcars - (vis.hanami/plot ht/boxplot-chart - {:X :gear - :XTYPE :nominal - :Y :mpg})) - -(-> datasets/iris - (vis.hanami/plot ht/rule-chart - {:X :sepal-width - :Y :sepal-length - :X2 :petal-width - :Y2 :petal-length - :OPACITY 0.2 - :SIZE 3 - :COLOR "species"})) - -;; ### Grouped datasets - -;; Grouped datasets are handled automatically with a table view. - -(-> datasets/iris - (tc/group-by [:species]) - (vis.hanami/plot ht/rule-chart - {:X :sepal-width - :Y :sepal-length - :X2 :petal-width - :Y2 :petal-length - :OPACITY 0.2 - :SIZE 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"})])) - -;; Alternatively: - -(-> random-walk - (vis.hanami/combined-plot - ht/layer-chart - {:TITLE "points and a line"} - :LAYER [[ht/point-chart - {:MSIZE 400}] - [ht/line-chart - {:MSIZE 4 - :MCOLOR "brown"}]])) - -;; ### Concatenation - -;; Vertical - -(-> 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})])) - -;; Alternatively: - -(-> random-walk - (vis.hanami/combined-plot - ht/vconcat-chart - {:HEIGHT 100 - :WIDTH 100} - :VCONCAT [[ht/point-chart - {:MSIZE 400}] - [ht/line-chart - {:MSIZE 4 - :MCOLOR "brown"}]])) - -;; Horizontal - -(-> 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})])) - -;; Alternatively: -(-> random-walk - (vis.hanami/combined-plot - ht/hconcat-chart - {:HEIGHT 100 - :WIDTH 100} - :HCONCAT [[ht/point-chart - {:MSIZE 400}] - [ht/line-chart - {:MSIZE 4 - :MCOLOR "brown"}]])) - - -:bye From 3aaf86295df636ac79f30f1f15a691c4c8e40a2e Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:47:36 +0300 Subject: [PATCH 07/23] added kind-pyplot dependency --- deps.edn | 1 + 1 file changed, 1 insertion(+) diff --git a/deps.edn b/deps.edn index bb53bc4..e9372c3 100644 --- a/deps.edn +++ b/deps.edn @@ -16,6 +16,7 @@ org.tribuo/tribuo-classification-sgd {:mvn/version "4.2.0"} org.tribuo/tribuo-classification-tree {:mvn/version "4.2.0"} clj-python/libpython-clj {:mvn/version "2.025"} + org.scicloj/kind-pyplot {:mvn/version "1-beta1"} scicloj/clojisr {:mvn/version "1.0.0"} org.scicloj/tempfiles {:mvn/version "1-alpha2"}} :aliases From c0524c789c9fb78669a9f1f0959aabb5091d4a36 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:48:45 +0300 Subject: [PATCH 08/23] removed all src namespaces --- src/scicloj/noj/v1/paths.clj | 23 -------- src/scicloj/noj/v1/stats.clj | 54 ------------------ src/scicloj/noj/v1/util.clj | 8 --- src/scicloj/noj/v1/vis/hanami.clj | 94 ------------------------------- src/scicloj/noj/v1/vis/python.clj | 40 ------------- src/scicloj/noj/v1/vis/stats.clj | 64 --------------------- 6 files changed, 283 deletions(-) delete mode 100644 src/scicloj/noj/v1/paths.clj delete mode 100644 src/scicloj/noj/v1/stats.clj delete mode 100644 src/scicloj/noj/v1/util.clj delete mode 100644 src/scicloj/noj/v1/vis/hanami.clj delete mode 100644 src/scicloj/noj/v1/vis/python.clj delete mode 100644 src/scicloj/noj/v1/vis/stats.clj diff --git a/src/scicloj/noj/v1/paths.clj b/src/scicloj/noj/v1/paths.clj deleted file mode 100644 index cef2285..0000000 --- a/src/scicloj/noj/v1/paths.clj +++ /dev/null @@ -1,23 +0,0 @@ -(ns scicloj.noj.v1.paths - (:require [clojure.java.io :as io] - [clojure.string :as string])) - -(defn url? [path] - (->> path - (re-matches #"^http.*"))) - -(defn file-type [path] - (-> path - (string/split #"\.") - last)) - -(defn exists? [path] - (-> path - io/file - (.exists))) - -(defn throw-if-not-exists! [path] - (if (exists? path) - path - (throw (ex-info "File does not exist" - {:path path})))) diff --git a/src/scicloj/noj/v1/stats.clj b/src/scicloj/noj/v1/stats.clj deleted file mode 100644 index c53d1f4..0000000 --- a/src/scicloj/noj/v1/stats.clj +++ /dev/null @@ -1,54 +0,0 @@ -(ns scicloj.noj.v1.stats - (:require [fastmath.stats :as stats] - [scicloj.metamorph.ml :as mmml] - [scicloj.ml.smile.regression] - [scicloj.noj.v1.util :as util] - [tablecloth.api :as tc] - [tech.v3.dataset :as tmd] - [tech.v3.dataset.modelling :as ds-mod] - [tech.v3.datatype :as dtype] - [tech.v3.datatype.functional :as fun])) - -;; Multivariate linear regression - -(defn regression-model [dataset target features options] - (let [mmml-model (-> dataset - (tc/select-columns (cons target features)) - (ds-mod/set-inference-target target) - (mmml/train options)) - predict (fn [ds] - (-> ds - (tc/drop-columns [target]) - (mmml/predict mmml-model) - target)) - predictions (-> dataset - predict) - r (-> dataset - target - (stats/correlation predictions))] - (-> mmml-model - (assoc :explained mmml/explain - :R2 (* r r) - :predict predict - :predictions predictions)))) - -(defn linear-regression-model [dataset target features] - (regression-model dataset target features - {:model-type :smile.regression/ordinary-least-square})) - -(defn add-predictions [dataset target features options] - (let [process-fn (fn [ds] - (let [{:as model - :keys [predictions]} (-> ds - (regression-model target features options))] - (-> ds - (tc/add-column (util/concat-keywords target :prediction) - (-> predictions - (vary-meta - assoc :model (-> model - (dissoc [:model-data - :predict - :predictions]))))))))] - (if (tc/grouped? dataset) - (tc/process-group-data dataset process-fn) - (process-fn dataset)))) diff --git a/src/scicloj/noj/v1/util.clj b/src/scicloj/noj/v1/util.clj deleted file mode 100644 index 80e0eac..0000000 --- a/src/scicloj/noj/v1/util.clj +++ /dev/null @@ -1,8 +0,0 @@ -(ns scicloj.noj.v1.util - (:require [clojure.string :as str])) - -(defn concat-keywords [& kws] - (->> kws - (map name) - (str/join "-") - keyword)) diff --git a/src/scicloj/noj/v1/vis/hanami.clj b/src/scicloj/noj/v1/vis/hanami.clj deleted file mode 100644 index ab54648..0000000 --- a/src/scicloj/noj/v1/vis/hanami.clj +++ /dev/null @@ -1,94 +0,0 @@ -(ns scicloj.noj.v1.vis.hanami - (:require [aerial.hanami.common :as hc] - [aerial.hanami.templates :as ht] - [scicloj.kindly.v4.kind :as kind] - [scicloj.noj.v1.paths :as paths] - [scicloj.tempfiles.api :as tempfiles] - [tablecloth.api :as tc] - [tech.v3.dataset :as tmd])) - -(defn prepare-data [data] - (when data - (cond (string? data) (if (paths/url? data) {:UDATA data} - ;; not a url -- assuming a local path - (let [file-type (paths/file-type "csv")] - (case file-type - "csv" {:DATA (-> data - paths/throw-if-not-exists! - slurp) - :DFMT {:type file-type}} - (throw (ex-info "Unsupported file type" - {:file-type file-type}))))) - (tmd/dataset? data) {:DFMT {:type "csv"} - :DATA (let [{:keys [path _]} - (tempfiles/tempfile! ".csv")] - (-> data - (tmd/write! path)) - (-> path - slurp))} - :else {:DATA data}))) - -(defn plot [data template options] - (if (tc/grouped? data) - (-> data - (tc/aggregate {:plot (fn [group-data] - [(-> group-data - (plot template - options))])}) - (tc/rename-columns {:plot-0 :plot}) - kind/table) - (-> data - prepare-data - (merge options) - (->> (apply concat) - (apply hc/xform template)) - (assoc :usermeta - {:embedOptions {:renderer :svg}}) - kind/vega-lite))) - -(defn collector [template template-key] - (fn [common-data - options - plots] - (-> common-data - (plot template - (merge {template-key plots} - options))))) - -(def layers - (collector ht/layer-chart - :LAYER)) - -(def vconcat - (collector ht/vconcat-chart - :VCONCAT)) - -(def hconcat - (collector ht/hconcat-chart - :HCONCAT)) - -(defn combined-plot [dataset - combining-template - options - template-key - plot-specs] - (-> dataset - (plot - combining-template - (assoc options - template-key - (->> plot-specs - (map (partial - apply - (fn inner-plot - ([inner-template - inner-options] - (inner-plot nil - inner-template - inner-options)) - ([inner-dataset - inner-template - inner-options] - (plot inner-dataset - inner-template - (merge options inner-options))))))))))) diff --git a/src/scicloj/noj/v1/vis/python.clj b/src/scicloj/noj/v1/vis/python.clj deleted file mode 100644 index 750b3aa..0000000 --- a/src/scicloj/noj/v1/vis/python.clj +++ /dev/null @@ -1,40 +0,0 @@ -(ns scicloj.noj.v1.vis.python - (:require [libpython-clj2.require :refer [require-python]] - [libpython-clj2.python :refer [py. py.. py.-] :as py] - [scicloj.kindly.v4.kind :as kind] - [scicloj.tempfiles.api :as tempfiles])) - -;; inspiration: http://gigasquidsoftware.com/blog/2020/01/18/parens-for-pyplot/ - -(require-python 'matplotlib.pyplot - 'matplotlib.backends.backend_agg - 'numpy) - -(defmacro with-pyplot - "Takes forms with mathplotlib.pyplot and returns a showable (SVG) plot." - [& body] - `(let [_# (matplotlib.pyplot/clf) - fig# (matplotlib.pyplot/figure) - agg-canvas# (matplotlib.backends.backend_agg/FigureCanvasAgg fig#) - path# (:path (tempfiles/tempfile! ".svg"))] - ~(cons 'do body) - (py. agg-canvas# "draw") - (matplotlib.pyplot/savefig path#) - (-> path# - slurp - kind/html))) - - -(defn pyplot - "Takes a function plotting using mathplotlib.pyplot, and returns a showable (SVG) plot" - [plotting-function] - (let [_ (matplotlib.pyplot/clf) - fig (matplotlib.pyplot/figure) - agg-canvas (matplotlib.backends.backend_agg/FigureCanvasAgg fig) - path (:path (tempfiles/tempfile! ".svg"))] - (plotting-function) - (py. agg-canvas "draw") - (matplotlib.pyplot/savefig path) - (-> path - slurp - kind/html))) diff --git a/src/scicloj/noj/v1/vis/stats.clj b/src/scicloj/noj/v1/vis/stats.clj deleted file mode 100644 index 20225a5..0000000 --- a/src/scicloj/noj/v1/vis/stats.clj +++ /dev/null @@ -1,64 +0,0 @@ -(ns scicloj.noj.v1.vis.stats - (:require [aerial.hanami.templates :as ht] - [scicloj.kindly.v4.kind :as kind] - [fastmath.stats] - [scicloj.noj.v1.stats :as stats] - [scicloj.noj.v1.vis.hanami :as hanami] - [tablecloth.api :as tc])) - -(defn histogram [dataset column-name {:keys [nbins]}] - (let [{:keys [bins max step]} (-> column-name - dataset - (fastmath.stats/histogram nbins)) - left (map first bins)] - (-> {:left (map first bins) - :right (concat (rest left) - [max]) - :count (map second bins)} - tc/dataset - (hanami/plot ht/bar-chart - {:X :left - :X2 :right - :Y :count}) - (assoc-in [:encoding :x :bin] {:binned true - :step step}) - (assoc-in [:encoding :x :title] column-name)))) - - -(defn linear-regression-plot [dataset target-column feature-column - {:as options - :keys [point-options - line-options]}] - (let [ds-with-predictions - (-> dataset - (stats/add-predictions target-column [feature-column] - {:model-type :smile.regression/ordinary-least-square})) - prediction-column-name (keyword - (str (name target-column) - "-prediction")) - process-fn (fn [ds] - (-> ds - (hanami/combined-plot - ht/layer-chart - (merge {:X feature-column - :TITLE (format "R^2 = %.3f" - (-> ds - prediction-column-name - meta - :model - :R2))} - options) - :LAYER [[ht/point-chart - (merge {:Y target-column} - point-options)] - [ht/line-chart - (merge {:Y prediction-column-name - :YTITLE target-column} - line-options)]])))] - (if (tc/grouped? ds-with-predictions) - (-> ds-with-predictions - (tc/aggregate {:plot (fn [group-data] - [(process-fn group-data)])}) - (tc/rename-columns {:plot-0 :plot}) - kind/table) - (process-fn ds-with-predictions)))) From 33786a6abace526cf1c834fcfba359947adbdf01 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:49:49 +0300 Subject: [PATCH 09/23] deps update --- deps.edn | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index e9372c3..587491b 100644 --- a/deps.edn +++ b/deps.edn @@ -1,14 +1,13 @@ {:paths ["src" "resources"] :deps {org.clojure/clojure {:mvn/version "1.11.1"} scicloj/tablecloth {:mvn/version "7.029.2"} - org.scicloj/kindly {:mvn/version "4-beta5"} + org.scicloj/kindly {:mvn/version "4-beta6"} generateme/fastmath {:mvn/version "3.0.0-SNAPSHOT"} aerial.hanami/aerial.hanami {:mvn/version "0.20.0"} - org.scicloj/hanamicloth {:mvn/version "1-alpha4-SNAPSHOT"} + org.scicloj/hanamicloth {:mvn/version "1-alpha5-SNAPSHOT"} cljplot/cljplot {:mvn/version "0.0.3"} scicloj/metamorph.ml {:git/url "https://github.com/scicloj/metamorph.ml" :git/sha "bb151eec256f0e4443cdb4f39be8609f6078a4ad"} - ;; scicloj/scicloj.ml.smile {:mvn/version "7.2"} scicloj/scicloj.ml.tribuo {:git/url "https://github.com/scicloj/scicloj.ml.tribuo" :git/sha "f4ebf1e1bb78eb99dd35ca886d75b9f65d800e8d"} org.tribuo/tribuo-regression-sgd {:mvn/version "4.2.0"} @@ -17,8 +16,7 @@ org.tribuo/tribuo-classification-tree {:mvn/version "4.2.0"} clj-python/libpython-clj {:mvn/version "2.025"} org.scicloj/kind-pyplot {:mvn/version "1-beta1"} - scicloj/clojisr {:mvn/version "1.0.0"} - org.scicloj/tempfiles {:mvn/version "1-alpha2"}} + scicloj/clojisr {:mvn/version "1.0.0"}} :aliases {:build {:deps {io.github.clojure/tools.build {:mvn/version "0.9.6"} slipset/deps-deploy {:mvn/version "0.2.1"}} From 543d648394f1a5bd0fae1f980cab7432592cdad0 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:54:12 +0300 Subject: [PATCH 10/23] reordered chapters --- notebooks/chapters.edn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebooks/chapters.edn b/notebooks/chapters.edn index 5f4e5b2..f34996b 100644 --- a/notebooks/chapters.edn +++ b/notebooks/chapters.edn @@ -1,5 +1,5 @@ -["prepare_for_ml" - "ml_basic" +["ml_basic" + "prepare_for_ml" "automl" "interactions_ols" "datasets" From 7ae2ef0cb7b8076fb61ca64f8a8e9ce6b5f28ce2 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:56:34 +0300 Subject: [PATCH 11/23] logo --- notebooks/corner.png | Bin 0 -> 32951 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 notebooks/corner.png diff --git a/notebooks/corner.png b/notebooks/corner.png new file mode 100644 index 0000000000000000000000000000000000000000..96caa2d3ce1d99a81707ca62e73039b548d5cf0a GIT binary patch literal 32951 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 literal 0 HcmV?d00001 From d8bb0305ee81e7a19dded59056d0d213bd1a2d3a Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:58:01 +0300 Subject: [PATCH 12/23] logo --- notebooks/index.clj | 7 +++++++ notebooks/{corner.png => logo.png} | Bin 2 files changed, 7 insertions(+) rename notebooks/{corner.png => logo.png} (100%) diff --git a/notebooks/index.clj b/notebooks/index.clj index 044ba68..ac0e35c 100644 --- a/notebooks/index.clj +++ b/notebooks/index.clj @@ -1,5 +1,12 @@ ;; # Preface +^{:kindly/hide-code true + :kindly/kind :kind/hiccup} +[:img + {:style {:width "100px"} + :src "notebooks/logo.png" + :alt "Noj logo"}] + ^:kindly/hide-code (ns index (:require [scicloj.kindly.v4.api :as kindly] diff --git a/notebooks/corner.png b/notebooks/logo.png similarity index 100% rename from notebooks/corner.png rename to notebooks/logo.png From 3791a019cd4cee8c6495493dc8056c3b4e3c8f0a Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:58:33 +0300 Subject: [PATCH 13/23] README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 520e4e1..8cc3be2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # scinojure (noj) +![Clay logo](notebooks/Noj.png) + The Noj library collects a few of the relevant Clojure data & science with additional documentation and convenience layers composing them together. [![Clojars Project](https://img.shields.io/clojars/v/org.scicloj/noj.svg)](https://clojars.org/org.scicloj/noj) From 5bb102951c08f484944f93dabe32231470931906 Mon Sep 17 00:00:00 2001 From: daslu Date: Tue, 13 Aug 2024 23:59:22 +0300 Subject: [PATCH 14/23] book cleanup --- notebooks/chapters.edn | 3 +-- notebooks/noj_book/known_issues.clj | 12 ------------ 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 notebooks/noj_book/known_issues.clj diff --git a/notebooks/chapters.edn b/notebooks/chapters.edn index f34996b..73ec267 100644 --- a/notebooks/chapters.edn +++ b/notebooks/chapters.edn @@ -3,5 +3,4 @@ "automl" "interactions_ols" "datasets" - "visualizing_correlation_matrices" - "known_issues"] + "visualizing_correlation_matrices"] diff --git a/notebooks/noj_book/known_issues.clj b/notebooks/noj_book/known_issues.clj deleted file mode 100644 index e70eb92..0000000 --- a/notebooks/noj_book/known_issues.clj +++ /dev/null @@ -1,12 +0,0 @@ -;; # Known Issues ❗ -^:kindly/hide-code -(ns noj-book.known-issues) - -;; ## `clj` fails with "mkl" error - -;; If you encounter: -;; _Error building classpath. Could not acquire write lock for 'artifact:org.bytedeco:mkl'_ - -;; Try: `clj -P -Sthreads 1` - -;; Explanation: see [deps issue report](https://clojurians-log.clojureverse.org/tools-deps/2021-09-16). From dac62572c47a5ff39f4eacf2d9f38fa2f5b0938e Mon Sep 17 00:00:00 2001 From: daslu Date: Wed, 14 Aug 2024 00:36:50 +0300 Subject: [PATCH 15/23] CHANGELOG --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f041b22..d68ab5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). -## [1-alpha35] - unreleased -- avoding the unnecessary use of Reagent for Python plots -- updated deps +## [2-alpha1-SNAPSHOT] - unreleased +- updating the set of depndencies: Fastmath 3-SNAPSHOT, the relevant metamorph.ml branch, kind-pyplot, hanamicloth, etc. +- removing all Noj namespaces ## [1-alpha34] - 2024-04-15 - removed the currently-unnecessary `calc-correlations-matrix` and `round` functions (#22) - thanks, @behrica From 8768830a10c458d162384fd3e93473cef01f0148 Mon Sep 17 00:00:00 2001 From: daslu Date: Sat, 17 Aug 2024 23:54:43 +0300 Subject: [PATCH 16/23] rerender doc --- docs/index.html | 61 +- docs/noj_book.automl.html | 593 +- docs/noj_book.datasets.html | 52 +- docs/noj_book.interactions_ols.html | 230 +- docs/noj_book.known_issues.html | 679 -- .../md-default0.js | 2 - .../md-default1.js | 6 - docs/noj_book.ml_basic.html | 147 +- docs/noj_book.more_visualization.html | 854 --- docs/noj_book.more_visualization_files/0.csv | 16 - docs/noj_book.more_visualization_files/1.csv | 13 - docs/noj_book.more_visualization_files/2.csv | 6 - docs/noj_book.more_visualization_files/3.csv | 33 - .../md-default4.js | 2 - .../md-default5.js | 6 - .../vega6.js | 2 - .../vega7.js | 2 - .../vega8.js | 7 - docs/noj_book.prepare_for_ml.html | 149 +- docs/noj_book.python.html | 6393 ----------------- docs/noj_book.python_files/md-default0.js | 2 - docs/noj_book.python_files/md-default1.js | 6 - docs/noj_book.statistical_visualization.html | 861 --- .../0.csv | 33 - .../1.csv | 13 - .../2.csv | 16 - .../3.csv | 6 - .../4.csv | 11 - .../md-default5.js | 2 - .../md-default6.js | 6 - .../vega7.js | 2 - .../vega8.js | 2 - .../vega9.js | 7 - docs/noj_book.statistics.html | 1060 --- docs/noj_book.statistics_files/md-default0.js | 2 - docs/noj_book.statistics_files/md-default1.js | 6 - docs/noj_book.visualization.html | 950 --- docs/noj_book.visualization_files/0.csv | 21 - docs/noj_book.visualization_files/1.csv | 33 - docs/noj_book.visualization_files/2.csv | 151 - docs/noj_book.visualization_files/3.csv | 51 - docs/noj_book.visualization_files/4.csv | 51 - docs/noj_book.visualization_files/5.csv | 51 - .../md-default6.js | 2 - .../md-default7.js | 6 - docs/noj_book.visualization_files/vega10.js | 7 - docs/noj_book.visualization_files/vega8.js | 2 - docs/noj_book.visualization_files/vega9.js | 2 - ...book.visualizing_correlation_matrices.html | 94 +- docs/notebooks/chapters.edn | 12 +- docs/notebooks/logo.png | Bin 0 -> 32951 bytes docs/search.json | 340 +- 52 files changed, 633 insertions(+), 12428 deletions(-) delete mode 100644 docs/noj_book.known_issues.html delete mode 100644 docs/noj_book.known_issues_files/md-default0.js delete mode 100644 docs/noj_book.known_issues_files/md-default1.js delete mode 100644 docs/noj_book.more_visualization.html delete mode 100644 docs/noj_book.more_visualization_files/0.csv delete mode 100644 docs/noj_book.more_visualization_files/1.csv delete mode 100644 docs/noj_book.more_visualization_files/2.csv delete mode 100644 docs/noj_book.more_visualization_files/3.csv delete mode 100644 docs/noj_book.more_visualization_files/md-default4.js delete mode 100644 docs/noj_book.more_visualization_files/md-default5.js delete mode 100644 docs/noj_book.more_visualization_files/vega6.js delete mode 100644 docs/noj_book.more_visualization_files/vega7.js delete mode 100644 docs/noj_book.more_visualization_files/vega8.js delete mode 100644 docs/noj_book.python.html delete mode 100644 docs/noj_book.python_files/md-default0.js delete mode 100644 docs/noj_book.python_files/md-default1.js delete mode 100644 docs/noj_book.statistical_visualization.html delete mode 100644 docs/noj_book.statistical_visualization_files/0.csv delete mode 100644 docs/noj_book.statistical_visualization_files/1.csv delete mode 100644 docs/noj_book.statistical_visualization_files/2.csv delete mode 100644 docs/noj_book.statistical_visualization_files/3.csv delete mode 100644 docs/noj_book.statistical_visualization_files/4.csv delete mode 100644 docs/noj_book.statistical_visualization_files/md-default5.js delete mode 100644 docs/noj_book.statistical_visualization_files/md-default6.js delete mode 100644 docs/noj_book.statistical_visualization_files/vega7.js delete mode 100644 docs/noj_book.statistical_visualization_files/vega8.js delete mode 100644 docs/noj_book.statistical_visualization_files/vega9.js delete mode 100644 docs/noj_book.statistics.html delete mode 100644 docs/noj_book.statistics_files/md-default0.js delete mode 100644 docs/noj_book.statistics_files/md-default1.js delete mode 100644 docs/noj_book.visualization.html delete mode 100644 docs/noj_book.visualization_files/0.csv delete mode 100644 docs/noj_book.visualization_files/1.csv delete mode 100644 docs/noj_book.visualization_files/2.csv delete mode 100644 docs/noj_book.visualization_files/3.csv delete mode 100644 docs/noj_book.visualization_files/4.csv delete mode 100644 docs/noj_book.visualization_files/5.csv delete mode 100644 docs/noj_book.visualization_files/md-default6.js delete mode 100644 docs/noj_book.visualization_files/md-default7.js delete mode 100644 docs/noj_book.visualization_files/vega10.js delete mode 100644 docs/noj_book.visualization_files/vega8.js delete mode 100644 docs/noj_book.visualization_files/vega9.js create mode 100644 docs/notebooks/logo.png diff --git a/docs/index.html b/docs/index.html index c144133..2d374b6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -30,7 +30,7 @@ - + @@ -114,14 +114,14 @@ - - - - - - @@ -236,6 +200,7 @@

Noj Documentation

1 Preface

+Noj logo

Noj (scinojure) is an opinionated way to use the emerging Clojure data stack.

It collects a few of the main dependencies together with functions allowing to conveniently use them together.

Source: (GitHub repo)

@@ -248,18 +213,12 @@

1.2 Existing chapters in this book:

@@ -693,8 +652,8 @@

diff --git a/docs/noj_book.automl.html b/docs/noj_book.automl.html index 6e50bf4..ea2d591 100644 --- a/docs/noj_book.automl.html +++ b/docs/noj_book.automl.html @@ -65,7 +65,7 @@ - + @@ -149,14 +149,14 @@ - - - - - - @@ -274,7 +238,8 @@

4 
(ns noj-book.automl
-  (:require [noj-book.ml-basic :as ml-basic]))
+ (:require [noj-book.ml-basic :as ml-basic] + [scicloj.kindly.v4.kind :as kind]))

4.1 The metamorph pipeline abstraction

@@ -356,14 +321,14 @@

0.0 3.0 -1.0 0.0 +1.0 +1.0 +1.0 0.0 -2.0 -0.0 -0.0 +1.0 1.0 @@ -372,46 +337,46 @@

1.0 -1.0 +0.0 2.0 0.0 -1.0 +0.0 -1.0 -2.0 0.0 -1.0 - - -1.0 3.0 0.0 0.0 - -1.0 -1.0 + +0.0 +3.0 2.0 1.0 + +0.0 +3.0 +0.0 +0.0 + 1.0 -2.0 +1.0 0.0 1.0 0.0 1.0 -2.0 0.0 +1.0 0.0 3.0 0.0 -0.0 +1.0 ... @@ -426,61 +391,61 @@

1.0 -0.0 -2.0 +1.0 +3.0 0.0 0.0 -0.0 -2.0 +1.0 +3.0 0.0 0.0 0.0 3.0 -2.0 +0.0 0.0 -0.0 -3.0 1.0 +3.0 +0.0 0.0 0.0 -1.0 -2.0 -1.0 +3.0 +0.0 +0.0 0.0 -1.0 +2.0 2.0 0.0 -0.0 -3.0 -0.0 -0.0 +1.0 +1.0 +2.0 +1.0 0.0 3.0 -0.0 +1.0 0.0 -1.0 +0.0 3.0 0.0 -1.0 +0.0 -1.0 +0.0 3.0 0.0 0.0 @@ -492,14 +457,14 @@

:metamorph/mode :fit
#uuid "21f19f19-0095-4c26-8174-66709862be80" {:model-data {:majority-class 1.0, :distinct-labels (0.0 1.0)}, :options {:model-type :metamorph.ml/dummy-classifier}, :id #uuid "785a6215-bb75-4486-a361-12a76f6050b1", :feature-columns [:sex :pclass :embarked], :target-columns [:survived], :target-categorical-maps {:survived #tech.v3.dataset.categorical.CategoricalMap{:lookup-table {"no" 0, "yes" 1}, :src-column :survived, :result-datatype :float64}}, :scicloj.metamorph.ml/unsupervised? nil}

}

+
:metamorph/mode :fit
#uuid "b567f0ac-96ea-41e2-a9cc-29348dcea334" {:model-data {:majority-class 0.0, :distinct-labels (1.0 0.0)}, :options {:model-type :metamorph.ml/dummy-classifier}, :id #uuid "15e2e99d-ccaf-483c-942e-67ec83e42a80", :feature-columns [:sex :pclass :embarked], :target-columns [:survived], :target-categorical-maps {:survived #tech.v3.dataset.categorical.CategoricalMap{:lookup-table {"no" 0, "yes" 1}, :src-column :survived, :result-datatype :float64}}, :scicloj.metamorph.ml/unsupervised? nil}

}

(keys ctx-after-train)
(:metamorph/data
  :metamorph/mode
- #uuid "21f19f19-0095-4c26-8174-66709862be80")
+ #uuid "b567f0ac-96ea-41e2-a9cc-29348dcea334")

This context map has the “data”, the “mode” and an UUID for each operation (we had only one in this pipeline)

@@ -519,14 +484,14 @@

0.0 3.0 -1.0 0.0 +1.0 +1.0 +1.0 0.0 -2.0 -0.0 -0.0 +1.0 1.0 @@ -535,46 +500,46 @@

1.0 -1.0 +0.0 2.0 0.0 -1.0 +0.0 -1.0 -2.0 0.0 -1.0 - - -1.0 3.0 0.0 0.0 - -1.0 -1.0 + +0.0 +3.0 2.0 1.0 + +0.0 +3.0 +0.0 +0.0 + 1.0 -2.0 +1.0 0.0 1.0 0.0 1.0 -2.0 0.0 +1.0 0.0 3.0 0.0 -0.0 +1.0 ... @@ -589,61 +554,61 @@

1.0 -0.0 -2.0 +1.0 +3.0 0.0 0.0 -0.0 -2.0 +1.0 +3.0 0.0 0.0 0.0 3.0 -2.0 +0.0 0.0 -0.0 -3.0 1.0 +3.0 +0.0 0.0 0.0 -1.0 -2.0 -1.0 +3.0 +0.0 +0.0 0.0 -1.0 +2.0 2.0 0.0 -0.0 -3.0 -0.0 -0.0 +1.0 +1.0 +2.0 +1.0 0.0 3.0 -0.0 +1.0 0.0 -1.0 +0.0 3.0 0.0 -1.0 +0.0 -1.0 +0.0 3.0 0.0 0.0 @@ -651,9 +616,9 @@

:fit -

{:model-data {:majority-class 1.0, :distinct-labels (0.0 1.0)},
+
{:model-data {:majority-class 0.0, :distinct-labels (1.0 0.0)},
  :options {:model-type :metamorph.ml/dummy-classifier},
- :id #uuid "785a6215-bb75-4486-a361-12a76f6050b1",
+ :id #uuid "15e2e99d-ccaf-483c-942e-67ec83e42a80",
  :feature-columns [:sex :pclass :embarked],
  :target-columns [:survived],
  :target-categorical-maps
@@ -696,70 +661,70 @@ 

-1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 ... -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 -1.0 +0.0 @@ -777,7 +742,7 @@

-
#uuid "21f19f19-0095-4c26-8174-66709862be80"
+
#uuid "b567f0ac-96ea-41e2-a9cc-29348dcea334"
@@ -819,53 +784,53 @@

1.0 -3.0 +2.0 0.0 -1.0 -3.0 -1.0 +0.0 +2.0 +0.0 -1.0 -1.0 +0.0 +3.0 0.0 0.0 1.0 -2.0 +0.0 -1.0 +0.0 2.0 0.0 -0.0 +1.0 3.0 -0.0 +2.0 -0.0 +1.0 3.0 1.0 1.0 -3.0 +2.0 0.0 -0.0 -3.0 +1.0 +2.0 0.0 -0.0 1.0 -2.0 +1.0 +0.0 ... @@ -873,18 +838,28 @@

... +1.0 +3.0 +2.0 + + 0.0 2.0 0.0 + +0.0 +3.0 +0.0 + -1.0 +0.0 3.0 0.0 -1.0 -3.0 +0.0 +2.0 0.0 @@ -898,34 +873,24 @@

2.0 -1.0 -1.0 -2.0 - - 0.0 3.0 0.0 - + 0.0 3.0 0.0 - + 0.0 3.0 0.0 - -1.0 + 1.0 2.0 - - 0.0 -3.0 -1.0 @@ -935,10 +900,10 @@

:model-data {:majority-class 1.0, :distinct-labels (0.0 1.0)}

+
:model-data {:majority-class 0.0, :distinct-labels (1.0 0.0)}
-
:id #uuid "785a6215-bb75-4486-a361-12a76f6050b1"
+
:id #uuid "15e2e99d-ccaf-483c-942e-67ec83e42a80"
@@ -961,28 +926,28 @@

-

+ - + - + - + - + - + @@ -994,7 +959,7 @@

...

- + @@ -1006,13 +971,13 @@

0.0

- + - + @@ -1021,10 +986,10 @@

0.0

- + - +
0.01.0
0.0
1.00.0
0.01.0
0.0
0.01.0
0.01.0
0.01.0
1.0
0.01.0
0.0
1.00.0
1.0
0.01.0
0.0
1.00.0
0.01.0
@@ -1052,7 +1017,7 @@

#tech.v3.dataset.column<float64>[178]
 :survived
-[1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000...]
+[0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000...]

This works as long as all operations of the pipeline follow the metamorph convention (we can create such compliant functions, out of normal dataset->dataset functions, as we will see)

my-pipeline represents therefore a not yet executed model training / prediction. It can be freely moved around and applied to a dataset when needed.

@@ -1235,7 +1200,7 @@

:metamorph/mode :fit
#uuid "2eb2b706-dddd-4765-8d18-3711729cd46d" {:model-data {:majority-class 1, :distinct-labels (0.0 1.0)}, :options {:model-type :metamorph.ml/dummy-classifier}, :id #uuid "923f24e5-8d39-4ae3-9297-0c02ba4e8322", :feature-columns [:sex :pclass :embarked], :target-columns [:survived], :target-categorical-maps {:survived #tech.v3.dataset.categorical.CategoricalMap{:lookup-table {"no" 0, "yes" 1}, :src-column :survived, :result-datatype :float64}}, :scicloj.metamorph.ml/unsupervised? nil}

}

+
:metamorph/mode :fit
#uuid "4c15287e-e6c4-4450-9edb-aa29889ed8c9" {:model-data {:majority-class 1, :distinct-labels (0.0 1.0)}, :options {:model-type :metamorph.ml/dummy-classifier}, :id #uuid "f98d1574-03b5-4425-8b47-148de6e26488", :feature-columns [:sex :pclass :embarked], :target-columns [:survived], :target-categorical-maps {:survived #tech.v3.dataset.categorical.CategoricalMap{:lookup-table {"no" 0, "yes" 1}, :src-column :survived, :result-datatype :float64}}, :scicloj.metamorph.ml/unsupervised? nil}

}

To show the power of pipelines, I start with doing the simplest possible pipeline, and expand then on it.

we can already chain train and test with usual functions:

@@ -1247,7 +1212,7 @@

#tech.v3.dataset.column<float64>[178]
 :survived
-[1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000...]
+[0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000...]

the same with pipelines

@@ -1263,7 +1228,7 @@

#tech.v3.dataset.column<float64>[178]
 :survived
-[1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000...]
+[0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000...]

@@ -1362,17 +1327,17 @@

(def models [{:model-type :metamorph.ml/dummy-classifier}
-             {:model-type :smile.classification/random-forest}
-             {:model-type :smile.classification/logistic-regression}
-             {:model-type :smile.classification/decision-tree}
-             {:model-type :smile.classification/ada-boost}
+             {:model-type :scicloj.ml.tribuo/classification
+              :tribuo-components [{:name "logistic"
+                                   :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}]
+              :tribuo-trainer-name "logistic"}
              {:model-type :scicloj.ml.tribuo/classification
-              :tribuo-components [{:name "trainer"
+              :tribuo-components [{:name "random-forest"
                                    :type "org.tribuo.classification.dtree.CARTClassificationTrainer"
                                    :properties {:maxDepth "8"
                                                 :useRandomSplitPoints "false"
                                                 :fractionFeaturesInSplit "0.5"}}]
-              :tribuo-trainer-name "trainer"}])
+ :tribuo-trainer-name "random-forest"}])

This uses models from Smile and Tribuo, but could be any metamorph.ml compliant model ( library sklearn-clj wraps all python sklearn models, for example)

The list of feature combinations to try for each model:

@@ -1434,7 +1399,7 @@

-[{:name trainer, +[{:name random-forest, @@ -1464,7 +1429,7 @@

-:tribuo-trainer-name trainer} +:tribuo-trainer-name random-forest} @@ -1486,147 +1451,117 @@

(->  evaluation-results-all flatten count)
-
180
+
90

We can find the best as well by hand, it’s the first from the list, when sorted by accuracy.

(-> (make-results-ds evaluation-results-all)
     (tc/unique-by)
     (tc/order-by [:mean-accuracy] :desc)
-    (tc/head))
+ (tc/head) + (kind/table))
-
-

_unnamed [5 3]:

- +
+
+
---+++ - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - + + +
:used-features:mean-accuracy:optionsused-featuresmean-accuracyoptions
[:sex :pclass :embarked]0.81107726{:model-type :scicloj.ml.tribuo/classification,
:tribuo-components
[{:name trainer,
:type org.tribuo.classification.dtree.CARTClassificationTrainer,
:properties
{:maxDepth 8,
:useRandomSplitPoints false,
:fractionFeaturesInSplit 0.5}}],
:tribuo-trainer-name trainer}
[:sex :pclass :embarked]0.81107726{:model-type :smile.classification/ada-boost}
[:sex :pclass]0.78633911{:model-type :smile.classification/random-forest}
[:sex]0.78633276{:model-type :scicloj.ml.tribuo/classification,
:tribuo-components
[{:name trainer,
:type org.tribuo.classification.dtree.CARTClassificationTrainer,
:properties
{:maxDepth 8,
+
[:sex :pclass :embarked]
+
0.8110772551260077
+
{:model-type :scicloj.ml.tribuo/classification,
+ :tribuo-components
+ [{:name "random-forest",
+   :type "org.tribuo.classification.dtree.CARTClassificationTrainer",
+   :properties
+   {:maxDepth "8",
+    :useRandomSplitPoints "false",
+    :fractionFeaturesInSplit "0.5"}}],
+ :tribuo-trainer-name "random-forest"}
+
:useRandomSplitPoints false,
+
[:sex]
+
0.7863327620135847
+
{:model-type :scicloj.ml.tribuo/classification,
+ :tribuo-components
+ [{:name "random-forest",
+   :type "org.tribuo.classification.dtree.CARTClassificationTrainer",
+   :properties
+   {:maxDepth "8",
+    :useRandomSplitPoints "false",
+    :fractionFeaturesInSplit "0.5"}}],
+ :tribuo-trainer-name "random-forest"}
+
:fractionFeaturesInSplit 0.5}}],
+
[:sex :pclass]
+
0.7863327620135847
+
{:model-type :scicloj.ml.tribuo/classification,
+ :tribuo-components
+ [{:name "logistic",
+   :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}],
+ :tribuo-trainer-name "logistic"}
+
:tribuo-trainer-name trainer}
+
[:sex :embarked]
+
0.7863327620135847
+
{:model-type :scicloj.ml.tribuo/classification,
+ :tribuo-components
+ [{:name "logistic",
+   :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}],
+ :tribuo-trainer-name "logistic"}
+
[:sex]0.78633276{:model-type :smile.classification/ada-boost}
+
[:sex]
+
0.7863327620135847
+
{:model-type :scicloj.ml.tribuo/classification,
+ :tribuo-components
+ [{:name "logistic",
+   :type "org.tribuo.classification.sgd.linear.LinearSGDTrainer"}],
+ :tribuo-trainer-name "logistic"}
+
+

4.6 Best practices for data transformation steps in or outside pipeline

-
(require '[scicloj.metamorph.ml.toydata :as data]
-         '[tech.v3.dataset.modelling :as ds-mod]
-         '[tech.v3.dataset.categorical :as ds-cat]
-         '[tech.v3.dataset :as ds])
+
(require '[scicloj.metamorph.ml.toydata :as data]
+         '[tech.v3.dataset.modelling :as ds-mod]
+         '[tech.v3.dataset.categorical :as ds-cat]
+         '[tech.v3.dataset :as ds])

We have seen that we have two ways to transform the input data, outside the pipeline and inside the pipeline.

These are the total steps from raw data to “into the model” for the titanic use case.

@@ -1634,33 +1569,33 @@

-
(def titanic
-  (:train
-   (data/titanic-ds-split)))
+
(def titanic
+  (:train
+   (data/titanic-ds-split)))
  1. first transformation, no metamorph pipeline
-
(def relevant-titanic-data
-  (-> titanic
-      (tc/select-columns (conj ml-basic/categorical-feature-columns :survived))
-      (tc/drop-missing)
-      (ds/categorical->number [:sex :pclass :embarked] [0 1 2 "male" "female" "S" "Q" "C"] :float64)
-      (ds/categorical->number [:survived] [0 1] :float64)
-      (ds-mod/set-inference-target :survived)))
+
(def relevant-titanic-data
+  (-> titanic
+      (tc/select-columns (conj ml-basic/categorical-feature-columns :survived))
+      (tc/drop-missing)
+      (ds/categorical->number [:sex :pclass :embarked] [0 1 2 "male" "female" "S" "Q" "C"] :float64)
+      (ds/categorical->number [:survived] [0 1] :float64)
+      (ds-mod/set-inference-target :survived)))
  1. transform via pipelines
-
(defn make-pipe-fn [model-type features]
-  (mm/pipeline
-   ;; store the used features in ctx, so we can retrieve them at the end
-   (fn [ctx]
-     (assoc ctx :used-features features))
-   (mm/lift tc/select-columns (conj features :survived))
-   {:metamorph/id :model} (ml/model {:model-type model-type})))
+
(defn make-pipe-fn [model-type features]
+  (mm/pipeline
+   ;; store the used features in ctx, so we can retrieve them at the end
+   (fn [ctx]
+     (assoc ctx :used-features features))
+   (mm/lift tc/select-columns (conj features :survived))
+   {:metamorph/id :model} (ml/model {:model-type model-type})))

While it would be technically possible to move all steps from the “first transformation” into the pipeline, by just using the “lifted” form of the transformations, I would not do so, even though this should give the same result.

I think it is better to separate the steps which are “fixed”, from the steps which are parameterized, so for which we want to find the best values by “trying out”.

@@ -2094,8 +2029,8 @@

- - - - - - - @@ -1265,8 +1229,8 @@

6  - - 7  Python (experimental 🛠) + + 7  Visualizing correlation matrices (experimental 🛠) - DRAFT diff --git a/docs/noj_book.interactions_ols.html b/docs/noj_book.interactions_ols.html index ca7bef4..8a0426b 100644 --- a/docs/noj_book.interactions_ols.html +++ b/docs/noj_book.interactions_ols.html @@ -178,14 +178,14 @@ - - - - - - @@ -304,11 +268,12 @@

5  [scicloj.metamorph.core :as mm] [scicloj.metamorph.ml :as ml] [scicloj.metamorph.ml.loss :as loss] - [tablecloth.api :as tc] - [tablecloth.column.api :as tcc] - [tablecloth.pipeline :as tcpipe] - [tech.v3.dataset.modelling :as modelling] - [scicloj.ml.smile.regression])) + [scicloj.metamorph.ml.regression] + [tablecloth.api :as tc] + [tablecloth.column.api :as tcc] + [tablecloth.pipeline :as tcpipe] + [tech.v3.dataset.modelling :as modelling] + [scicloj.ml.tribuo]))

This examples shows how to do interactions in linear regression with metamorph.ml.

Taking ideas from: Interaction Effect in Multiple Regression: Essentials by Alboukadel Kassambara

@@ -329,44 +294,66 @@

5  5.1 Additive model

First we build an additive model, which model equation is \[sales = b0 + b1 * youtube + b2 * facebook\]

-
(def additive-pipeline
-  (mm/pipeline
-   {:metamorph/id :model}
-   (ml/model {:model-type :smile.regression/ordinary-least-square})))
+
(def linear-model-config {:model-type :fastmath/ols})
+
+
+
(def additive-pipeline
+  (mm/pipeline
+   {:metamorph/id :model}
+   (ml/model linear-model-config)))

We evaluate it,

-
(def evaluations
-  (ml/evaluate-pipelines
-   [additive-pipeline]
-   (tc/split->seq preprocessed-data :holdout)
-   loss/rmse
-   :loss
-   {:other-metrices [{:name :r2
-                      :metric-fn fmstats/r2-determination}]}))
+
(def evaluations
+  (ml/evaluate-pipelines
+   [additive-pipeline]
+   (tc/split->seq preprocessed-data :holdout)
+   loss/rmse
+   :loss
+   {:other-metrices [{:name :r2
+                      :metric-fn fmstats/r2-determination}]}))
-

and print the result:

+

and print the resulting model: (note that the :sales term means the intercept b0)

+

(note that )

-
(-> evaluations flatten first :fit-ctx :model ml/thaw-model)
+
(-> evaluations flatten first :fit-ctx :model ml/tidy)
-
-
Linear Model:
-
-Residuals:
-       Min          1Q      Median          3Q         Max
-   -5.4674     -0.9787      0.2357      1.1127      2.7604
-
-Coefficients:
-                  Estimate Std. Error    t value   Pr(>|t|)
-Intercept           3.6987     0.3814     9.6974     0.0000 ***
-youtube             0.0436     0.0015    29.3653     0.0000 ***
-facebook            0.1988     0.0083    23.8933     0.0000 ***
----------------------------------------------------------------------
-Significance codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 1.7036 on 130 degrees of freedom
-Multiple R-squared: 0.9203,    Adjusted R-squared: 0.9191
-F-statistic: 750.8533 on 3 and 130 DF,  p-value: 3.842e-72
+
+

_unnamed [3 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:term:statistic:estimate:p.value:std.error
:sales7.717698053.643020322.76045853E-120.47203457
:youtube23.751302840.045211570.00000000E+000.00190354
:facebook17.860192360.184504920.00000000E+000.01033051

We have the following metrics:

\(RMSE\)

@@ -374,14 +361,14 @@

(-> evaluations flatten first :test-transform :metric)

-
2.5729902897341193
+
1.7581012538303793

\(R^2\)

(-> evaluations flatten first :test-transform :other-metrices first :metric)
-
0.8535450866841606
+
0.9344041363388802

@@ -391,7 +378,7 @@

(def pipe-interaction
   (mm/pipeline
    (tcpipe/add-column :youtube*facebook (fn [ds] (tcc/* (ds :youtube) (ds :facebook))))
-   {:metamorph/id :model}(ml/model {:model-type :smile.regression/ordinary-least-square})))
+ {:metamorph/id :model} (ml/model linear-model-config)))

Again we evaluate the model,

@@ -404,44 +391,75 @@

{:other-metrices [{:name :r2 :metric-fn fmstats/r2-determination}]}))

-

and print it and the performance metrices:

+

and print it and the performance metrics:

-
(-> evaluations flatten first :fit-ctx :model ml/thaw-model)
+
(-> evaluations flatten first :fit-ctx :model ml/tidy)
-
-
Linear Model:
-
-Residuals:
-       Min          1Q      Median          3Q         Max
-   -7.8412     -0.3875      0.1922      0.6872      1.7406
-
-Coefficients:
-                  Estimate Std. Error    t value   Pr(>|t|)
-Intercept           8.2403     0.3909    21.0826     0.0000 ***
-youtube             0.0186     0.0020     9.2254     0.0000 ***
-facebook            0.0309     0.0112     2.7531     0.0068 **
-youtube*facebook     0.0009     0.0001    15.8956     0.0000 ***
----------------------------------------------------------------------
-Significance codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
-
-Residual standard error: 1.1732 on 129 degrees of freedom
-Multiple R-squared: 0.9661,    Adjusted R-squared: 0.9653
-F-statistic: 1225.0733 on 4 and 129 DF,  p-value: 1.440e-94
+
+

_unnamed [4 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:term:statistic:estimate:p.value:std.error
:sales23.495527288.259760820.000000000.35154609
:youtube10.263398820.018094000.000000000.00176296
:facebook2.114582970.023085550.036389580.01091731
:youtube*facebook17.656461010.000938420.000000000.00005315

As the multiplcation of youtube*facebook is as well statistically relevant, it suggests that there is indeed an interaction between these 2 predictor variables youtube and facebook.

\(RMSE\)

-
(-> evaluations flatten first :test-transform :metric)
+
(-> evaluations flatten first :test-transform :metric)
-
1.0639476851234684
+
1.1026912986422122

\(R^2\)

-
(-> evaluations flatten first :test-transform :other-metrices first :metric)
+
(-> evaluations flatten first :test-transform :other-metrices first :metric)
-
0.9715836303642873
+
0.9712002908416029

\(RMSE\) and \(R^2\) of the intercation model are sligtly better.

These results suggest that the model with the interaction term is better than the model that contains only main effects. So, for this specific data, we should go for the model with the interaction model.

diff --git a/docs/noj_book.known_issues.html b/docs/noj_book.known_issues.html deleted file mode 100644 index bff9e33..0000000 --- a/docs/noj_book.known_issues.html +++ /dev/null @@ -1,679 +0,0 @@ - - - - - - - - - -Noj Documentation – 13  Known Issues ❗ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - -
- - - -
- -
-
-

13  Known Issues ❗

-
- - - -
- - - - -
- - - -
- - - - - - - -
-

13.1 clj fails with “mkl” error

-

If you encounter: Error building classpath. Could not acquire write lock for ‘artifact:org.bytedeco:mkl’

-

Try: clj -P -Sthreads 1

-

Explanation: see deps issue report.

-
- - - -
- -
- - -
- - - - - \ No newline at end of file diff --git a/docs/noj_book.known_issues_files/md-default0.js b/docs/noj_book.known_issues_files/md-default0.js deleted file mode 100644 index c4c6022..0000000 --- a/docs/noj_book.known_issues_files/md-default0.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=V(e||this.defaultElement||this)[0],this.element=V(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=V(),this.hoverable=V(),this.focusable=V(),this.classesElementLookup={},e!==this&&(V.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=V(e.style?e.ownerDocument:e.document||e),this.window=V(this.document[0].defaultView||this.document[0].parentWindow)),this.options=V.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:V.noop,_create:V.noop,_init:V.noop,destroy:function(){var i=this;this._destroy(),V.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:V.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return V.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t]=V.widget.extend({},this.options[t]),n=0;n
"),i=e.children()[0];return V("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.widthx(k(s),k(n))?o.important="horizontal":o.important="vertical",u.using.call(this,t,o)}),a.offset(V.extend(h,{using:t}))})},V.ui.position={fit:{left:function(t,e){var i=e.within,s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,a=s-o,r=o+e.collisionWidth-n-s;e.collisionWidth>n?0n?0")[0],w=d.each;function P(t){return null==t?t+"":"object"==typeof t?p[e.call(t)]||"object":typeof t}function M(t,e,i){var s=v[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:Math.min(s.max,Math.max(0,t)))}function S(s){var n=m(),o=n._rgba=[];return s=s.toLowerCase(),w(g,function(t,e){var i=e.re.exec(s),i=i&&e.parse(i),e=e.space||"rgba";if(i)return i=n[e](i),n[_[e].cache]=i[_[e].cache],o=n._rgba=i._rgba,!1}),o.length?("0,0,0,0"===o.join()&&d.extend(o,B.transparent),n):B[s]}function H(t,e,i){return 6*(i=(i+1)%1)<1?t+(e-t)*i*6:2*i<1?e:3*i<2?t+(e-t)*(2/3-i)*6:t}y.style.cssText="background-color:rgba(1,1,1,.5)",b.rgba=-1o.mod/2?s+=o.mod:s-n>o.mod/2&&(s-=o.mod)),l[i]=M((n-s)*a+s,e)))}),this[e](l)},blend:function(t){if(1===this._rgba[3])return this;var e=this._rgba.slice(),i=e.pop(),s=m(t)._rgba;return m(d.map(e,function(t,e){return(1-i)*s[e]+i*t}))},toRgbaString:function(){var t="rgba(",e=d.map(this._rgba,function(t,e){return null!=t?t:2").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e={width:i.width(),height:i.height()},n=document.activeElement;try{n.id}catch(t){n=document.body}return i.wrap(t),i[0]!==n&&!V.contains(i[0],n)||V(n).trigger("focus"),t=i.parent(),"static"===i.css("position")?(t.css({position:"relative"}),i.css({position:"relative"})):(V.extend(s,{position:i.css("position"),zIndex:i.css("z-index")}),V.each(["top","left","bottom","right"],function(t,e){s[e]=i.css(e),isNaN(parseInt(s[e],10))&&(s[e]="auto")}),i.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),i.css(e),t.css(s).show()},removeWrapper:function(t){var e=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),t[0]!==e&&!V.contains(t[0],e)||V(e).trigger("focus")),t}}),V.extend(V.effects,{version:"1.13.1",define:function(t,e,i){return i||(i=e,e="effect"),V.effects.effect[t]=i,V.effects.effect[t].mode=e,i},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,e="vertical"!==i?(e||100)/100:1;return{height:t.height()*e,width:t.width()*s,outerHeight:t.outerHeight()*e,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();1").insertAfter(t).css({display:/^(inline|ruby)/.test(t.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:t.css("marginTop"),marginBottom:t.css("marginBottom"),marginLeft:t.css("marginLeft"),marginRight:t.css("marginRight"),float:t.css("float")}).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).addClass("ui-effects-placeholder"),t.data(j+"placeholder",e)),t.css({position:i,left:s.left,top:s.top}),e},removePlaceholder:function(t){var e=j+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(t){V.effects.restoreStyle(t),V.effects.removePlaceholder(t)},setTransition:function(s,t,n,o){return o=o||{},V.each(t,function(t,e){var i=s.cssUnit(e);0");l.appendTo("body").addClass(t.className).css({top:s.top-a,left:s.left-r,height:i.innerHeight(),width:i.innerWidth(),position:n?"fixed":"absolute"}).animate(o,t.duration,t.easing,function(){l.remove(),"function"==typeof e&&e()})}}),V.fx.step.clip=function(t){t.clipInit||(t.start=V(t.elem).cssClip(),"string"==typeof t.end&&(t.end=G(t.end,t.elem)),t.clipInit=!0),V(t.elem).cssClip({top:t.pos*(t.end.top-t.start.top)+t.start.top,right:t.pos*(t.end.right-t.start.right)+t.start.right,bottom:t.pos*(t.end.bottom-t.start.bottom)+t.start.bottom,left:t.pos*(t.end.left-t.start.left)+t.start.left})},Y={},V.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,t){Y[t]=function(t){return Math.pow(t,e+2)}}),V.extend(Y,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;t<((e=Math.pow(2,--i))-1)/11;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),V.each(Y,function(t,e){V.easing["easeIn"+t]=e,V.easing["easeOut"+t]=function(t){return 1-e(1-t)},V.easing["easeInOut"+t]=function(t){return t<.5?e(2*t)/2:1-e(-2*t+2)/2}});y=V.effects,V.effects.define("blind","hide",function(t,e){var i={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},s=V(this),n=t.direction||"up",o=s.cssClip(),a={clip:V.extend({},o)},r=V.effects.createPlaceholder(s);a.clip[i[n][0]]=a.clip[i[n][1]],"show"===t.mode&&(s.cssClip(a.clip),r&&r.css(V.effects.clipToBox(a)),a.clip=o),r&&r.animate(V.effects.clipToBox(a),t.duration,t.easing),s.animate(a,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("bounce",function(t,e){var i,s,n=V(this),o=t.mode,a="hide"===o,r="show"===o,l=t.direction||"up",h=t.distance,c=t.times||5,o=2*c+(r||a?1:0),u=t.duration/o,d=t.easing,p="up"===l||"down"===l?"top":"left",f="up"===l||"left"===l,g=0,t=n.queue().length;for(V.effects.createPlaceholder(n),l=n.css(p),h=h||n["top"==p?"outerHeight":"outerWidth"]()/3,r&&((s={opacity:1})[p]=l,n.css("opacity",0).css(p,f?2*-h:2*h).animate(s,u,d)),a&&(h/=Math.pow(2,c-1)),(s={})[p]=l;g").css({position:"absolute",visibility:"visible",left:-s*p,top:-i*f}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:p,height:f,left:n+(u?a*p:0),top:o+(u?r*f:0),opacity:u?0:1}).animate({left:n+(u?0:a*p),top:o+(u?0:r*f),opacity:u?1:0},t.duration||500,t.easing,m)}),V.effects.define("fade","toggle",function(t,e){var i="show"===t.mode;V(this).css("opacity",i?0:1).animate({opacity:i?1:0},{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("fold","hide",function(e,t){var i=V(this),s=e.mode,n="show"===s,o="hide"===s,a=e.size||15,r=/([0-9]+)%/.exec(a),l=!!e.horizFirst?["right","bottom"]:["bottom","right"],h=e.duration/2,c=V.effects.createPlaceholder(i),u=i.cssClip(),d={clip:V.extend({},u)},p={clip:V.extend({},u)},f=[u[l[0]],u[l[1]]],s=i.queue().length;r&&(a=parseInt(r[1],10)/100*f[o?0:1]),d.clip[l[0]]=a,p.clip[l[0]]=a,p.clip[l[1]]=0,n&&(i.cssClip(p.clip),c&&c.css(V.effects.clipToBox(p)),p.clip=u),i.queue(function(t){c&&c.animate(V.effects.clipToBox(d),h,e.easing).animate(V.effects.clipToBox(p),h,e.easing),t()}).animate(d,h,e.easing).animate(p,h,e.easing).queue(t),V.effects.unshift(i,s,4)}),V.effects.define("highlight","show",function(t,e){var i=V(this),s={backgroundColor:i.css("backgroundColor")};"hide"===t.mode&&(s.opacity=0),V.effects.saveStyle(i),i.css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(s,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("size",function(s,e){var n,i=V(this),t=["fontSize"],o=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],a=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],r=s.mode,l="effect"!==r,h=s.scale||"both",c=s.origin||["middle","center"],u=i.css("position"),d=i.position(),p=V.effects.scaledDimensions(i),f=s.from||p,g=s.to||V.effects.scaledDimensions(i,0);V.effects.createPlaceholder(i),"show"===r&&(r=f,f=g,g=r),n={from:{y:f.height/p.height,x:f.width/p.width},to:{y:g.height/p.height,x:g.width/p.width}},"box"!==h&&"both"!==h||(n.from.y!==n.to.y&&(f=V.effects.setTransition(i,o,n.from.y,f),g=V.effects.setTransition(i,o,n.to.y,g)),n.from.x!==n.to.x&&(f=V.effects.setTransition(i,a,n.from.x,f),g=V.effects.setTransition(i,a,n.to.x,g))),"content"!==h&&"both"!==h||n.from.y!==n.to.y&&(f=V.effects.setTransition(i,t,n.from.y,f),g=V.effects.setTransition(i,t,n.to.y,g)),c&&(c=V.effects.getBaseline(c,p),f.top=(p.outerHeight-f.outerHeight)*c.y+d.top,f.left=(p.outerWidth-f.outerWidth)*c.x+d.left,g.top=(p.outerHeight-g.outerHeight)*c.y+d.top,g.left=(p.outerWidth-g.outerWidth)*c.x+d.left),delete f.outerHeight,delete f.outerWidth,i.css(f),"content"!==h&&"both"!==h||(o=o.concat(["marginTop","marginBottom"]).concat(t),a=a.concat(["marginLeft","marginRight"]),i.find("*[width]").each(function(){var t=V(this),e=V.effects.scaledDimensions(t),i={height:e.height*n.from.y,width:e.width*n.from.x,outerHeight:e.outerHeight*n.from.y,outerWidth:e.outerWidth*n.from.x},e={height:e.height*n.to.y,width:e.width*n.to.x,outerHeight:e.height*n.to.y,outerWidth:e.width*n.to.x};n.from.y!==n.to.y&&(i=V.effects.setTransition(t,o,n.from.y,i),e=V.effects.setTransition(t,o,n.to.y,e)),n.from.x!==n.to.x&&(i=V.effects.setTransition(t,a,n.from.x,i),e=V.effects.setTransition(t,a,n.to.x,e)),l&&V.effects.saveStyle(t),t.css(i),t.animate(e,s.duration,s.easing,function(){l&&V.effects.restoreStyle(t)})})),i.animate(g,{queue:!1,duration:s.duration,easing:s.easing,complete:function(){var t=i.offset();0===g.opacity&&i.css("opacity",f.opacity),l||(i.css("position","static"===u?"relative":u).offset(t),V.effects.saveStyle(i)),e()}})}),V.effects.define("scale",function(t,e){var i=V(this),s=t.mode,s=parseInt(t.percent,10)||(0===parseInt(t.percent,10)||"effect"!==s?0:100),s=V.extend(!0,{from:V.effects.scaledDimensions(i),to:V.effects.scaledDimensions(i,s,t.direction||"both"),origin:t.origin||["middle","center"]},t);t.fade&&(s.from.opacity=1,s.to.opacity=0),V.effects.effect.size.call(this,s,e)}),V.effects.define("puff","hide",function(t,e){t=V.extend(!0,{},t,{fade:!0,percent:parseInt(t.percent,10)||150});V.effects.effect.scale.call(this,t,e)}),V.effects.define("pulsate","show",function(t,e){var i=V(this),s=t.mode,n="show"===s,o=2*(t.times||5)+(n||"hide"===s?1:0),a=t.duration/o,r=0,l=1,s=i.queue().length;for(!n&&i.is(":visible")||(i.css("opacity",0).show(),r=1);l li > :first-child").add(t.find("> :not(li)").even())},heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=V(),this._addClass("ui-accordion","ui-widget ui-helper-reset"),this.element.attr("role","tablist"),t.collapsible||!1!==t.active&&null!=t.active||(t.active=0),this._processPanels(),t.active<0&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():V()}},_createIcons:function(){var t,e=this.options.icons;e&&(t=V(""),this._addClass(t,"ui-accordion-header-icon","ui-icon "+e.header),t.prependTo(this.headers),t=this.active.children(".ui-accordion-header-icon"),this._removeClass(t,e.header)._addClass(t,null,e.activeHeader)._addClass(this.headers,"ui-accordion-icons"))},_destroyIcons:function(){this._removeClass(this.headers,"ui-accordion-icons"),this.headers.children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeAttr("role"),this.headers.removeAttr("role aria-expanded aria-selected aria-controls tabIndex").removeUniqueId(),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role aria-hidden aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){"active"!==t?("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||!1!==this.options.active||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons())):this._activate(e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t),this._toggleClass(this.headers.add(this.headers.next()),null,"ui-state-disabled",!!t)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var e=V.ui.keyCode,i=this.headers.length,s=this.headers.index(t.target),n=!1;switch(t.keyCode){case e.RIGHT:case e.DOWN:n=this.headers[(s+1)%i];break;case e.LEFT:case e.UP:n=this.headers[(s-1+i)%i];break;case e.SPACE:case e.ENTER:this._eventHandler(t);break;case e.HOME:n=this.headers[0];break;case e.END:n=this.headers[i-1]}n&&(V(t.target).attr("tabIndex",-1),V(n).attr("tabIndex",0),V(n).trigger("focus"),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===V.ui.keyCode.UP&&t.ctrlKey&&V(t.currentTarget).prev().trigger("focus")},refresh:function(){var t=this.options;this._processPanels(),!1===t.active&&!0===t.collapsible||!this.headers.length?(t.active=!1,this.active=V()):!1===t.active?this._activate(0):this.active.length&&!V.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=V()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;"function"==typeof this.options.header?this.headers=this.options.header(this.element):this.headers=this.element.find(this.options.header),this._addClass(this.headers,"ui-accordion-header ui-accordion-header-collapsed","ui-state-default"),this.panels=this.headers.next().filter(":not(.ui-accordion-content-active)").hide(),this._addClass(this.panels,"ui-accordion-content","ui-helper-reset ui-widget-content"),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var i,t=this.options,e=t.heightStyle,s=this.element.parent();this.active=this._findActive(t.active),this._addClass(this.active,"ui-accordion-header-active","ui-state-active")._removeClass(this.active,"ui-accordion-header-collapsed"),this._addClass(this.active.next(),"ui-accordion-content-active"),this.active.next().show(),this.headers.attr("role","tab").each(function(){var t=V(this),e=t.uniqueId().attr("id"),i=t.next(),s=i.uniqueId().attr("id");t.attr("aria-controls",s),i.attr("aria-labelledby",e)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(t.event),"fill"===e?(i=s.height(),this.element.siblings(":visible").each(function(){var t=V(this),e=t.css("position");"absolute"!==e&&"fixed"!==e&&(i-=t.outerHeight(!0))}),this.headers.each(function(){i-=V(this).outerHeight(!0)}),this.headers.next().each(function(){V(this).height(Math.max(0,i-V(this).innerHeight()+V(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.headers.next().each(function(){var t=V(this).is(":visible");t||V(this).show(),i=Math.max(i,V(this).css("height","").height()),t||V(this).hide()}).height(i))},_activate:function(t){t=this._findActive(t)[0];t!==this.active[0]&&(t=t||this.active[0],this._eventHandler({target:t,currentTarget:t,preventDefault:V.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):V()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&V.each(t.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var e=this.options,i=this.active,s=V(t.currentTarget),n=s[0]===i[0],o=n&&e.collapsible,a=o?V():s.next(),r=i.next(),a={oldHeader:i,oldPanel:r,newHeader:o?V():s,newPanel:a};t.preventDefault(),n&&!e.collapsible||!1===this._trigger("beforeActivate",t,a)||(e.active=!o&&this.headers.index(s),this.active=n?V():s,this._toggle(a),this._removeClass(i,"ui-accordion-header-active","ui-state-active"),e.icons&&(i=i.children(".ui-accordion-header-icon"),this._removeClass(i,null,e.icons.activeHeader)._addClass(i,null,e.icons.header)),n||(this._removeClass(s,"ui-accordion-header-collapsed")._addClass(s,"ui-accordion-header-active","ui-state-active"),e.icons&&(n=s.children(".ui-accordion-header-icon"),this._removeClass(n,null,e.icons.header)._addClass(n,null,e.icons.activeHeader)),this._addClass(s.next(),"ui-accordion-content-active")))},_toggle:function(t){var e=t.newPanel,i=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=e,this.prevHide=i,this.options.animate?this._animate(e,i,t):(i.hide(),e.show(),this._toggleComplete(t)),i.attr({"aria-hidden":"true"}),i.prev().attr({"aria-selected":"false","aria-expanded":"false"}),e.length&&i.length?i.prev().attr({tabIndex:-1,"aria-expanded":"false"}):e.length&&this.headers.filter(function(){return 0===parseInt(V(this).attr("tabIndex"),10)}).attr("tabIndex",-1),e.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(t,i,e){var s,n,o,a=this,r=0,l=t.css("box-sizing"),h=t.length&&(!i.length||t.index()",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.lastMousePosition={x:null,y:null},this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault(),this._activateItem(t)},"click .ui-menu-item":function(t){var e=V(t.target),i=V(V.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&e.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),e.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&i.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":"_activateItem","mousemove .ui-menu-item":"_activateItem",mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this._menuItems().first();e||this.focus(t,i)},blur:function(t){this._delay(function(){V.contains(this.element[0],V.ui.safeActiveElement(this.document[0]))||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t,!0),this.mouseHandled=!1}})},_activateItem:function(t){var e,i;this.previousFilter||t.clientX===this.lastMousePosition.x&&t.clientY===this.lastMousePosition.y||(this.lastMousePosition={x:t.clientX,y:t.clientY},e=V(t.target).closest(".ui-menu-item"),i=V(t.currentTarget),e[0]===i[0]&&(i.is(".ui-state-active")||(this._removeClass(i.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(t,i))))},_destroy:function(){var t=this.element.find(".ui-menu-item").removeAttr("role aria-disabled").children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),t.children().each(function(){var t=V(this);t.data("ui-menu-submenu-caret")&&t.remove()})},_keydown:function(t){var e,i,s,n=!0;switch(t.keyCode){case V.ui.keyCode.PAGE_UP:this.previousPage(t);break;case V.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case V.ui.keyCode.HOME:this._move("first","first",t);break;case V.ui.keyCode.END:this._move("last","last",t);break;case V.ui.keyCode.UP:this.previous(t);break;case V.ui.keyCode.DOWN:this.next(t);break;case V.ui.keyCode.LEFT:this.collapse(t);break;case V.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case V.ui.keyCode.ENTER:case V.ui.keyCode.SPACE:this._activate(t);break;case V.ui.keyCode.ESCAPE:this.collapse(t);break;default:e=this.previousFilter||"",s=n=!1,i=96<=t.keyCode&&t.keyCode<=105?(t.keyCode-96).toString():String.fromCharCode(t.keyCode),clearTimeout(this.filterTimer),i===e?s=!0:i=e+i,e=this._filterMenuItems(i),(e=s&&-1!==e.index(this.active.next())?this.active.nextAll(".ui-menu-item"):e).length||(i=String.fromCharCode(t.keyCode),e=this._filterMenuItems(i)),e.length?(this.focus(t,e),this.previousFilter=i,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}n&&t.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var t,e,s=this,n=this.options.icons.submenu,i=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),e=i.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=V(this),e=t.prev(),i=V("").data("ui-menu-submenu-caret",!0);s._addClass(i,"ui-menu-icon","ui-icon "+n),e.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",e.attr("id"))}),this._addClass(e,"ui-menu","ui-widget ui-widget-content ui-front"),(t=i.add(this.element).find(this.options.items)).not(".ui-menu-item").each(function(){var t=V(this);s._isDivider(t)&&s._addClass(t,"ui-menu-divider","ui-widget-content")}),i=(e=t.not(".ui-menu-item, .ui-menu-divider")).children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(e,"ui-menu-item")._addClass(i,"ui-menu-item-wrapper"),t.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!V.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){var i;"icons"===t&&(i=this.element.find(".ui-menu-icon"),this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",String(t)),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),i=this.active.children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",i.attr("id")),i=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),(i=e.children(".ui-menu")).length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(t){var e,i,s;this._hasScroll()&&(i=parseFloat(V.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(V.css(this.activeMenu[0],"paddingTop"))||0,e=t.offset().top-this.activeMenu.offset().top-i-s,i=this.activeMenu.scrollTop(),s=this.activeMenu.height(),t=t.outerHeight(),e<0?this.activeMenu.scrollTop(i+e):s",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,liveRegionTimer:null,_create:function(){var i,s,n,t=this.element[0].nodeName.toLowerCase(),e="textarea"===t,t="input"===t;this.isMultiLine=e||!t&&this._isContentEditable(this.element),this.valueMethod=this.element[e||t?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(t){if(this.element.prop("readOnly"))s=n=i=!0;else{s=n=i=!1;var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:i=!0,this._move("previousPage",t);break;case e.PAGE_DOWN:i=!0,this._move("nextPage",t);break;case e.UP:i=!0,this._keyEvent("previous",t);break;case e.DOWN:i=!0,this._keyEvent("next",t);break;case e.ENTER:this.menu.active&&(i=!0,t.preventDefault(),this.menu.select(t));break;case e.TAB:this.menu.active&&this.menu.select(t);break;case e.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(t),t.preventDefault());break;default:s=!0,this._searchTimeout(t)}}},keypress:function(t){if(i)return i=!1,void(this.isMultiLine&&!this.menu.element.is(":visible")||t.preventDefault());if(!s){var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:this._move("previousPage",t);break;case e.PAGE_DOWN:this._move("nextPage",t);break;case e.UP:this._keyEvent("previous",t);break;case e.DOWN:this._keyEvent("next",t)}}},input:function(t){if(n)return n=!1,void t.preventDefault();this._searchTimeout(t)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){clearTimeout(this.searching),this.close(t),this._change(t)}}),this._initSource(),this.menu=V("