├── src ├── deps.cljs └── tech │ └── v3 │ ├── dataset │ ├── node.cljs │ ├── rolling.cljs │ ├── protocols.cljs │ ├── string_table.cljs │ ├── columnwise_map.cljs │ ├── io │ │ └── column_parsers.cljs │ └── impl │ │ └── column.cljs │ ├── datatype │ ├── argtypes.cljs │ ├── copy_make_container.cljs │ ├── datetime.cljs │ ├── format_sequence.cljs │ ├── casting.cljs │ ├── bitmap.cljs │ ├── protocols.cljs │ ├── emap1_vec.cljs │ ├── functional.cljs │ ├── statistics.cljs │ ├── reader_vec.cljs │ ├── list.cljs │ ├── base.cljs │ └── argops.cljs │ └── libs │ ├── muuntaja.clj │ └── cljs_ajax.cljs ├── scripts ├── cljs-docs ├── repl ├── install-local ├── run-tests └── deploy ├── docs ├── images │ └── memcomp.png ├── highlight │ ├── solarized-light.css │ └── highlight.min.js ├── js │ └── page_effects.js ├── tech.v3.dataset.node.html ├── Reductions.html ├── tech.v3.libs.cljs-ajax.html ├── tech.v3.datatype.argops.html ├── css │ └── default.css └── tech.v3.datatype.functional.html ├── testapp ├── .gitignore ├── dev-resources │ └── logback.xml ├── README.md ├── shadow-cljs.edn ├── deps.edn └── src │ └── testapp │ ├── webapp.cljs │ └── main.clj ├── .gitignore ├── .clj-kondo ├── config.edn └── cnuernber │ └── dtype-next │ ├── tech │ └── v3 │ │ ├── datatype.clj │ │ ├── datatype_api.clj │ │ ├── parallel │ │ └── for.clj │ │ ├── datatype │ │ ├── binary_pred.clj │ │ ├── gradient.clj │ │ ├── jvm_map.clj │ │ ├── ffi.clj │ │ ├── statistics.clj │ │ ├── binary_op.clj │ │ ├── unary_op.clj │ │ ├── export_symbols.clj │ │ └── functional_api.clj │ │ ├── tensor.clj │ │ └── tensor_api.clj │ ├── taoensso │ └── nippy.clj │ └── config.edn ├── shadow-cljs.edn ├── package.json ├── resources └── logback.xml ├── LICENSE ├── test └── data │ ├── text.transit-json │ └── stocks.transit-json ├── CHANGELOG.md ├── deps.edn ├── topics └── Reductions.md └── README.md /src/deps.cljs: -------------------------------------------------------------------------------- 1 | {:npm-deps {"base64-js" "^1.5.0"}} 2 | -------------------------------------------------------------------------------- /scripts/cljs-docs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clj -X:cljs-docs -------------------------------------------------------------------------------- /scripts/repl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | clj -A:dev -M:cljs node-repl 5 | -------------------------------------------------------------------------------- /scripts/install-local: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clj -X:depstar 4 | clj -X:install -------------------------------------------------------------------------------- /docs/images/memcomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnuernber/tmdjs/HEAD/docs/images/memcomp.png -------------------------------------------------------------------------------- /testapp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cpcache 3 | target 4 | resources/public 5 | package.json 6 | package-lock.json -------------------------------------------------------------------------------- /scripts/run-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clj -M:cljs compile test && node target/test.js 4 | clj -M:cljs release test && node target/test.js 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cpcache 3 | .shadow-cljs 4 | .nrepl-port 5 | target 6 | *.transit-json 7 | *.transit-json.gz 8 | pom.xml 9 | *.pom.asc 10 | .clj-kondo 11 | .lsp 12 | -------------------------------------------------------------------------------- /scripts/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | scripts/run-tests 6 | clj -A:dev -X:codox 7 | rm -rf pom.xml 8 | clojure -T:build jar 9 | cp target/classes/META-INF/maven/com.cnuernber/tmdjs/pom.xml . 10 | clojure -X:deploy 11 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:config-paths ["cnuernber/dtype-next"] 2 | :skip-comments true 3 | ;;We have to use private calls into the cljs runtime in order to avoid duplicating 4 | ;;some pretty dirty code. 5 | :linters {:private-call {:level :off}}} 6 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:deps true 2 | ;; set an nrepl port for connection to a REPL. 3 | :nrepl {:port 8777} 4 | :source-paths ["src" "test"] 5 | :builds {:test {:target :node-test 6 | :output-to "target/test.js" 7 | :ns-regexp ".*test$"}}} 8 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype) 2 | 3 | (defmacro make-reader 4 | ([datatype n-elems read-op] 5 | `(let [~'idx ~n-elems] 6 | ~read-op)) 7 | ([reader-datatype adv-datatype n-elems read-op] 8 | `(let [~'idx ~n-elems] 9 | ~read-op))) 10 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype_api.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype-api) 2 | 3 | 4 | (defmacro make-reader 5 | ([datatype n-elems read-op] 6 | `(let [~'idx ~n-elems] 7 | ~read-op)) 8 | ([reader-datatype adv-datatype n-elems read-op] 9 | `(let [~'idx ~n-elems] 10 | ~read-op))) 11 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/parallel/for.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.parallel.for) 2 | 3 | 4 | (defmacro doiter 5 | [varname iterable & body] 6 | `(let [~varname ~iterable] 7 | ~@body)) 8 | 9 | 10 | (defmacro parallel-for 11 | [idx-var n-elems & body] 12 | `(let [~idx-var ~n-elems] 13 | ~@body)) 14 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/binary_pred.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.binary-pred) 2 | 3 | 4 | 5 | (defmacro make-boolean-predicate 6 | [opname op] 7 | `(let [~'x 1 8 | ~'y 2] 9 | ~op)) 10 | 11 | 12 | (defmacro make-numeric-binary-predicate 13 | [opname scalar-op object-op] 14 | `(let [~'x 1 15 | ~'y 2] 16 | ~scalar-op 17 | ~object-op)) 18 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/taoensso/nippy.clj: -------------------------------------------------------------------------------- 1 | (ns taoensso.nippy) 2 | 3 | 4 | (defmacro extend-freeze 5 | [dt _kwd argvec & code] 6 | (let [[buf-var out-var] argvec] 7 | `(let [~buf-var 1 8 | ~out-var 2] 9 | (type ~dt) 10 | ~@code))) 11 | 12 | 13 | (defmacro extend-thaw 14 | [_kwd argvec & code] 15 | (let [invar (first argvec)] 16 | `(let [~invar 1] 17 | ~@code))) 18 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/gradient.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.gradient) 2 | 3 | 4 | (defmacro append-diff 5 | [rtype n-elems read-fn cast-fn append reader] 6 | `(. ~read-fn ~reader)) 7 | 8 | (defmacro prepend-diff 9 | [rtype n-elems read-fn cast-fn append reader] 10 | `(. ~read-fn ~reader)) 11 | 12 | (defmacro basic-diff 13 | [rtype n-elems read-fn reader] 14 | `(. ~read-fn ~reader)) 15 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/jvm_map.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.jvm-map) 2 | 3 | 4 | (defmacro bi-consumer 5 | [karg varg code] 6 | `(let [~karg 1 7 | ~varg 2] 8 | ~@code)) 9 | 10 | 11 | (defmacro bi-function 12 | [karg varg code] 13 | `(let [~karg 1 14 | ~varg 2] 15 | ~@code)) 16 | 17 | 18 | (defmacro function 19 | [karg code] 20 | `(let [~karg 1] 21 | ~@code)) 22 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/node.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.node 2 | "Functions and helpers that require the node runtime." 3 | (:require [fs :as fs] 4 | [tech.v3.dataset :as ds])) 5 | 6 | 7 | (defn transit-file->dataset 8 | "Given a file of transit data return a dataset." 9 | [fname] 10 | ;;returns buffer 11 | (-> (.readFileSync fs fname) 12 | ;;utf-8 encoded string 13 | (.toString) 14 | (ds/transit-str->dataset))) 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@cljs-oss/module-deps": "^1.1.1", 4 | "@js-joda/core": "^3.2.0", 5 | "@js-joda/locale_en-us": "3.1.1", 6 | "@js-joda/timezone": "2.5.0", 7 | "base64-js": "^1.5.0", 8 | "cached-path-relative": ">=1.1.0", 9 | "minimist": ">=1.2.6", 10 | "ws": "^7.5.7" 11 | }, 12 | "devDependencies": { 13 | "source-map-support": "^0.5.21" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/argtypes.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.argtypes) 2 | 3 | 4 | (defn argtype 5 | [data] 6 | (cond 7 | (or (number? data) (string? data) (boolean? data)) :scalar 8 | (or (instance? js/Array data) (indexed? data)) :reader 9 | ;;sequences aren't actually iterable but they are close to iterables 10 | (seq? data) :iterable 11 | :else :scalar)) 12 | 13 | 14 | (defn scalar? 15 | [data] 16 | (= :scalar (argtype data))) 17 | -------------------------------------------------------------------------------- /resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /testapp/dev-resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /testapp/README.md: -------------------------------------------------------------------------------- 1 | # Test App for tmdjs 2 | 3 | ## Usage 4 | 5 | 1. Create package.json - `clj -M:cljs-install -m cljs.main --install-deps`. 6 | 2. `npm install` 7 | 3. `clj -M:cljs compile app` 8 | 9 | Now you can run the server from the main namespace and check out the timings. 10 | 11 | In order to run the cljs repl use 12 | 13 | 1. `clj -M:cljs watch app` 14 | 2. cider-connect to port 8777 15 | 3. `(shadow/repl :app)` 16 | 17 | To test a release version type: 18 | 19 | * `rm -rf resources/public && clj -M:cljs release app` 20 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/ffi.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.ffi) 2 | 3 | 4 | (defmacro define-library! 5 | [lib-varname lib-fns _lib-symbols _error-checker] 6 | (let [fn-defs (second lib-fns)] 7 | `(do 8 | (def ~lib-varname :ok) 9 | ~@(map (fn [[fn-name fn-data]] 10 | (let [argvec (mapv first (:argtypes fn-data))] 11 | `(defn ~(symbol (name fn-name)) 12 | ~argvec 13 | (apply + ~argvec)))) 14 | fn-defs)))) 15 | -------------------------------------------------------------------------------- /testapp/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:deps true 2 | ;; set an nrepl port for connection to a REPL. 3 | :nrepl {:port 8777} 4 | :builds {;; example build config, usage suitable for user apps 5 | :app {:target :browser 6 | ;;module :app will output data to app.js 7 | :modules {:app {:init-fn testapp.webapp/init}} 8 | :output-dir "resources/public/js" 9 | :asset-path "js" 10 | :devtools {:http-root "resources/public" 11 | :http-port 8700}}}} 12 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/statistics.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.statistics) 2 | 3 | 4 | (defmacro define-descriptive-stats 5 | [] 6 | `(do 7 | ~@(->> [:skew :variance :standard-deviation :moment-3 :kurtosis :moment-4 :moment-2] 8 | (map (fn [tower-key] 9 | (let [fn-symbol (symbol (name tower-key))] 10 | `(defn ~fn-symbol 11 | ([~'data ~'options] 12 | (apply + ~'data ~'options) 13 | ~'data) 14 | ([~'data] 15 | (apply + ~'data))))))))) 16 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/binary_op.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.binary-op) 2 | 3 | (defmacro make-numeric-object-binary-op 4 | [opname scalar-op object-op identity-value] 5 | `(let [~'x 1 6 | ~'y 2] 7 | ~scalar-op 8 | ~object-op)) 9 | 10 | (defmacro make-float-double-binary-op 11 | ([opname scalar-op identity-value] 12 | `(let [~'x 1 13 | ~'y 2] 14 | ~scalar-op)) 15 | ([opname scalar-op] 16 | `(let [~'x 1 17 | ~'y 2] 18 | ~scalar-op))) 19 | 20 | (defmacro make-int-long-binary-op 21 | [opname scalar-op] 22 | `(let [~'x 1 23 | ~'y 2] 24 | ~scalar-op)) 25 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/unary_op.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.unary-op) 2 | 3 | 4 | (defmacro make-double-unary-op 5 | [_opname opcode] 6 | `(let [~'x 1.0] 7 | ~opcode)) 8 | 9 | 10 | (defmacro make-numeric-object-unary-op 11 | [_opname opcode] 12 | `(let [~'x 1.0] 13 | ~opcode)) 14 | 15 | 16 | (defmacro make-float-double-unary-op 17 | [_opname opcode] 18 | `(let [~'x 1.0] 19 | ~opcode)) 20 | 21 | 22 | (defmacro make-numeric-unary-op 23 | [_opname opcode] 24 | `(let [~'x 1.0] 25 | ~opcode)) 26 | 27 | 28 | (defmacro make-long-unary-op 29 | [_opname opcode] 30 | `(let [~'x 1] 31 | ~opcode)) 32 | 33 | 34 | (defmacro make-all-datatype-unary-op 35 | [_opname opcode] 36 | `(let [~'x 1] 37 | ~opcode)) 38 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/tensor.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.tensor) 2 | 3 | (defn args-expansion 4 | [op-code-args] 5 | (if (symbol? op-code-args) 6 | [op-code-args [:a :b :c]] 7 | (->> op-code-args 8 | (mapcat (fn [argname] 9 | [argname 1.0])) 10 | (vec)))) 11 | 12 | 13 | (defmacro typed-compute-tensor 14 | ([datatype advertised-datatype rank shape op-code-args op-code] 15 | (let [args-expansion (args-expansion op-code-args)] 16 | `(let ~args-expansion 17 | ~op-code))) 18 | ([advertised-datatype rank shape op-code-args op-code] 19 | (let [args-expansion (args-expansion op-code-args)] 20 | `(let ~args-expansion 21 | ~op-code))) 22 | ([_advertised-datatype _shape op-code-args op-code] 23 | (let [args-expansion (args-expansion op-code-args)] 24 | `(let ~args-expansion 25 | ~op-code)))) 26 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/tensor_api.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.tensor-api) 2 | 3 | 4 | (defn args-expansion 5 | [op-code-args] 6 | (if (symbol? op-code-args) 7 | [op-code-args [:a :b :c]] 8 | (->> op-code-args 9 | (mapcat (fn [argname] 10 | [argname 1.0])) 11 | (vec)))) 12 | 13 | 14 | (defmacro typed-compute-tensor 15 | ([datatype advertised-datatype rank shape op-code-args op-code] 16 | (let [args-expansion (args-expansion op-code-args)] 17 | `(let ~args-expansion 18 | ~op-code))) 19 | ([advertised-datatype rank shape op-code-args op-code] 20 | (let [args-expansion (args-expansion op-code-args)] 21 | `(let ~args-expansion 22 | ~op-code))) 23 | ([_advertised-datatype _shape op-code-args op-code] 24 | (let [args-expansion (args-expansion op-code-args)] 25 | `(let ~args-expansion 26 | ~op-code)))) 27 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/copy_make_container.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.copy-make-container 2 | (:require [tech.v3.datatype.arrays :as dt-arrays] 3 | [tech.v3.datatype.base :as dt-base])) 4 | 5 | 6 | (defn make-container 7 | [dtype len-or-data] 8 | (let [data (if (number? len-or-data) 9 | nil 10 | (if-let [ag-data (dt-base/as-typed-array len-or-data)] 11 | ag-data 12 | (if-let [ag-data (dt-base/as-js-array len-or-data)] 13 | ag-data 14 | (if (and (dt-base/counted? len-or-data) 15 | (dt-base/indexed? len-or-data)) 16 | len-or-data 17 | (vec len-or-data))))) 18 | dlen (if (number? len-or-data) 19 | len-or-data 20 | (count data)) 21 | container (dt-arrays/make-array dtype dlen)] 22 | (when (not-empty data) (dt-base/set-value! container 0 data)) 23 | container)) 24 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/rolling.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.rolling 2 | (:require [tech.v3.dataset :as ds] 3 | [tech.v3.datatype :as dt])) 4 | 5 | 6 | (defn mean 7 | [data] 8 | (let [data (dt/as-agetable data) 9 | n (dt/ecount data)] 10 | (loop [idx 0 11 | sum 0.0] 12 | (if (< idx n) 13 | (recur (unchecked-inc idx) (+ sum (aget data idx))) 14 | (/ sum n))))) 15 | 16 | (defn scalar-fixed-rolling 17 | [ds cname filter-width reducer] 18 | (let [ne (ds/row-count ds) 19 | col (ds cname) 20 | data (or (dt/as-agetable col) (dt/as-agetable (dt/make-container :float32 col))) 21 | rv (dt/make-container :float32 ne) 22 | rvd (dt/as-agetable rv) 23 | filter-width (long filter-width)] 24 | (dotimes [idx ne] 25 | (let [sidx (max 0 (- idx filter-width)) 26 | eidx (min ne (+ idx filter-width)) 27 | sbuf (dt/sub-buffer data sidx (- eidx sidx))] 28 | (aset rvd idx (reducer sbuf)))) 29 | rv)) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2021 Chris Nuernberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/export_symbols.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.export-symbols 2 | (:require [clj-kondo.hooks-api :as hooks-api])) 3 | 4 | (defmacro export-symbols 5 | [src-ns & symbol-list] 6 | (let [analysis (:clj (hooks-api/ns-analysis src-ns))] 7 | `(do 8 | ~@(->> symbol-list 9 | (mapv 10 | (fn [sym-name] 11 | (when-let [fn-data (get analysis sym-name)] 12 | (if-let [arities (get fn-data :fixed-arities)] 13 | `(defn ~sym-name 14 | ~@(->> arities 15 | (map (fn [arity] 16 | (let [argvec (mapv 17 | #(symbol (str "arg-" %)) 18 | (range arity))] 19 | `(~argvec (apply + ~argvec) 20 | ;;This line is to disable the type detection of clj-kondo 21 | ~(if-not (= 0 arity) 22 | (first argvec) 23 | :ok))))))) 24 | `(def ~sym-name :defined))))))))) 25 | -------------------------------------------------------------------------------- /test/data/text.transit-json: -------------------------------------------------------------------------------- 1 | ["~#tech.v3.dataset",["^ ","~:metadata",["^ ","~:name","_unnamed"],"~:flavor","~:transit","~:version",1,"~:columns",[["^ ","^1",["^ ","~:categorical?",true,"^2","~:e","~:datatype","~:persistent-vector","~:n-elems",5],"~:missing",[4],"~:data",[[1,2,3],[1,2,3],[1,2,3],[1,2,3],null]],["^ ","^1",["^ ","^2","~:g","^8","~:instant","^:",5],"^;",[],"^<","dXPuq4IBAAB1c+6rggEAAHVz7quCAQAAdXPuq4IBAAB1c+6rggEAAA=="],["^ ","^1",["^ ","^7",true,"^2","~:c","^8","~:string","^:",5],"^;",[],"^<",["^ ","~:strtable",["hey"],"~:indexes","AAAAAAAAAAAAAAAAAAAAAAAAAAA="]],["^ ","^1",["^ ","^2","~:h","^8","~:boolean","^:",5],"^;",[],"^<","AQABAQA="],["^ ","^1",["^ ","^7",true,"^2","~:b","^8","~:keyword","^:",5],"^;",[],"^<",["~:a","~:a","~:a","~:a","~:a"]],["^ ","^1",["^ ","^7",true,"^2","~:d","^8","~:persistent-map","^:",5],"^;",[],"^<",[["^ ","~:a",1,"~:b",2],["^ ","~:a",1,"~:b",2],["^ ","~:a",1,"~:b",2],["^ ","~:a",1,"~:b",2],["^ ","~:a",1,"~:b",2]]],["^ ","^1",["^ ","^2","~:f","^8","~:local-date","^:",5],"^;",[],"^<","FUsAABVLAAAVSwAAFUsAABVLAAA="],["^ ","^1",["^ ","^7",true,"^2","~:i","^8","~:text","^:",5],"^;",[],"^<",["^ ","~:offset-dtype","~:int8","~:offsets","BAgMEBQ=","~:buffer","texttexttexttexttext"]],["^ ","^1",["^ ","^2","~:a","^8","~:float64","^:",5],"^;",[],"^<","AAAAAAAAAAAAAAAAAADwPwAAAAAAAABAAAAAAAAACEAAAAAAAAAQQA=="]]]] -------------------------------------------------------------------------------- /src/tech/v3/datatype/datetime.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.datetime 2 | "Minimal API for converting dealing with instants and local dates. Please see 3 | juxt/tick for a more thorough treatment." 4 | (:require [cljc.java-time.local-date :as local-date] 5 | [cljc.java-time.instant :as instant] 6 | [java.time :refer [LocalDate Instant]] 7 | [tech.v3.datatype.protocols :as dt-proto])) 8 | 9 | (extend-protocol dt-proto/PDatatype 10 | LocalDate 11 | (-datatype [item] :local-date) 12 | Instant 13 | (-datatype [item] :instant)) 14 | 15 | 16 | (defn datetime-datatype? 17 | [dtype] 18 | (if (#{:local-date :instant} dtype) 19 | true 20 | false)) 21 | 22 | 23 | (defn local-date 24 | [] (local-date/now)) 25 | 26 | 27 | (defn local-date->epoch-days 28 | [ld] 29 | (local-date/to-epoch-day ld)) 30 | 31 | 32 | (defn epoch-days->local-date 33 | [ed] 34 | (local-date/of-epoch-day ed)) 35 | 36 | 37 | (defn instant 38 | [] (instant/now)) 39 | 40 | 41 | (defn epoch-milliseconds->instant 42 | [em] (instant/of-epoch-milli em)) 43 | 44 | 45 | (defn instant->epoch-milliseconds 46 | [in] (instant/to-epoch-milli in)) 47 | 48 | 49 | (defn epoch-microseconds->instant 50 | [em] (instant/of-epoch-milli (/ em 1000))) 51 | 52 | 53 | (defn instant->epoch-microseconds 54 | [in] (* 1000 (instant/to-epoch-milli in))) 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.001 2 | * Fix many issues that had crept in - one being (seq ds) being a sequence of keyvals which is wrong - java TMD implements seq/reduce as sequence of columns. **This leads to other incompatilibities mainly keys/vals no longer work!!.**. 3 | * `(assoc ds :b (dt/reify-reader ...))` works correctly both when using :object datatype and and exact datatype - this is a lazy operation and does not rescan the data for ##NaN and friends -- if you want to provide a missing values 4 | using the form `#:tech.v3.dataset{:data (reify-reader...) :missing #{missing data}}`. This form works with assoc. As always passing in a container with `:object` datatype will always force a scan of the data. 5 | 6 | # 2.000 7 | * Latest TMD support - clj-transit is now in TMD and not in this project as transit is a first-class format for tmd. 8 | * clojars coord is now com.cnuernber/tmdjs to match other libs. 9 | 10 | # 2.000-beta-10 11 | * Added high performance pathway for parsing datasets and realizing intermediate datasets. 12 | The dataset-parser now implements several protocols including count, nth, so you can get, 13 | for instance, the last row during the parsing phase. When using the dataset-parser you 14 | have to use the tech.v3.dataset.protocols/-add-row and -add-rows methods. 15 | # 2.000-beta-9 16 | * fast-nth fixed for integer columns. 17 | * various fixes around parsing data. 18 | -------------------------------------------------------------------------------- /docs/highlight/solarized-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #fdf6e3; 12 | color: #657b83; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #93a1a1; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #eee8d5; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/protocols.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.protocols) 2 | 3 | (defprotocol PRowCount 4 | (-row-count [this])) 5 | 6 | (defprotocol PColumnCount 7 | (-column-count [this])) 8 | 9 | (defprotocol PMissing 10 | (-missing [this])) 11 | 12 | (defprotocol PSelectRows 13 | (-select-rows [this rowidxs])) 14 | 15 | (defprotocol PSelectColumns 16 | (-select-columns [this colnames])) 17 | 18 | (defprotocol PColumn 19 | (-is-column? [col]) 20 | (-column-buffer [col])) 21 | 22 | 23 | (defprotocol PDataset 24 | "Raw protocols for dealing with datasets." 25 | ;;error on failure 26 | (-column [ds colname]) 27 | (-columns [ds]) 28 | (-columns-as-map [ds]) 29 | ;;indexable object. 30 | (-rows [ds]) 31 | (-rowvecs [ds]) 32 | (-row-at [ds idx]) 33 | (-rowvec-at [ds idx])) 34 | 35 | 36 | (defprotocol PDatasetParser 37 | "Protocols for the dataset parser created via (dataset-parser)." 38 | (-add-row [p row] 39 | "row needs to reduce to a sequence of objects implementing -key and -val") 40 | (-add-rows [p rows] 41 | "rows need only be reducible") 42 | (-parser->rf [p])) 43 | 44 | 45 | (extend-type object 46 | PMissing 47 | (-missing [this] (js/Set.)) 48 | PColumn 49 | (-is-column? [col] false) 50 | PRowCount 51 | (-row-count [this] (count this)) 52 | PColumnCount 53 | (-column-count [this] 0)) 54 | 55 | (extend-type array 56 | PColumn 57 | (-is-column? [col] false)) 58 | 59 | (extend-type boolean 60 | PColumn 61 | (-is-column? [col] false)) 62 | 63 | (extend-type number 64 | PColumn 65 | (-is-column? [col] false)) 66 | 67 | (extend-type nil 68 | PColumn 69 | (-is-column? [col] false)) 70 | -------------------------------------------------------------------------------- /testapp/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "resources"] 2 | :deps {com.cnuernber/tmdjs {:mvn/version "2.000-beta-3-SNAPSHOT"} 3 | http-kit/http-kit {:mvn/version "2.5.3"} 4 | hiccup/hiccup {:mvn/version"1.0.5"} 5 | bidi/bidi {:mvn/version "2.1.6"} 6 | ring/ring {:mvn/version "1.7.0"} 7 | ring/ring-codec {:mvn/version "1.1.3"} 8 | metosin/muuntaja {:mvn/version "0.6.8"} 9 | amalloy/ring-gzip-middleware {:mvn/version "0.1.4"}} 10 | :aliases 11 | {:cljs 12 | {:extra-deps {thheller/shadow-cljs {:mvn/version "2.12.4"} 13 | cider/cider-nrepl {:mvn/version "0.26.0"} 14 | cljs-ajax/cljs-ajax {:mvn/version "0.8.3"} 15 | re-frame/re-frame {:mvn/version "1.2.0"}} 16 | ;;dev-resources contains logback.xml which disables annoying jboss logging 17 | :extra-paths ["dev-resources"] 18 | :main-opts ["-m" "shadow.cljs.devtools.cli"]} 19 | 20 | ;;used for installing base package.json 21 | ;;clj -M:cljs-install -m cljs.main --install-deps 22 | :cljs-install 23 | {:extra-deps {thheller/shadow-cljs {:mvn/version "2.12.4"} 24 | cljs-ajax/cljs-ajax {:mvn/version "0.8.0"} 25 | re-frame/re-frame {:mvn/version "1.2.0"}}} 26 | :standalone-server 27 | {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.0.193"}} 28 | :ns-default hf.depstar 29 | :exec-fn hf.depstar/uberjar 30 | :exec-args {:group-id "com.cnuernber" 31 | :artifact-id "testapp" 32 | :version "1.00-beta-1" 33 | :sync-pom true 34 | :aot true 35 | :main-class testapp.main 36 | :jar "target/testapp.jar" 37 | ;;Disable tensor code generation and ensure direct linking. 38 | :jvm-opts ["-Dclojure.compiler.direct-linking=true" 39 | "-Dclojure.spec.skip-macros=true"]}}}} 40 | -------------------------------------------------------------------------------- /src/tech/v3/libs/muuntaja.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.libs.muuntaja 2 | "Bindings to allow you to send/receive datasets via transit-json using 3 | server-side muuntaja bindings. For client side bindings, please see 4 | tech.v3.libs.cljs-ajax." 5 | (:require [muuntaja.core :as m] 6 | [tech.v3.libs.clj-transit :as tech-transit] 7 | [muuntaja.middleware :as muuntaja-middle])) 8 | 9 | 10 | ;;https://github.com/metosin/muuntaja/blob/master/doc/With-Ring.md 11 | 12 | 13 | (defn- muuntaja-opts 14 | [{:keys [read-handlers write-handlers]}] 15 | (reduce (fn [opts opt-key] 16 | (update-in opts 17 | [:formats opt-key] 18 | merge 19 | {:encoder-opts {:handlers (merge tech-transit/write-handlers 20 | write-handlers)} 21 | :decoder-opts {:handlers (merge tech-transit/read-handlers 22 | read-handlers)}})) 23 | m/default-options 24 | ["application/transit+json" "application/transit+msgpack"])) 25 | 26 | 27 | (defn- muuntaja 28 | [& [options]] 29 | (m/create (muuntaja-opts options))) 30 | 31 | 32 | (defn wrap-format 33 | "Wrapper for muuntaja wrap-format that adds in the transit read/write handlers 34 | for datasets. 35 | 36 | Options 37 | 38 | * `:write-handlers` - transit handler-map of extra handlers to use when writing data. 39 | * `:read-handlers` - transit handler-map of extra handlers to use when reading data." 40 | [handler & [options]] 41 | (muuntaja-middle/wrap-format handler (muuntaja options))) 42 | 43 | 44 | (defn wrap-format-java-time 45 | "Wrap-format support datasets and scalar java.time.LocalDate and java.time.Instant objects." 46 | [handler & [options]] 47 | (wrap-format handler (assoc options 48 | :write-handlers tech-transit/java-time-write-handlers 49 | :read-handlers tech-transit/java-time-read-handlers))) 50 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/string_table.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.string-table 2 | "A dtype 'list' that unique-ifies its strings. Unlike the jvm version, this 3 | simply uses an object array for the strings." 4 | (:require [tech.v3.datatype :as dtype] 5 | [tech.v3.datatype.protocols :as dt-proto])) 6 | 7 | 8 | (defn- intern-string 9 | [strval str-table] 10 | (if-let [retval (.get str-table strval)] 11 | retval 12 | (do (.set str-table strval strval) 13 | strval))) 14 | 15 | 16 | (deftype StringTable [container str-table metadata] 17 | ICounted 18 | (-count [_this] (count container)) 19 | ICloneable 20 | (-clone [_this] 21 | (clone container)) 22 | IFn 23 | (-invoke [this idx] (nth this idx)) 24 | IIndexed 25 | (-nth [_this n] (nth container n)) 26 | (-nth [_this n not-found] (nth container n not-found)) 27 | IMeta 28 | (-meta [_this] metadata) 29 | IWithMeta 30 | (-with-meta [_this metadata] (StringTable. container str-table metadata)) 31 | IPrintWithWriter 32 | (-pr-writer [_array writer _opts] 33 | (-write writer (str "#string-table" 34 | (take 20 (seq container))))) 35 | IReduce 36 | (-reduce [this rfn] (-reduce container rfn)) 37 | (-reduce [this rfn acc] (-reduce container rfn acc)) 38 | dt-proto/PElemwiseDatatype 39 | (-elemwise-datatype [_this] :string) 40 | dt-proto/PSubBuffer 41 | (-sub-buffer [_this off len] 42 | (StringTable. (dtype/sub-buffer container off len) 43 | str-table 44 | metadata)) 45 | dt-proto/PListLike 46 | (-add [this val] 47 | (let [val (if val (intern-string val str-table) val)] 48 | (dt-proto/-add container val)) 49 | this) 50 | (-add-all [this data] 51 | (dtype/iterate! #(dt-proto/-add this %) data) 52 | this) 53 | (-ensure-capacity [_this buflen] 54 | (dt-proto/-ensure-capacity container buflen))) 55 | 56 | 57 | (defn make-string-table 58 | [& [strdata]] 59 | (let [retval (StringTable. (dtype/make-list :string) (js/Map.) nil)] 60 | (when strdata (dt-proto/-add-all retval strdata)) 61 | retval)) 62 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/format_sequence.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.format-sequence 2 | "Format a sequence of numbers. We do what we can here...") 3 | 4 | 5 | (defn- left 6 | "What is the power of number" 7 | ^long [^double x] 8 | (-> x Math/abs Math/log10 Math/floor unchecked-long inc)) 9 | 10 | (defn- right 11 | "Calculate maximum digits on the right side of the dot." 12 | ^long [^double x ^long max-digits] 13 | (let [orig-x (Math/abs x)] 14 | (loop [n-pow 1.0 15 | idx 0] 16 | (let [x (* orig-x n-pow)] 17 | (if (or (>= idx max-digits) 18 | (= (Math/floor x) x)) 19 | idx 20 | (recur (* n-pow 10.0) 21 | (unchecked-inc idx))))))) 22 | 23 | ;; public functions 24 | 25 | (defn formatter 26 | "Create formatter for given: 27 | * `xs` - sequence of doubles 28 | * `digits` - maximum precision 29 | * `threshold` - what is absolute power to switch to scientific notation 30 | Returns formatter." 31 | ([xs] (formatter xs 8)) 32 | ([xs ^long digits] (formatter xs digits 8)) 33 | ([xs ^long digits ^long threshold] 34 | (let [xs (filter #(and (number? %) (js/isFinite %)) xs) 35 | max-left (apply max 0 (map left xs)) 36 | max-right (apply max 0 (map #(right % digits) xs)) 37 | e? (> max-left threshold) 38 | format-fn (if e? 39 | #(.toExponential ^double %) 40 | #(.toFixed ^double % max-right))] 41 | (fn [x] 42 | (let [^double x (js/parseFloat (or x ##NaN))] 43 | (if (js/isFinite x) 44 | (format-fn x) 45 | (cond 46 | (== ##Inf x) "Inf" 47 | (== ##-Inf x) "-Inf" 48 | :else "NaN"))))))) 49 | 50 | 51 | (defn format-sequence 52 | "Format sequence of double for given: 53 | * `xs` - sequence of doubles 54 | * `digits` - maximum precision 55 | * `threshold` - what is absolute power to switch to scientific notation 56 | Returns sequence of strings." 57 | ([xs] (format-sequence xs 8)) 58 | ([xs ^long digits] (format-sequence xs digits 8)) 59 | ([xs ^long digits ^long threshold] 60 | (let [fmt (formatter xs digits threshold)] 61 | (map fmt xs)))) 62 | -------------------------------------------------------------------------------- /src/tech/v3/libs/cljs_ajax.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.libs.cljs-ajax 2 | "Bindings to use the dataset handlers in cljs GET/POST calls." 3 | (:require [ajax.core :as ajax] 4 | [ajax.interceptors :as ajax-interceptors] 5 | [ajax.transit :as ajax-transit] 6 | [tech.v3.dataset :as ds] 7 | [tech.v3.datatype.datetime :as dtype-dt] 8 | [java.time :refer [LocalDate Instant]] 9 | [cognitect.transit :as t])) 10 | 11 | 12 | (defonce write-handlers* (atom (ds/transit-write-handler-map))) 13 | (defonce read-handlers* (atom (ds/transit-read-handler-map))) 14 | 15 | 16 | (defn add-transit-io-handlers! 17 | [datatype tag read-fn write-fn] 18 | (swap! write-handlers* assoc datatype (t/write-handler (constantly tag) write-fn)) 19 | (swap! read-handlers* assoc tag read-fn)) 20 | 21 | 22 | (defn add-java-time-handlers! 23 | "Add handlers for java.time.LocalDate and java.time.Instant" 24 | [] 25 | (add-transit-io-handlers! LocalDate "java.time.LocalDate" 26 | dtype-dt/epoch-days->local-date 27 | dtype-dt/local-date->epoch-days) 28 | (add-transit-io-handlers! Instant "java.time.Instant" 29 | dtype-dt/epoch-milliseconds->instant 30 | dtype-dt/instant->epoch-milliseconds)) 31 | 32 | 33 | (defn writer 34 | "Transit writer used for writing transit-json datasets and uses 35 | `ds/transit-write-handler-map`" 36 | [] 37 | (t/writer :json {:handlers @write-handlers*})) 38 | 39 | 40 | (defn response-format 41 | "cljs-ajax interceptor that hardwires content-type to application/transit+json 42 | and uses `ds/transit-read-handler-map`." 43 | [& [content-type]] 44 | (ajax-interceptors/map->ResponseFormat 45 | {:content-type (or content-type ["application/transit+json"]) 46 | :description "Transit response" 47 | :read (ajax-transit/transit-read-fn {:handlers @read-handlers*})})) 48 | 49 | 50 | (defn opt-map 51 | "Options map that must be included in the cljs-ajax request in order 52 | to activate dataset->transit pathways." 53 | [] 54 | {:writer (writer) :response-format (response-format)}) 55 | 56 | 57 | (defn transit-request 58 | "Perform a cljs-ajax request using the select cljs-ajax.core method and merging 59 | [[opt-map]] first into the options." 60 | [method url options] 61 | (method url (merge (opt-map) options))) 62 | 63 | 64 | (defn GET 65 | "Drop in replacement for cljs-ajax.core/GET" 66 | [url options] (transit-request ajax/GET url options)) 67 | 68 | 69 | (defn POST 70 | "Drop in replacement for cljs-ajax.core/POST" 71 | [url options] (transit-request ajax/POST url options)) 72 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/casting.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.casting 2 | (:require [clojure.set :as set])) 3 | 4 | (def type-graph-data 5 | [[:boolean :int8 :uint8] 6 | [:int8 :int16] 7 | [:uint8 :int16 :uint16] 8 | [:int16 :int32 :float32] 9 | [:uint16 :int32 :uint32 :float32] 10 | [:int32 :float64] 11 | [:uint32 :float64] 12 | [:float32 :float64] 13 | [:float64 :object] 14 | ;;int64 doesn't exist in js land so these are auto-promoted to object 15 | ;;containers 16 | [:int64 :object] 17 | [:uint64 :object]]) 18 | 19 | 20 | (def datatype-vec [:boolean :int8 :uint8 :int16 :uint16 :int32 :uint32 21 | :float32 :float64 :int64 :uint64 :object]) 22 | 23 | (def datatype-rank 24 | (->> datatype-vec 25 | (map-indexed vector) 26 | (map (comp vec reverse)) 27 | (into {}))) 28 | 29 | 30 | (defn smallest-datatype 31 | [dt-seq] 32 | (reduce (fn [lhs rhs] 33 | (if (< (datatype-rank lhs) 34 | (datatype-rank rhs)) 35 | lhs 36 | rhs)) 37 | dt-seq)) 38 | 39 | 40 | (def type-graph 41 | (reduce (fn [tg entry] 42 | (let [new-dt (first entry) 43 | valid-types (set entry) 44 | tg (update tg new-dt set/union valid-types)] 45 | (->> tg 46 | (map (fn [[k v]] 47 | [k (if (v new-dt) 48 | (set/union v valid-types) 49 | v)])) 50 | (into {})))) 51 | {} 52 | type-graph-data)) 53 | 54 | 55 | (defn widest-datatype 56 | ([dtype] dtype) 57 | ([lhs-dtype rhs-dtype] 58 | (if (= lhs-dtype rhs-dtype) 59 | lhs-dtype 60 | (let [lhs-types (get type-graph lhs-dtype #{:object}) 61 | rhs-types (type-graph rhs-dtype #{:object}) 62 | res (set/intersection lhs-types rhs-types) 63 | n-res (count res)] 64 | (cond 65 | (= 0 n-res) 66 | :object 67 | (= 1 n-res) 68 | (first res) 69 | :else 70 | (smallest-datatype res)))))) 71 | 72 | 73 | (def numeric-types (disj (set (map first type-graph-data)) :boolean)) 74 | 75 | 76 | (defn numeric-type? 77 | [dtype] 78 | (boolean (numeric-types dtype))) 79 | 80 | 81 | (defn ->bigint 82 | [data] 83 | (if (instance? js/BigInt data) data (js/BigInt data))) 84 | 85 | 86 | (defn ->number 87 | [data] 88 | (if (instance? js/Number data) 89 | data 90 | (js/Number data))) 91 | 92 | 93 | (defn cast-fn 94 | [dtype] 95 | (cond 96 | (#{:int64 :uint64} dtype) 97 | ->bigint 98 | (numeric-type? dtype) 99 | ->number 100 | :else 101 | identity)) 102 | -------------------------------------------------------------------------------- /testapp/src/testapp/webapp.cljs: -------------------------------------------------------------------------------- 1 | (ns testapp.webapp 2 | (:require [goog.dom :as gdom] 3 | [reagent.dom] 4 | [reagent.ratom :refer [atom]] 5 | [tech.v3.libs.cljs-ajax :refer [GET POST] :as tech-cljs-ajax] 6 | [tech.v3.dataset :as ds] 7 | [tech.v3.datatype.functional :as dfn])) 8 | 9 | 10 | (defonce app* (atom {})) 11 | 12 | 13 | (defn merge-by-key 14 | [lhs rhs colname] 15 | (->> (concat lhs rhs) 16 | (sort-by :time) 17 | (dedupe))) 18 | 19 | 20 | (defn home 21 | [] 22 | (GET "/data" {:handler #(swap! app* assoc 23 | :ds % 24 | :raw (mapv (partial into {}) (ds/rows %)))}) 25 | (fn [] 26 | [:div.container 27 | [:h1 "Hello from TMDJS"] 28 | [:div 29 | (if-let [ds (:ds @app*)] 30 | (let [raw (:raw @app*)] 31 | [:div 32 | [:div "Dataset" 33 | [:div [:pre (pr-str ds)]]] 34 | [:div "Some Timings"] 35 | [:table 36 | [:thead 37 | [:tr [:th "Operation"] [:th "Mapseq"] [:th "Dataset"]]] 38 | [:tbody 39 | [:tr [:td "Sort by time"] 40 | [:td (with-out-str (time (sort-by :time raw)))] 41 | [:td (with-out-str (time (ds/sort-by-column ds :time)))]] 42 | [:tr [:td "Merge by time"] 43 | [:td (with-out-str (time (count (merge-by-key raw raw :time))))] 44 | [:td (with-out-str (time (ds/row-count (ds/merge-by-column ds ds :time))))]] 45 | [:tr [:td "Serialize to transit"] 46 | [:td (with-out-str (time (ds/dataset->transit-str raw)))] 47 | [:td (with-out-str (time (ds/dataset->transit-str ds)))]] 48 | [:tr [:td "transit size (bytes)"] 49 | [:td (count (ds/dataset->transit-str raw))] 50 | [:td (count (ds/dataset->transit-str ds))]] 51 | [:tr [:td "Average temp"] 52 | [:td (with-out-str (time (dfn/mean (map :temp raw))))] 53 | [:td (with-out-str (time (dfn/mean (ds :temp))))]] 54 | [:tr [:td "Filter valid"] 55 | [:td (with-out-str (time (count (filter :valid? raw))))] 56 | [:td (with-out-str (time (ds/row-count (ds/filter-column ds :valid? identity))))]] 57 | [:tr [:td "Filter temps > 0.5"] 58 | [:td (with-out-str (time (count (filter #(> (:temp %) 0.5) raw))))] 59 | [:td (with-out-str (time (count (ds/filter-column ds :temp #(> % 0.5)))))]] 60 | ]]]) 61 | [:div "Downloading dataset"])]])) 62 | 63 | 64 | (defn render 65 | [] 66 | (reagent.dom/render [home] (gdom/getElement "app"))) 67 | 68 | (defn ^:dev/after-load clear-cache-and-render! 69 | [] 70 | (render)) 71 | 72 | (defn init 73 | "Entrypoint into the application." 74 | [] 75 | (tech-cljs-ajax/add-java-time-handlers!) 76 | 77 | (render)) 78 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/bitmap.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.bitmap 2 | "set implementation specialized towards unsigned 32 bit integers." 3 | (:require [tech.v3.datatype.protocols :as dt-proto] 4 | [tech.v3.datatype.base :as dt-base] 5 | [tech.v3.datatype.list :as dt-list] 6 | [clojure.set :as set])) 7 | 8 | 9 | (extend-type js/Set 10 | ICounted 11 | (-count [s] (aget s "size")) 12 | ICloneable 13 | (-clone [s] (js/Set. s))) 14 | 15 | 16 | (defn ->iterable 17 | "make something iterable" 18 | [data] 19 | (if (aget data "values") 20 | data 21 | (clj->js data))) 22 | 23 | 24 | (defn as-iterable 25 | [data] 26 | (when (aget data "values") data)) 27 | 28 | 29 | (defn ->js-set 30 | ([] (js/Set.)) 31 | ([data] 32 | (cond 33 | (nil? data) 34 | (->js-set) 35 | (instance? js/Set data) 36 | data 37 | :else 38 | (reduce #(do (.add ^JS %1 %2) %1) 39 | (js/Set.) 40 | data)))) 41 | 42 | 43 | (defn ->bitmap 44 | "compat with jvm" 45 | ([] (->js-set)) 46 | ([data] (->js-set data))) 47 | 48 | 49 | (extend-type js/Set 50 | dt-proto/PBitmapSet 51 | (-set-or [lhs rhs] 52 | (let [rhs (->iterable rhs) 53 | retval (js/Set. lhs)] 54 | (dt-base/iterate! #(.add retval %) rhs) 55 | retval)) 56 | (-set-and [lhs rhs] 57 | (let [rhs (->iterable rhs) 58 | retval (js/Set.)] 59 | (dt-base/iterate! #(when (.has lhs %) 60 | (.add retval %)) 61 | rhs) 62 | retval)) 63 | (-set-and-not [lhs rhs] 64 | (let [retval (js/Set. lhs)] 65 | (dt-base/iterate! #(.delete retval %) rhs) 66 | retval)) 67 | (-set-offset [lhs off] 68 | (let [retval (js/Set.)] 69 | (dt-base/iterate! #(.add retval (+ % off)) lhs) 70 | retval))) 71 | 72 | 73 | ;;These work but are slower than their clojurescript implementations. 74 | (extend-type PersistentHashSet 75 | dt-proto/PBitmapSet 76 | (-set-or [lhs rhs] (set/union lhs rhs)) 77 | (-set-and [lhs rhs] (set/intersection lhs rhs)) 78 | (-set-and-not [lhs rhs] (set/difference lhs rhs)) 79 | (-set-offset [lhs off] 80 | (let [retval (js/Set.)] 81 | (dt-base/iterate! #(.add retval (+ % off)) lhs) 82 | retval))) 83 | 84 | 85 | (defn set->ordered-indexes 86 | "Return the value in the set in an int32 array ordered from least to greatest." 87 | [data] 88 | (let [indexes (dt-list/make-list :int32 (count data)) 89 | buffer (dt-base/as-agetable indexes)] 90 | (dt-base/iterate! #(dt-proto/-add indexes %) data) 91 | (.sort buffer) 92 | indexes)) 93 | 94 | 95 | (defn bitmap->efficient-random-access-reader 96 | "old name for [[set->ordered-indexes]]" 97 | [bitmap] 98 | (set->ordered-indexes bitmap)) 99 | 100 | 101 | (defn js-set->clj 102 | [js-set] 103 | (let [values (.values js-set)] 104 | (loop [retval (transient (set nil))] 105 | (let [next-val (.next values)] 106 | (if (.-done next-val) 107 | (persistent! retval) 108 | (recur (conj! retval (.-value next-val)))))))) 109 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/protocols.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.protocols) 2 | 3 | 4 | (defprotocol PDatatype 5 | (-datatype [item])) 6 | 7 | 8 | (extend-protocol PDatatype 9 | object 10 | (-datatype [item] :object) 11 | Keyword 12 | (-datatype [item] :keyword) 13 | string 14 | (-datatype [item] :string) 15 | boolean 16 | (-datatype [item] :boolean) 17 | number 18 | (-datatype [item] :float64) 19 | PersistentArrayMap 20 | (-datatype [item] :persistent-map) 21 | PersistentHashMap 22 | (-datatype [item] :persistent-map) 23 | PersistentVector 24 | (-datatype [item] :persistent-vector) 25 | js/Date 26 | (-datatype [item] :js-date) 27 | UUID 28 | (-datatype [item] :uuid)) 29 | 30 | 31 | (defprotocol PElemwiseDatatype 32 | (-elemwise-datatype [item])) 33 | 34 | 35 | (extend-protocol PElemwiseDatatype 36 | object 37 | (-elemwise-datatype [item] :object) 38 | string 39 | (-elemwise-datatype [item] (-datatype item)) 40 | boolean 41 | (-elemwise-datatype [item] (-datatype item)) 42 | number 43 | (-elemwise-datatype [item] (-datatype item))) 44 | 45 | 46 | (defprotocol PShape 47 | (-shape [item])) 48 | 49 | 50 | (extend-type array 51 | PElemwiseDatatype 52 | (-elemwise-datatype [item] :object) 53 | PDatatype 54 | (-datatype [item] :array)) 55 | 56 | 57 | (extend-type array 58 | PDatatype 59 | (-datatype [item] :object)) 60 | 61 | 62 | (defprotocol PSubBufferCopy 63 | (-sub-buffer-copy [item offset length])) 64 | 65 | (defprotocol PSubBuffer 66 | (-sub-buffer [item offset length])) 67 | 68 | 69 | (defprotocol PToTypedArray 70 | (-convertible-to-typed-array? [buf]) 71 | (->typed-array [buf])) 72 | 73 | 74 | (extend-type object 75 | PToTypedArray 76 | (-convertible-to-typed-array? [buf] false)) 77 | 78 | (extend-type array 79 | PToTypedArray 80 | (-convertible-to-typed-array? [buf] false)) 81 | 82 | 83 | (defprotocol PToJSArray 84 | (-convertible-to-js-array? [buf]) 85 | (->js-array [buf])) 86 | 87 | 88 | (extend-type object 89 | PToJSArray 90 | (-convertible-to-js-array? [buf] false)) 91 | 92 | 93 | (defprotocol PAgetable 94 | (-convertible-to-agetable? [buf]) 95 | (->agetable [buf])) 96 | 97 | 98 | (extend-protocol PAgetable 99 | object 100 | (-convertible-to-agetable? [buf] (or (-convertible-to-js-array? buf) 101 | (-convertible-to-typed-array? buf))) 102 | (->agetable [buf] (cond 103 | (-convertible-to-js-array? buf) (->js-array buf) 104 | (-convertible-to-typed-array? buf) (->typed-array buf) 105 | :else 106 | (throw (js/Error. "Object is not aget-able")))) 107 | array 108 | (-convertible-to-agetable? [buf] true) 109 | (->agetable [buf] buf)) 110 | 111 | 112 | (defprotocol PSetValue 113 | (-set-value! [item idx data])) 114 | 115 | 116 | (defprotocol PSetConstant 117 | (-set-constant! [item offset elem-count data])) 118 | 119 | 120 | (defprotocol PListLike 121 | (-add [this elem]) 122 | (-add-all [this container]) 123 | (-ensure-capacity [this capacity])) 124 | 125 | 126 | (defprotocol PBitmapSet 127 | (-set-or [lhs rhs]) 128 | (-set-and [lhs rhs]) 129 | (-set-and-not [lhs rhs]) 130 | (-set-offset [this off])) 131 | 132 | 133 | (defprotocol PFastAccessor 134 | (->fast-nth [item])) 135 | 136 | (extend-protocol PFastAccessor 137 | object 138 | (->fast-nth [item] 139 | (if (-convertible-to-agetable? item) 140 | (let [ai (->agetable item)] 141 | #(aget ai %)) 142 | #(nth item %))) 143 | array 144 | (->fast-nth [item] #(aget item %))) 145 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/emap1_vec.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.emap1-vec 2 | (:require [tech.v3.datatype.copy-make-container :as dt-cmc] 3 | [tech.v3.datatype.arrays :as dt-arrays] 4 | [tech.v3.datatype.base :as dt-base] 5 | [tech.v3.datatype.protocols :as dt-proto] 6 | [ham-fisted.api :as hamf] 7 | [ham-fisted.lazy-noncaching :as lznc])) 8 | 9 | 10 | (declare emap1-vec) 11 | 12 | 13 | (deftype Emap1Vec [meta map-fn dtype src ^:mutable __hash] 14 | Object 15 | (toString [coll] 16 | (pr-str* coll)) 17 | (equiv [this other] 18 | (-equiv this other)) 19 | (indexOf [coll x] 20 | (-indexOf coll x 0)) 21 | (indexOf [coll x start] 22 | (-indexOf coll x start)) 23 | (lastIndexOf [coll x] 24 | (-lastIndexOf coll x (count coll))) 25 | (lastIndexOf [coll x start] 26 | (-lastIndexOf coll x start)) 27 | 28 | ICloneable 29 | (-clone [this] (dt-cmc/make-container dtype this)) 30 | 31 | IWithMeta 32 | (-with-meta [coll new-meta] 33 | (if (identical? new-meta meta) 34 | coll 35 | (Emap1Vec. meta map-fn dtype src new-meta))) 36 | 37 | IMeta 38 | (-meta [_coll] meta) 39 | 40 | ISequential 41 | IEquiv 42 | (-equiv [coll other] 43 | (dt-arrays/equiv-nthable coll other)) 44 | 45 | IHash 46 | (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) 47 | 48 | ISeqable 49 | (-seq [coll] 50 | (seq (lznc/map map-fn src))) 51 | 52 | ICounted 53 | (-count [_coll] (count src)) 54 | 55 | IIndexed 56 | (-nth [_coll n] 57 | (let [l (count src) 58 | n (if (< n 0) (+ n l) n)] 59 | (when-not (< n l) (throw (js/Error. (str "nth out of range:" n " >= " l)))) 60 | (map-fn (-nth src n)))) 61 | (-nth [_coll n not-found] 62 | (let [l (count src) 63 | n (if (< n 0) (+ n l) n)] 64 | (map-fn (-nth src n)) 65 | not-found)) 66 | 67 | IReduce 68 | (-reduce [this rfn] (-reduce src #(rfn %1 (map-fn %2)))) 69 | (-reduce [this rfn acc] (-reduce src #(rfn %1 (map-fn %2)) acc)) 70 | 71 | ILookup 72 | (-lookup [coll k] (-lookup coll k nil)) 73 | (-lookup [coll k not-found] (if (number? k) 74 | (-nth coll k not-found) 75 | not-found)) 76 | 77 | IFind 78 | (-find [_coll n] 79 | (let [length (count src) 80 | n (if (< n 0) (+ length n) n)] 81 | (when (and (<= 0 n) (< n length)) 82 | (MapEntry. n (-nth _coll n) nil)))) 83 | 84 | 85 | IKVReduce 86 | (-kv-reduce [_v f init] 87 | (-reduce #(f %1 %2 (map-fn %2)) 88 | init 89 | src)) 90 | 91 | IFn 92 | (-invoke [coll k] 93 | (-nth coll k)) 94 | (-invoke [coll k not-found] 95 | (-nth coll k not-found)) 96 | 97 | IReversible 98 | (-rseq [coll] 99 | (seq (lznc/map map-fn (-rseq src)))) 100 | 101 | IIterable 102 | (-iterator [this] 103 | (dt-arrays/nth-iter this)) 104 | 105 | IPrintWithWriter 106 | (-pr-writer [rdr writer _opts] 107 | (-write writer (dt-base/reader->str rdr "reader"))) 108 | 109 | dt-proto/PDatatype 110 | (-datatype [_this] :reader) 111 | dt-proto/PElemwiseDatatype 112 | (-elemwise-datatype [_this] dtype) 113 | dt-proto/PSubBufferCopy 114 | (-sub-buffer-copy [this off len] 115 | (dt-proto/-sub-buffer this off len)) 116 | dt-proto/PSubBuffer 117 | (-sub-buffer [_this off len] 118 | (emap1-vec dtype map-fn (dt-proto/-sub-buffer src off len) meta))) 119 | 120 | 121 | (defn emap1-vec 122 | ([dtype map-fn data meta] 123 | (Emap1Vec. meta map-fn dtype data nil)) 124 | ([dtype map-fn data] 125 | (emap1-vec dtype map-fn data nil))) 126 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | ;;We serialize datasets to transit-json 3 | :deps {com.cognitect/transit-cljs {:mvn/version "0.8.269"} 4 | techascent/tech.ml.dataset {:mvn/version "7.037"} 5 | tick/tick {:mvn/version "0.5.0"} 6 | com.cognitect/transit-clj {:mvn/version "1.0.324"} 7 | com.cnuernber/ham-scripted {:git/url "https://github.com/cnuernber/ham-scripted" 8 | :sha "36b657d7bdf3d6e168cb083897efbfa9021333da"}} 9 | :aliases 10 | {:dev 11 | {:extra-deps {metosin/muuntaja {:mvn/version "0.6.8"}}} 12 | :cljs 13 | {:extra-deps {thheller/shadow-cljs {:mvn/version "2.12.4"} 14 | cider/cider-nrepl {:mvn/version "0.50.2"} 15 | cljs-ajax/cljs-ajax {:mvn/version "0.8.3"} 16 | kixi/stats {:mvn/version "0.5.5"}} 17 | ;;resources has logback.xml which disables the annoying jboss logging 18 | :extra-paths ["test" "resources"] 19 | :main-opts ["-m" "shadow.cljs.devtools.cli"]} 20 | ;;run with clj -X:depstar 21 | :build 22 | {:deps {io.github.clojure/tools.build {:git/tag "v0.9.6" :git/sha "8e78bcc"}} 23 | :ns-default build} 24 | ;;deploy to clojars 25 | :deploy 26 | {:replace-deps {slipset/deps-deploy {:mvn/version "0.1.5"}} 27 | :exec-fn deps-deploy.deps-deploy/deploy 28 | :exec-args {:installer :remote 29 | :sign-releases? true 30 | :artifact "target/tmdjs.jar"}} 31 | 32 | :install 33 | {:replace-deps {slipset/deps-deploy {:mvn/version "0.1.5"}} 34 | :exec-fn deps-deploy.deps-deploy/deploy 35 | :exec-args {:installer :local 36 | :artifact "target/tmdjs.jar"}} 37 | 38 | ;;used for installing base package.json 39 | ;;clj -M:cljs-install -m cljs.main --install-deps 40 | :cljs-install 41 | {:extra-deps {thheller/shadow-cljs {:mvn/version "2.12.4"}}} 42 | 43 | ;;Writes docs into docs/index.html 44 | ;;clj -X:cljs-docs 45 | :codox 46 | {:extra-deps {codox-theme-rdash/codox-theme-rdash {:mvn/version "0.1.2"} 47 | com.cnuernber/codox {:mvn/version "1.000"} 48 | thheller/shadow-cljs {:mvn/version "2.12.4"} 49 | cider/cider-nrepl {:mvn/version "0.26.0"} 50 | cljs-ajax/cljs-ajax {:mvn/version "0.8.3"}} 51 | :extra-paths ["test" "resources"] 52 | :exec-fn codox.main/-main 53 | :exec-args {:arg-paths [[:aliases :depstar :exec-args]] 54 | :description "Dataframe processing for ClojureScript" 55 | :group-id "com.cnuernber" 56 | :artifact-id "tmdjs" 57 | :metadata {:doc/format :markdown} 58 | :version "2.002" 59 | :google-analytics "G-CLH3CS7E1R" 60 | :html {:transforms [[:head] [:append [:script {:async true 61 | :src "https://www.googletagmanager.com/gtag/js?id=G-CLH3CS7E1R"}]] 62 | [:head] [:append [:script "window.dataLayer = window.dataLayer || []; 63 | function gtag(){dataLayer.push(arguments);} 64 | gtag('js', new Date()); 65 | 66 | gtag('config', 'G-CLH3CS7E1R');"]]]} 67 | :themes [:rdash] 68 | :language :clojurescript 69 | :source-paths ["src"] 70 | :output-path "docs" 71 | :doc-paths ["topics"] 72 | :source-uri "https://github.com/cnuernber/tmdjs/blob/master/{filepath}#L{line}" 73 | :namespaces [tech.v3.dataset 74 | tech.v3.dataset.node 75 | tech.v3.dataset.protocols 76 | tech.v3.libs.cljs-ajax 77 | tech.v3.datatype 78 | tech.v3.datatype.functional 79 | tech.v3.datatype.argops]}}}} 80 | -------------------------------------------------------------------------------- /testapp/src/testapp/main.clj: -------------------------------------------------------------------------------- 1 | (ns testapp.main 2 | (:require [org.httpkit.server :as server] 3 | [hiccup.page :as hiccup] 4 | [ring.util.response :as response] 5 | [ring.middleware.resource :refer [wrap-resource]] 6 | [ring.middleware.content-type :refer [wrap-content-type]] 7 | ;;automatic handling of transit->dataset conversion 8 | [tech.v3.libs.muuntaja :refer [wrap-format-java-time]] 9 | [ring.middleware.cookies :refer [wrap-cookies]] 10 | [ring.middleware.gzip :refer [wrap-gzip]] 11 | [bidi.ring :as bidi-ring] 12 | [clojure.tools.logging :as log] 13 | [tech.v3.dataset :as ds] 14 | [tech.v3.libs.transit :as tech-transit]) 15 | (:import [java.time LocalDate Instant]) 16 | (:gen-class)) 17 | 18 | 19 | (defn- home-page 20 | [request] 21 | (-> (hiccup/html5 22 | {:lang "en"} 23 | [:head 24 | [:meta {:charset "utf-8"}] 25 | [:title "TMD For CLJS"] 26 | [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] 27 | [:meta {:name "description" :content "Dataframes for cljs"}] 28 | [:link {:href "//fonts.googleapis.com/css?family=Raleway:400,300,600" 29 | :rel "stylesheet" 30 | :type "text/css"}] 31 | [:link {:rel "stylesheet" :href "css/normalize.css"}] 32 | [:link {:rel "stylesheet" :href "css/skeleton.css"}] 33 | [:body [:div#app]] 34 | [:script {:src "js/app.js" :type "text/javascript"}]]) 35 | (response/response) 36 | (response/header "Content-Type" "text/html"))) 37 | 38 | 39 | (defn generate-data 40 | [request] 41 | (-> (ds/->dataset (repeatedly 10000 #(hash-map :time (rand) 42 | :temp (rand) 43 | :temp1 (rand) 44 | :temp2 (rand) 45 | :valid? (if (> (rand) 0.5) 46 | true 47 | false)))) 48 | (response/response))) 49 | 50 | 51 | (defn datetest 52 | [request] 53 | (response/response {:local-date (LocalDate/now) 54 | :instant (Instant/now)})) 55 | 56 | 57 | (defn stocks-data 58 | [request] 59 | (-> (ds/->dataset "https://github.com/techascent/tech.ml.dataset/raw/master/test/data/stocks.csv" {:key-fn keyword}) 60 | (response/response))) 61 | 62 | 63 | (def routes ["/" {:get [["data" #'generate-data] 64 | ["stocks" #'stocks-data] 65 | ["datetest" #'datetest] 66 | [true #'home-page]]}]) 67 | 68 | (defn handler 69 | [] 70 | (-> (bidi-ring/make-handler routes) 71 | (wrap-format-java-time) 72 | (wrap-cookies) 73 | (wrap-resource "public") 74 | (wrap-content-type) 75 | (wrap-gzip))) 76 | 77 | 78 | (defonce ^:private server* (atom nil)) 79 | 80 | 81 | (defn start-server 82 | ([{:keys [port] 83 | :or {port 3000} 84 | :as options}] 85 | (swap! server* 86 | (fn [existing-server] 87 | (if existing-server 88 | (do 89 | (log/infof "Restarting server on port %d" port) 90 | (existing-server)) 91 | (log/infof "Starting server on port %d" port)) 92 | (server/run-server (handler) 93 | (merge {:port port} 94 | options))))) 95 | ([] 96 | (start-server nil))) 97 | 98 | 99 | (defn -main 100 | [& args] 101 | (start-server) 102 | (log/infof "Main function exiting-server still running")) 103 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/functional.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.functional 2 | "Simple math primitives." 3 | (:require [tech.v3.datatype :as dtype] 4 | [tech.v3.datatype.statistics :as stats])) 5 | 6 | 7 | (defn reduce-min 8 | "Nan-unaware min. tech.v3.datatype.statistics/min is nan-aware" 9 | [v] 10 | (reduce min v)) 11 | 12 | 13 | (defn reduce-max 14 | "Nan-unaware max. tech.v3.datatype.statistics/max is nan-aware" 15 | [v] 16 | (reduce max v)) 17 | 18 | 19 | (defn sum 20 | "Nan-aware sum. Nan's will be skipped." 21 | [v] 22 | (stats/sum v)) 23 | 24 | (defn mean 25 | "Nan-aware mean. Nan's will be skipped." 26 | [v] 27 | (stats/mean v)) 28 | 29 | (defn variance 30 | "Nan-aware variance. Nan's will be skipped." 31 | [v] 32 | (stats/variance v)) 33 | 34 | (defn standard-deviation 35 | "Nan-aware standard-deviation. Nan's will be skipped." 36 | [v] (stats/standard-deviation v)) 37 | 38 | (defn percentiles 39 | "Percentiles are given in whole numbers: 40 | 41 | ```clojure 42 | tech.v3.datatype.functional> (percentiles [0 25 50 75 100] (range 10)) 43 | [0.0 1.75 4.5 7.25 9.0] 44 | ```" 45 | ([percentages options v] (stats/percentiles percentages options v)) 46 | ([percentages v] (stats/percentiles percentages v))) 47 | 48 | 49 | (defn median 50 | "Nan-aware median. Nan's will be skipped." 51 | ([options v] (stats/median options v)) 52 | ([v] (stats/median v))) 53 | 54 | 55 | (defn quartiles 56 | "return [min, 25 50 75 max] of item" 57 | ([item] 58 | (stats/quartiles item)) 59 | ([options item] 60 | (stats/quartiles options item))) 61 | 62 | 63 | (defn descriptive-statistics 64 | "Given a sequence of desired stats return a map of statname->value. 65 | 66 | Example: 67 | 68 | ```clojure 69 | cljs.user> (dfn/descriptive-statistics [:min :max :mean :n-values] (range 10)) 70 | {:min 0, :max 9, :mean 4.5, :n-values 10} 71 | ```" 72 | [stats v] 73 | (stats/descriptive-statistics stats v)) 74 | 75 | 76 | (defn magnitude-squared 77 | "Magnitude-squared of the vector" 78 | [v] 79 | (->> (dtype/emap #(* % %) :float64 v) 80 | (sum))) 81 | 82 | 83 | (defn magnitude 84 | "Magnitude of the vector" 85 | [v] 86 | (Math/sqrt (magnitude-squared v))) 87 | 88 | 89 | (defn distance-squared 90 | "Euclidian distance squared between x,y" 91 | [x y] 92 | (when-not (== (count x) (count y)) 93 | (throw (js/Error. "X,y have different lengths"))) 94 | (let [xlen (count x)] 95 | (if (== 0 xlen) 96 | 0 97 | (->> (dtype/emap #(let [v (- %1 %2)] 98 | (* v v)) 99 | :float64 100 | x y) 101 | (sum))))) 102 | 103 | 104 | (defn distance 105 | "Euclidian distance between x,y" 106 | [x y] 107 | (Math/sqrt (distance-squared x y))) 108 | 109 | 110 | (defn equals 111 | "Numeric equals - the distance between x,y must be less than error-bar which defaults 112 | to 0.001." 113 | [lhs rhs & [error-bar]] 114 | (cljs.core/< (double (distance lhs rhs)) 115 | (double (cljs.core/or error-bar 0.001)))) 116 | 117 | 118 | (defn scalar-eq 119 | "NaN-aware eq" 120 | [lhs rhs] 121 | (let [l-nan? (js/isNaN lhs) 122 | r-nan? (js/isNaN rhs)] 123 | (cond 124 | (and l-nan? r-nan?) true 125 | (or l-nan? r-nan?) false 126 | :else 127 | (-equiv lhs rhs)))) 128 | 129 | 130 | (defn shift 131 | "Shift by n and fill in with the first element for n>0 or last element for n<0. 132 | 133 | Examples: 134 | 135 | ```clojure 136 | user> (dfn/shift (range 10) 2) 137 | [0 0 0 1 2 3 4 5 6 7] 138 | user> (dfn/shift (range 10) -2) 139 | [2 3 4 5 6 7 8 9 9 9] 140 | ```" 141 | [rdr n] 142 | (let [n-elems (count rdr) 143 | max-idx (dec n-elems) 144 | n (long n)] 145 | (dtype/reify-reader (dtype/elemwise-datatype rdr) 146 | n-elems 147 | (fn [idx] 148 | (nth rdr (max 0 (min max-idx (- idx n)))))))) 149 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks 2 | {:macroexpand {tech.v3.datatype/make-reader 3 | tech.v3.datatype/make-reader 4 | tech.v3.datatype-api/make-reader 5 | tech.v3.datatype-api/make-reader 6 | tech.v3.datatype.export-symbols/export-symbols 7 | tech.v3.datatype.export-symbols/export-symbols 8 | tech.v3.datatype.binary-op/make-numeric-object-binary-op 9 | tech.v3.datatype.binary-op/make-numeric-object-binary-op 10 | tech.v3.datatype.binary-op/make-float-double-binary-op 11 | tech.v3.datatype.binary-op/make-float-double-binary-op 12 | tech.v3.datatype.binary-op/make-int-long-binary-op 13 | tech.v3.datatype.binary-op/make-int-long-binary-op 14 | tech.v3.datatype.binary-pred/make-boolean-predicate 15 | tech.v3.datatype.binary-pred/make-boolean-predicate 16 | tech.v3.datatype.binary-pred/make-numeric-binary-predicate 17 | tech.v3.datatype.binary-pred/make-numeric-binary-predicate 18 | tech.v3.parallel.for/doiter tech.v3.parallel.for/doiter 19 | tech.v3.parallel.for/parallel-for tech.v3.parallel.for/parallel-for 20 | tech.v3.datatype.ffi/define-library! 21 | tech.v3.datatype.ffi/define-library! 22 | tech.v3.datatype.statistics/define-descriptive-stats 23 | tech.v3.datatype.statistics/define-descriptive-stats 24 | tech.v3.datatype.functional-api/implement-arithmetic-operations 25 | tech.v3.datatype.functional-api/implement-arithmetic-operations 26 | tech.v3.datatype.functional-api/implement-unary-predicates 27 | tech.v3.datatype.functional-api/implement-unary-predicates 28 | tech.v3.datatype.functional-api/implement-binary-predicates 29 | tech.v3.datatype.functional-api/implement-binary-predicates 30 | tech.v3.datatype.functional-api/implement-compare-predicates 31 | tech.v3.datatype.functional-api/implement-compare-predicates 32 | tech.v3.datatype.gradient/append-diff 33 | tech.v3.datatype.gradient/append-diff 34 | tech.v3.datatype.gradient/prepend-diff 35 | tech.v3.datatype.gradient/prepend-diff 36 | tech.v3.datatype.gradient/basic-diff 37 | tech.v3.datatype.gradient/basic-diff 38 | tech.v3.datatype.jvm-map/bi-consumer 39 | tech.v3.datatype.jvm-map/bi-consumer 40 | tech.v3.datatype.jvm-map/bi-function 41 | tech.v3.datatype.jvm-map/bi-function 42 | tech.v3.datatype.jvm-map/function 43 | tech.v3.datatype.jvm-map/function 44 | taoensso.nippy/extend-freeze 45 | taoensso.nippy/extend-freeze 46 | taoensso.nippy/extend-thaw 47 | taoensso.nippy/extend-thaw 48 | tech.v3.datatype.unary-op/make-double-unary-op 49 | tech.v3.datatype.unary-op/make-double-unary-op 50 | tech.v3.datatype.unary-op/make-numeric-object-unary-op 51 | tech.v3.datatype.unary-op/make-numeric-object-unary-op 52 | tech.v3.datatype.unary-op/make-float-double-unary-op 53 | tech.v3.datatype.unary-op/make-float-double-unary-op 54 | tech.v3.datatype.unary-op/make-numeric-unary-op 55 | tech.v3.datatype.unary-op/make-numeric-unary-op 56 | tech.v3.datatype.unary-op/make-long-unary-op 57 | tech.v3.datatype.unary-op/make-long-unary-op 58 | tech.v3.datatype.unary-op/make-all-datatype-unary-op 59 | tech.v3.datatype.unary-op/make-all-datatype-unary-op 60 | tech.v3.tensor-api/typed-compute-tensor 61 | tech.v3.tensor-api/typed-compute-tensor 62 | tech.v3.tensor/typed-compute-tensor 63 | tech.v3.tensor/typed-compute-tensor 64 | }}} 65 | -------------------------------------------------------------------------------- /docs/js/page_effects.js: -------------------------------------------------------------------------------- 1 | function visibleInParent(element) { 2 | var position = $(element).position().top 3 | return position > -50 && position < ($(element).offsetParent().height() - 50) 4 | } 5 | 6 | function hasFragment(link, fragment) { 7 | return $(link).attr("href").indexOf("#" + fragment) != -1 8 | } 9 | 10 | function findLinkByFragment(elements, fragment) { 11 | return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() 12 | } 13 | 14 | function scrollToCurrentVarLink(elements) { 15 | var elements = $(elements); 16 | var parent = elements.offsetParent(); 17 | 18 | if (elements.length == 0) return; 19 | 20 | var top = elements.first().position().top; 21 | var bottom = elements.last().position().top + elements.last().height(); 22 | 23 | if (top >= 0 && bottom <= parent.height()) return; 24 | 25 | if (top < 0) { 26 | parent.scrollTop(parent.scrollTop() + top); 27 | } 28 | else if (bottom > parent.height()) { 29 | parent.scrollTop(parent.scrollTop() + bottom - parent.height()); 30 | } 31 | } 32 | 33 | function setCurrentVarLink() { 34 | $('.secondary a').parent().removeClass('current') 35 | $('.anchor'). 36 | filter(function(index) { return visibleInParent(this) }). 37 | each(function(index, element) { 38 | findLinkByFragment(".secondary a", element.id). 39 | parent(). 40 | addClass('current') 41 | }); 42 | scrollToCurrentVarLink('.secondary .current'); 43 | } 44 | 45 | var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) 46 | 47 | function scrollPositionId(element) { 48 | var directory = window.location.href.replace(/[^\/]+\.html$/, '') 49 | return 'scroll::' + $(element).attr('id') + '::' + directory 50 | } 51 | 52 | function storeScrollPosition(element) { 53 | if (!hasStorage) return; 54 | localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) 55 | localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) 56 | } 57 | 58 | function recallScrollPosition(element) { 59 | if (!hasStorage) return; 60 | $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) 61 | $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) 62 | } 63 | 64 | function persistScrollPosition(element) { 65 | recallScrollPosition(element) 66 | $(element).scroll(function() { storeScrollPosition(element) }) 67 | } 68 | 69 | function sidebarContentWidth(element) { 70 | var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) 71 | return Math.max.apply(Math, widths) 72 | } 73 | 74 | function calculateSize(width, snap, margin, minimum) { 75 | if (width == 0) { 76 | return 0 77 | } 78 | else { 79 | return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) 80 | } 81 | } 82 | 83 | function resizeSidebars() { 84 | var primaryWidth = sidebarContentWidth('.primary') 85 | var secondaryWidth = 0 86 | 87 | if ($('.secondary').length != 0) { 88 | secondaryWidth = sidebarContentWidth('.secondary') 89 | } 90 | 91 | // snap to grid 92 | primaryWidth = calculateSize(primaryWidth, 32, 13, 160) 93 | secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) 94 | 95 | $('.primary').css('width', primaryWidth) 96 | $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) 97 | 98 | if (secondaryWidth > 0) { 99 | $('#content').css('left', primaryWidth + secondaryWidth + 2) 100 | } 101 | else { 102 | $('#content').css('left', primaryWidth + 1) 103 | } 104 | } 105 | 106 | $(window).ready(resizeSidebars) 107 | $(window).ready(setCurrentVarLink) 108 | $(window).ready(function() { persistScrollPosition('.primary')}) 109 | $(window).ready(function() { 110 | $('#content').scroll(setCurrentVarLink) 111 | $(window).resize(setCurrentVarLink) 112 | $(window).resize(resizeSidebars) 113 | }) 114 | -------------------------------------------------------------------------------- /topics/Reductions.md: -------------------------------------------------------------------------------- 1 | # Some Reduction Timings 2 | 3 | 4 | The datatype library has some helpers that work with datasets that can make certain types of 5 | reductions much faster. 6 | 7 | 8 | ## Filter On One Column, Reduce Another 9 | 10 | This is a very common operation so let's take a closer look. The generic dataset 11 | pathway would be: 12 | 13 | ```clojure 14 | cljs.user> (require '[tech.v3.dataset :as ds]) 15 | nil 16 | cljs.user> (def test-ds (ds/->dataset {:a (range 20000) 17 | :b (repeatedly 20000 rand)})) 18 | #'cljs.user/test-ds 19 | cljs.user> ;;filter on a, sum b. 20 | cljs.user> (reduce + 0.0 (-> (ds/filter-column test-ds :a #(> % 10000)) 21 | (ds/column :b))) 22 | 5000.898384571656 23 | cljs.user> (time (dotimes [idx 100] (reduce + 0.0 (-> (ds/filter-column test-ds :a #(> % 10000)) 24 | (ds/column :b))))) 25 | "Elapsed time: 282.714231 msecs" 26 | "Elapsed time: 282.714231 msecs" 27 | "Elapsed time: 282.714231 msecs" 28 | ``` 29 | 30 | Think transducers are fast? What about a generic transducer pathway? 31 | 32 | ```clojure 33 | cljs.user> (let [a (test-ds :a) 34 | b (test-ds :b)] 35 | (transduce (comp (filter #(> (nth a %) 10000)) 36 | (map #(nth b %))) 37 | (completing +) 38 | (range (ds/row-count test-ds)))) 39 | 5000.898384571656 40 | cljs.user> (time (dotimes [idx 100] 41 | (let [a (test-ds :a) 42 | b (test-ds :b)] 43 | (transduce (comp (filter #(> (nth a %) 10000)) 44 | (map #(nth b %))) 45 | (completing +) 46 | (range (ds/row-count test-ds)))))) 47 | "Elapsed time: 436.235972 msecs" 48 | "Elapsed time: 436.235972 msecs" 49 | "Elapsed time: 436.235972 msecs" 50 | nil 51 | ``` 52 | 53 | Transducers are fast - after looking at this pathway we found the 54 | `nth` call is relatively expensive. The datatype library has a way 55 | to get the fastest nth-like access available for a given container. Columns overload 56 | this pathway such that if there are no missing they use the fastest 57 | access for their buffer, else they have to wrap a missing check. Regardless, 58 | this gets us a solid improvement: 59 | 60 | ```clojure 61 | cljs.user> (require '[tech.v3.datatype :as dtype]) 62 | nil 63 | cljs.user> (time (dotimes [idx 100] 64 | (let [a (dtype/->fast-nth (test-ds :a)) 65 | b (dtype/->fast-nth (test-ds :b))] 66 | (transduce (comp (filter #(> (a %) 10000)) 67 | (map #(b %))) 68 | (completing +) 69 | (range (ds/row-count test-ds)))))) 70 | "Elapsed time: 77.823553 msecs" 71 | "Elapsed time: 77.823553 msecs" 72 | "Elapsed time: 77.823553 msecs" 73 | nil 74 | ``` 75 | 76 | OK - there is another more dangerous approach. dtype has another query, 77 | as-agetable, that either returns something for which `aget` works or 78 | nil. If you know your dataset's columns have no missing data and their 79 | backing store data itself is agetable - then you can get an agetable. This 80 | doesn't have a fallback so you risk null ptr issues - but it is the fastest 81 | possible pathway. 82 | 83 | 84 | ```clojure 85 | cljs.user> (time (dotimes [idx 100] 86 | (let [a (dtype/as-agetable (test-ds :a)) 87 | b (dtype/as-agetable (test-ds :b))] 88 | (transduce (comp (filter #(> (aget a %) 10000)) 89 | (map #(aget b %))) 90 | (completing +) 91 | (range (ds/row-count test-ds)))))) 92 | "Elapsed time: 57.404783 msecs" 93 | "Elapsed time: 57.404783 msecs" 94 | "Elapsed time: 57.404783 msecs" 95 | nil 96 | ``` 97 | 98 | 99 | In this simple example we find that a transducing pathway is indeed a quite bit faster but only 100 | when it is coupled with an efficient per-element access pattern. 101 | -------------------------------------------------------------------------------- /docs/tech.v3.dataset.node.html: -------------------------------------------------------------------------------- 1 | 3 | tech.v3.dataset.node documentation

tech.v3.dataset.node

Functions and helpers that require the node runtime.

8 |

transit-file->dataset

(transit-file->dataset fname)

Given a file of transit data return a dataset.

9 |
-------------------------------------------------------------------------------- /src/tech/v3/datatype/statistics.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.statistics 2 | (:require [tech.v3.datatype :as dtype] 3 | [ham-fisted.api :as hamf]) 4 | (:refer-clojure :exclude [min max])) 5 | 6 | 7 | (deftype GenReducer [^:mutable total ^:mutable n-elems reduce-fn nan-behavior] 8 | IFn 9 | (-invoke [this arg] 10 | (let [arg (if (js/isNaN arg) 11 | (nan-behavior arg) 12 | arg)] 13 | (when-not (nil? arg) 14 | (set! total (if (== 0 n-elems) 15 | arg 16 | (reduce-fn total arg))) 17 | (set! n-elems (inc n-elems)))) 18 | this) 19 | IPrintWithWriter 20 | (-pr-writer [this w _options] 21 | (-write w (str "#sum-reducer" @this))) 22 | IDeref 23 | (-deref [_this] 24 | {:sum total 25 | :n-elems n-elems})) 26 | 27 | 28 | (defn ->nan-behavior 29 | [nan-strategy] 30 | (case (or nan-strategy :remove) 31 | :remove (constantly nil) 32 | :keep identity 33 | :exception #(throw (js/Error. "NaN detected in data")))) 34 | 35 | 36 | (defn sum-reducer [& [nan-strategy]] 37 | (GenReducer. 0 0 + (->nan-behavior (or nan-strategy :remove)))) 38 | 39 | 40 | (defn min-reducer [& [nan-strategy]] 41 | (GenReducer. 0 0 cljs.core/min (->nan-behavior (or nan-strategy :remove)))) 42 | 43 | 44 | (defn max-reducer [& [nan-strategy]] 45 | (GenReducer. 0 0 cljs.core/max (->nan-behavior (or nan-strategy :remove)))) 46 | 47 | 48 | (defn sum 49 | [v] 50 | (hamf/sum v)) 51 | 52 | 53 | (defn min 54 | [v] 55 | (->> (hamf/apply-nan-strategy nil v) 56 | (hamf/mmin-key identity))) 57 | 58 | 59 | (defn max 60 | [v] 61 | (->> (hamf/apply-nan-strategy nil v) 62 | (hamf/mmax-key identity))) 63 | 64 | 65 | (defn mean 66 | [v] 67 | (hamf/mean v)) 68 | 69 | 70 | (defn variance 71 | [v] 72 | (hamf/variance v)) 73 | 74 | 75 | (defn standard-deviation 76 | [v] 77 | (hamf/standard-deviation v)) 78 | 79 | 80 | (defn mode 81 | [v] 82 | (hamf/mode v)) 83 | 84 | 85 | (defn- ensure-sorted 86 | "ensure v is sorted returning a typed buffer of data." 87 | [options v] 88 | (let [v (hamf/apply-nan-strategy options v) 89 | tbuf (or (dtype/as-typed-array v) 90 | (-> (dtype/make-container :float64 v) 91 | (dtype/as-typed-array)))] 92 | (.sort tbuf) 93 | tbuf)) 94 | 95 | 96 | (defn percentiles 97 | "Percentiles are given in whole numbers: 98 | 99 | ```clojure 100 | tech.v3.datatype.functional> (percentiles [0 25 50 75 100] (range 10)) 101 | [0.0 1.75 4.5 7.25 9.0] 102 | ```" 103 | ([percentages options v] 104 | (let [v (ensure-sorted options v) 105 | nv (inc (count v))] 106 | (-> (->> percentages 107 | (dtype/emap 108 | (fn [percentage] 109 | (let [percentage (/ percentage 100.0)] 110 | (if (>= percentage 1.0) 111 | (aget v (dec (count v))) 112 | (let [rank (* nv percentage)] 113 | (if (== rank (Math/floor rank)) 114 | (aget v rank) 115 | (let [lrank (Math/floor rank) 116 | leftover (- rank lrank) 117 | lval (aget v (dec lrank)) 118 | diff (- (aget v lrank) 119 | lval)] 120 | (+ lval (* diff leftover)))))))) 121 | :float64) 122 | ;;realize the result 123 | (clone)) 124 | (vary-meta assoc :simple-print? true)))) 125 | ([percentages v] 126 | (percentiles percentages nil v))) 127 | 128 | 129 | (defn median 130 | ([options v] 131 | (nth (percentiles [50] options v) 0)) 132 | ([v] (median nil v))) 133 | 134 | 135 | (defn quartiles 136 | "return [min, 25 50 75 max] of item" 137 | ([item] 138 | (percentiles [0 25 50 75 100] item)) 139 | ([options item] 140 | (percentiles [0 25 50 75 100] options item))) 141 | 142 | 143 | ;;we can worry about a more efficient, fewer-pass pathway later. 144 | (def ^:private stat-fn-map 145 | {:min min 146 | :max max 147 | :sum sum 148 | :mean mean 149 | :median median 150 | :variance variance 151 | :standard-deviation standard-deviation 152 | :n-values #(-> (dtype/iterate! (sum-reducer) %) 153 | (deref) 154 | :n-elems)}) 155 | 156 | 157 | (defn descriptive-statistics 158 | [stats v] 159 | (->> stats 160 | (map (fn [stat] 161 | (if-let [sfn (get stat-fn-map stat)] 162 | [stat (sfn v)] 163 | (throw (js/Error. (str "Unrecognized stat " stat)))))) 164 | (into {}))) 165 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/columnwise_map.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.columnwise-map 2 | (:require [tech.v3.datatype.protocols :as dt-proto] 3 | [tech.v3.dataset.protocols :as ds-proto])) 4 | 5 | (deftype ColAryRowIter [col-ary n-cols ^:mutable col-idx row-idx] 6 | Object 7 | (hasNext [_iter] (< col-idx n-cols)) 8 | (next [_iter] 9 | (let [col-val (col-ary col-idx)] 10 | (set! col-idx (inc col-idx)) 11 | (MapEntry. (name col-val) (col-val row-idx) nil)))) 12 | 13 | 14 | (deftype ColumnwiseMap [meta col-ary colname->col row-idx ^:mutable __hash] 15 | Object 16 | (toString [coll] 17 | (pr-str* coll)) 18 | (equiv [this other] 19 | (-equiv this other)) 20 | 21 | ;; EXPERIMENTAL: subject to change 22 | (keys [coll] 23 | (es6-iterator (mapv name (ds-proto/-columns coll)))) 24 | (entries [coll] 25 | (es6-entries-iterator (ds-proto/-columns-as-map coll))) 26 | (values [coll] 27 | (es6-iterator (ds-proto/-columns coll))) 28 | (has [coll k] 29 | (contains? coll k)) 30 | (get [coll k not-found] 31 | (-lookup coll k not-found)) 32 | (forEach [coll f] 33 | (doseq [[k v] coll] 34 | (f v k))) 35 | 36 | ICloneable 37 | (-clone [coll] (-> (into {} coll) (with-meta meta))) 38 | 39 | IIterable 40 | (-iterator [_coll] 41 | (ColAryRowIter. col-ary (count col-ary) 0 row-idx)) 42 | 43 | IWithMeta 44 | (-with-meta [coll new-meta] 45 | (if (identical? new-meta meta) 46 | coll 47 | (ColumnwiseMap. new-meta col-ary colname->col row-idx __hash))) 48 | 49 | IMeta 50 | (-meta [_coll] meta) 51 | 52 | ICollection 53 | (-conj [coll entry] 54 | (-conj (into {} coll) entry)) 55 | 56 | IEmptyableCollection 57 | (-empty [_coll] (-with-meta (.-EMPTY PersistentHashMap) meta)) 58 | 59 | IEquiv 60 | (-equiv [coll other] (equiv-map coll other)) 61 | 62 | IHash 63 | (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) 64 | 65 | ISeqable 66 | (-seq [_coll] 67 | (when (pos? (count col-ary)) 68 | (map (fn [col] (MapEntry. (name col) (col row-idx) nil)) col-ary))) 69 | 70 | ICounted 71 | (-count [_coll] (count col-ary)) 72 | 73 | ILookup 74 | (-lookup [coll k] 75 | (-lookup coll k nil)) 76 | 77 | (-lookup [_coll k not-found] 78 | (if-let [idx (colname->col k)] 79 | ((col-ary idx) row-idx) 80 | not-found)) 81 | 82 | IAssociative 83 | (-assoc [coll k v] 84 | (persistent! (assoc! (transient coll) k v))) 85 | 86 | (-contains-key? [_coll k] 87 | (-contains-key? colname->col k)) 88 | 89 | IFind 90 | (-find [_coll k] 91 | (when-let [col-idx (colname->col k)] 92 | (MapEntry. k ((col-ary col-idx) row-idx) nil))) 93 | 94 | IMap 95 | (-dissoc [coll k] 96 | (if-let [col-idx (colname->col k)] 97 | (let [left-vec (subvec col-ary 0 col-idx) 98 | right-vec (subvec col-ary (inc col-idx)) 99 | new-ary (vec (concat left-vec right-vec)) 100 | new-colmap (->> new-ary 101 | (map-indexed #(vector (name %2) %1)) 102 | (into {}))] 103 | (ColumnwiseMap. meta new-ary new-colmap row-idx nil)) 104 | coll)) 105 | 106 | IReduce 107 | (-reduce [this rfn] (-reduce col-ary 108 | (fn [acc col] 109 | (rfn acc (MapEntry. (name col) (col row-idx) nil))))) 110 | (-reduce [this rfn acc] (-reduce col-ary 111 | (fn [acc col] 112 | (rfn acc (MapEntry. (name col) (col row-idx) nil))) 113 | acc)) 114 | IKVReduce 115 | (-kv-reduce [_coll f init] 116 | (let [n-cols (count col-ary)] 117 | (loop [idx 0 118 | init init] 119 | (if (< idx n-cols) 120 | (let [col (col-ary idx) 121 | init (f init (name col) (col row-idx))] 122 | (if (reduced? init) 123 | init 124 | (recur (inc idx) init))) 125 | init)))) 126 | 127 | IFn 128 | (-invoke [coll k] 129 | (-lookup coll k)) 130 | 131 | (-invoke [coll k not-found] 132 | (-lookup coll k not-found)) 133 | 134 | IEditableCollection 135 | (-as-transient [_coll] 136 | (let [n-cols (count col-ary)] 137 | (loop [retval (transient {}) 138 | idx 0] 139 | (if (< idx n-cols) 140 | (let [col (col-ary idx)] 141 | (recur (assoc! retval (name col) (col row-idx)) (inc idx))) 142 | retval)))) 143 | IPrintWithWriter 144 | (-pr-writer [cmap writer opts] 145 | (-pr-writer (into {} cmap) writer opts)) 146 | dt-proto/PDatatype 147 | (-datatype [_this] :persistent-map)) 148 | 149 | 150 | (defn columnwise-map 151 | [col-ary colname->col row-idx] 152 | (ColumnwiseMap. nil col-ary colname->col row-idx nil)) 153 | -------------------------------------------------------------------------------- /.clj-kondo/cnuernber/dtype-next/tech/v3/datatype/functional_api.clj: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.functional-api 2 | (:require [clojure.set :as set])) 3 | 4 | (def init-binary-ops 5 | #{:tech.numerics/hypot 6 | :tech.numerics/bit-xor 7 | :tech.numerics/unsigned-bit-shift-right 8 | :tech.numerics/quot 9 | :tech.numerics/atan2 10 | :tech.numerics/* 11 | :tech.numerics/min 12 | :tech.numerics/- 13 | :tech.numerics/pow 14 | :tech.numerics/bit-test 15 | :tech.numerics/bit-and 16 | :tech.numerics/rem 17 | :tech.numerics/max 18 | :tech.numerics/bit-or 19 | :tech.numerics// 20 | :tech.numerics/bit-flip 21 | :tech.numerics/+ 22 | :tech.numerics/bit-shift-left 23 | :tech.numerics/bit-clear 24 | :tech.numerics/ieee-remainder 25 | :tech.numerics/bit-shift-right 26 | :tech.numerics/bit-set 27 | :tech.numerics/bit-and-not}) 28 | 29 | (def init-unary-ops 30 | #{:tech.numerics/tanh 31 | :tech.numerics/sq 32 | :tech.numerics/expm1 33 | :tech.numerics/log10 34 | :tech.numerics/cos 35 | :tech.numerics/tan 36 | :tech.numerics/atan 37 | :tech.numerics/sqrt 38 | :tech.numerics/cosh 39 | :tech.numerics/get-significand 40 | :tech.numerics/- 41 | :tech.numerics/next-up 42 | :tech.numerics/cbrt 43 | :tech.numerics/next-down 44 | :tech.numerics/exp 45 | :tech.numerics/log1p 46 | :tech.numerics// 47 | :tech.numerics/asin 48 | :tech.numerics/sinh 49 | :tech.numerics/rint 50 | :tech.numerics/+ 51 | :tech.numerics/bit-not 52 | :tech.numerics/signum 53 | :tech.numerics/abs 54 | :tech.numerics/ulp 55 | :tech.numerics/sin 56 | :tech.numerics/to-radians 57 | :tech.numerics/acos 58 | :tech.numerics/ceil 59 | :tech.numerics/to-degrees 60 | :tech.numerics/identity 61 | :tech.numerics/logistic 62 | :tech.numerics/log 63 | :tech.numerics/floor}) 64 | 65 | 66 | (defmacro implement-arithmetic-operations 67 | [] 68 | (let [binary-ops init-binary-ops 69 | unary-ops init-unary-ops 70 | dual-ops (set/intersection binary-ops unary-ops) 71 | unary-ops (set/difference unary-ops dual-ops)] 72 | `(do 73 | ~@(->> 74 | unary-ops 75 | (map 76 | (fn [opname] 77 | (let [op-sym (symbol (name opname))] 78 | `(defn ~op-sym 79 | ([~'x ~'options] 80 | (apply + ~'options) 81 | ~'x) 82 | ([~'x] 83 | ~'x)))))) 84 | ~@(->> 85 | binary-ops 86 | (map 87 | (fn [opname] 88 | (let [op-sym (symbol (name opname)) 89 | dual-op? (dual-ops opname)] 90 | (if dual-op? 91 | `(defn ~op-sym 92 | ([~'x] 93 | ~'x) 94 | ([~'x ~'y] 95 | [~'x ~'y]) 96 | ([~'x ~'y & ~'args] 97 | [~'x ~'y ~'args])) 98 | `(defn ~op-sym 99 | ([~'x ~'y] 100 | [~'x ~'y]) 101 | ([~'x ~'y & ~'args] 102 | [~'x ~'y ~'args])))))))))) 103 | 104 | 105 | (def init-unary-pred-ops 106 | [:tech.numerics/mathematical-integer? 107 | :tech.numerics/even? 108 | :tech.numerics/infinite? 109 | :tech.numerics/zero? 110 | :tech.numerics/not 111 | :tech.numerics/odd? 112 | :tech.numerics/finite? 113 | :tech.numerics/pos? 114 | :tech.numerics/nan? 115 | :tech.numerics/neg?]) 116 | 117 | 118 | (defmacro implement-unary-predicates 119 | [] 120 | `(do 121 | ~@(->> init-unary-pred-ops 122 | (map (fn [pred-op] 123 | (let [fn-symbol (symbol (name pred-op))] 124 | `(defn ~fn-symbol 125 | ([~'arg ~'_options] 126 | ~'arg) 127 | ([~'arg] 128 | ~'arg)))))))) 129 | 130 | 131 | (def init-binary-pred-ops 132 | [:tech.numerics/and 133 | :tech.numerics/or 134 | :tech.numerics/eq 135 | :tech.numerics/not-eq]) 136 | 137 | 138 | (defmacro implement-binary-predicates 139 | [] 140 | `(do 141 | ~@(->> init-binary-pred-ops 142 | (map (fn [pred-op] 143 | (let [fn-symbol (symbol (name pred-op))] 144 | `(defn ~fn-symbol 145 | [~'lhs ~'rhs] 146 | (apply + ~'lhs ~'rhs) 147 | ~'lhs))))))) 148 | 149 | 150 | (def init-binary-pred-comp-ops 151 | [:tech.numerics/> 152 | :tech.numerics/>= 153 | :tech.numerics/< 154 | :tech.numerics/<=]) 155 | 156 | 157 | (defmacro implement-compare-predicates 158 | [] 159 | `(do 160 | ~@(->> init-binary-pred-comp-ops 161 | (map (fn [pred-op] 162 | (let [fn-symbol (symbol (name pred-op)) 163 | k pred-op] 164 | `(defn ~fn-symbol 165 | ([~'lhs ~'rhs] 166 | (apply + [~'lhs ~'rhs]) 167 | ~'lhs) 168 | ([~'lhs ~'mid ~'rhs] 169 | (apply + [~'lhs ~'mid ~'rhs]) 170 | ~'lhs)))))))) 171 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/reader_vec.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.reader-vec 2 | (:require [tech.v3.datatype.copy-make-container :as dt-cmc] 3 | [tech.v3.datatype.arrays :as dt-arrays] 4 | [tech.v3.datatype.base :as dt-base] 5 | [tech.v3.datatype.protocols :as dt-proto] 6 | [ham-fisted.api :as hamf] 7 | [ham-fisted.lazy-noncaching :as lznc])) 8 | 9 | 10 | (declare reader-vec) 11 | 12 | 13 | (deftype ReaderVec [meta idx->val cnt dtype ^:mutable __hash] 14 | Object 15 | (toString [coll] 16 | (pr-str* coll)) 17 | (equiv [this other] 18 | (-equiv this other)) 19 | (indexOf [coll x] 20 | (-indexOf coll x 0)) 21 | (indexOf [coll x start] 22 | (-indexOf coll x start)) 23 | (lastIndexOf [coll x] 24 | (-lastIndexOf coll x (count coll))) 25 | (lastIndexOf [coll x start] 26 | (-lastIndexOf coll x start)) 27 | 28 | ICloneable 29 | (-clone [this] (dt-cmc/make-container dtype this)) 30 | 31 | IWithMeta 32 | (-with-meta [coll new-meta] 33 | (if (identical? new-meta meta) 34 | coll 35 | (reader-vec cnt dtype idx->val meta))) 36 | 37 | IMeta 38 | (-meta [_coll] meta) 39 | 40 | IStack 41 | (-peek [coll] 42 | (when (> cnt 0) 43 | (-nth coll (dec cnt)))) 44 | (-pop [_coll] 45 | (cond 46 | (zero? cnt) (throw (js/Error. "Can't pop empty vector")) 47 | (== 1 cnt) (-with-meta (.-EMPTY PersistentVector) meta) 48 | :else 49 | (reader-vec (dec cnt) dtype idx->val meta))) 50 | 51 | ICollection 52 | (-conj [coll o] 53 | (persistent! (conj! (transient coll) o))) 54 | 55 | IEmptyableCollection 56 | (-empty [_coll] (-with-meta (.-EMPTY PersistentVector) meta)) 57 | 58 | ISequential 59 | IEquiv 60 | (-equiv [coll other] 61 | (dt-arrays/equiv-nthable coll other)) 62 | 63 | IHash 64 | (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) 65 | 66 | ISeqable 67 | (-seq [coll] 68 | (when-not (zero? cnt) 69 | (map #(nth coll %) (range cnt)))) 70 | 71 | ICounted 72 | (-count [_coll] cnt) 73 | 74 | IIndexed 75 | (-nth [_coll n] 76 | (let [n (if (< n 0) (+ n cnt) n)] 77 | (when-not (< n cnt) (throw (js/Error. (str "nth out of range:" n " >= " cnt)))) 78 | (idx->val n))) 79 | (-nth [_coll n not-found] 80 | (let [n (if (< n 0) (+ n cnt) n)] 81 | (idx->val n) 82 | not-found)) 83 | 84 | IReduce 85 | (-reduce [this rfn] (-reduce (hamf/range cnt) #(rfn %1 (idx->val %2)))) 86 | (-reduce [this rfn acc] (-reduce (hamf/range cnt) #(rfn %1 (idx->val %2)) acc)) 87 | 88 | ILookup 89 | (-lookup [coll k] (-lookup coll k nil)) 90 | (-lookup [coll k not-found] (if (number? k) 91 | (-nth coll k not-found) 92 | not-found)) 93 | 94 | IAssociative 95 | (-assoc [coll k v] 96 | (if (number? k) 97 | (-assoc-n coll k v) 98 | (throw (js/Error. "Vector's key for assoc must be a number.")))) 99 | (-contains-key? [_coll k] 100 | (if (integer? k) 101 | (let [k (if (< k 0) (+ cnt k) k)] 102 | (and (<= 0 k) (< k cnt))) 103 | false)) 104 | 105 | IFind 106 | (-find [_coll n] 107 | (let [n (if (< n 0) (+ cnt n) n)] 108 | (when (and (<= 0 n) (< n cnt)) 109 | (MapEntry. n (idx->val n) nil)))) 110 | 111 | 112 | IVector 113 | (-assoc-n [coll n val] 114 | (let [n (if (< n 0) (+ cnt n) n)] 115 | (cond 116 | (and (<= 0 n) (< n cnt)) 117 | (loop [idx 0 118 | nvec (transient [])] 119 | (if (< idx cnt) 120 | (recur (inc idx) 121 | (if (== idx n) 122 | (conj! nvec val) 123 | (conj! nvec (idx->val n)))) 124 | (persistent! nvec))) 125 | (== n cnt) (-conj coll val) 126 | :else (throw (js/Error. (str "Index " n " out of bounds [0," cnt "]")))))) 127 | 128 | IKVReduce 129 | (-kv-reduce [_v f init] 130 | (-reduce #(f %1 %2 (idx->val %2)) 131 | init 132 | (hamf/range cnt))) 133 | 134 | IFn 135 | (-invoke [coll k] 136 | (-nth coll k)) 137 | (-invoke [coll k not-found] 138 | (-nth coll k not-found)) 139 | 140 | IEditableCollection 141 | (-as-transient [coll] (hamf/mut-list coll)) 142 | 143 | IReversible 144 | (-rseq [coll] 145 | (when (pos? cnt) 146 | (seq (lznc/map #(idx->val %) (range (dec cnt) -1 -1))))) 147 | 148 | IIterable 149 | (-iterator [this] 150 | (dt-arrays/nth-iter this)) 151 | 152 | IPrintWithWriter 153 | (-pr-writer [rdr writer _opts] 154 | (-write writer (dt-base/reader->str rdr "reader"))) 155 | 156 | dt-proto/PDatatype 157 | (-datatype [_this] :reader) 158 | dt-proto/PElemwiseDatatype 159 | (-elemwise-datatype [_this] dtype) 160 | dt-proto/PSubBufferCopy 161 | (-sub-buffer-copy [this off len] 162 | (dt-proto/-sub-buffer this off len)) 163 | dt-proto/PSubBuffer 164 | (-sub-buffer [_this off len] 165 | (let [off (if (< off 0) (+ cnt off) off) 166 | mlen (+ off len)] 167 | (when-not (<= mlen cnt) 168 | (throw (js/Error. (str "Off+len is out of range: " mlen " > " cnt)))) 169 | (reader-vec len dtype #(idx->val (+ off %)) meta)))) 170 | 171 | 172 | (defn reader-vec 173 | ([n-elems dtype idx->val meta] 174 | (ReaderVec. meta idx->val n-elems dtype nil)) 175 | ([n-elems dtype idx->val] 176 | (reader-vec n-elems dtype idx->val nil))) 177 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/list.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.list 2 | (:require [tech.v3.datatype.base :as dt-base] 3 | [tech.v3.datatype.copy-make-container :as dt-cmc] 4 | [tech.v3.datatype.protocols :as dt-proto] 5 | [tech.v3.datatype.arrays :as dt-arrays] 6 | [tech.v3.datatype.casting :as casting] 7 | [ham-fisted.api :as hamf])) 8 | 9 | 10 | (declare make-primitive-list) 11 | 12 | 13 | (deftype PrimitiveList [^:unsynchronized-mutable buf 14 | ^:unsynchronized-mutable agetable? 15 | dtype 16 | cast-fn 17 | ^:unsynchronized-mutable ptr 18 | ^:unsynchronized-mutable buflen 19 | metadata] 20 | ICounted 21 | (-count [_this] ptr) 22 | ICloneable 23 | (-clone [_this] (make-primitive-list 24 | (-> (dt-proto/-sub-buffer buf 0 ptr) 25 | (clone)) 26 | dtype ptr)) 27 | IMeta 28 | (-meta [_this] metadata) 29 | IWithMeta 30 | (-with-meta [_this new-meta] 31 | (make-primitive-list buf dtype ptr new-meta)) 32 | IPrintWithWriter 33 | (-pr-writer [rdr writer _opts] 34 | (-write writer (dt-base/reader->str rdr "list"))) 35 | ISequential 36 | ISeqable 37 | (-seq [_array] (array-seq (dt-proto/-sub-buffer buf 0 ptr))) 38 | ISeq 39 | (-first [_array] (nth buf 0)) 40 | (-rest [_array] (dt-proto/-sub-buffer buf 1 (dec (count buf)))) 41 | IFn 42 | (-invoke [array n] 43 | (let [n (if (< n 0) (+ (count array) n) n)] 44 | (nth buf n))) 45 | IIndexed 46 | (-nth [array n] 47 | (dt-arrays/nth-impl n (count array) ::dt-arrays/exception 48 | nth buf)) 49 | (-nth [array n not-found] 50 | (dt-arrays/nth-impl n (count array) not-found 51 | nth buf)) 52 | IHash 53 | (-hash [o] (hamf/hash-ordered o)) 54 | 55 | IEquiv 56 | (-equiv [this other] 57 | (dt-arrays/equiv-nthable this other)) 58 | 59 | IReduce 60 | (-reduce [this rfn] (-reduce (dt-proto/-sub-buffer buf 0 ptr) rfn)) 61 | (-reduce [this rfn init] (-reduce (dt-proto/-sub-buffer buf 0 ptr) rfn init)) 62 | 63 | IIterable 64 | (-iterator [this] (dt-arrays/nth-iter this)) 65 | 66 | dt-proto/PElemwiseDatatype 67 | (-elemwise-datatype [_this] dtype) 68 | dt-proto/PDatatype 69 | (-datatype [_this] :list) 70 | dt-proto/PSubBufferCopy 71 | (-sub-buffer-copy [_item offset length] 72 | (dt-arrays/make-typed-buffer (dt-proto/-sub-buffer-copy buf offset length) 73 | dtype metadata)) 74 | dt-proto/PSubBuffer 75 | (-sub-buffer [_item offset length] 76 | (dt-arrays/make-typed-buffer (dt-proto/-sub-buffer buf offset length) 77 | dtype metadata)) 78 | dt-proto/PToTypedArray 79 | (-convertible-to-typed-array? [_this] (dt-proto/-convertible-to-typed-array? buf)) 80 | (->typed-array [_this] (dt-proto/->typed-array (dt-proto/-sub-buffer buf 0 ptr))) 81 | 82 | dt-proto/PToJSArray 83 | (-convertible-to-js-array? [_this] (dt-proto/-convertible-to-js-array? buf)) 84 | (->js-array [_this] (dt-proto/->js-array (dt-proto/-sub-buffer buf 0 ptr))) 85 | 86 | 87 | dt-proto/PAgetable 88 | (-convertible-to-agetable? [_this] (dt-proto/-convertible-to-agetable? buf)) 89 | (->agetable [_this] (dt-proto/->agetable (dt-proto/-sub-buffer buf 0 ptr))) 90 | 91 | dt-proto/PSetValue 92 | (-set-value! [item idx data] 93 | (-> (dt-proto/-sub-buffer buf 0 ptr) 94 | ;;use base version as it has error checking 95 | (dt-base/set-value! idx data)) 96 | item) 97 | dt-proto/PSetConstant 98 | (-set-constant! [_item offset elem-count data] 99 | (-> (dt-proto/-sub-buffer buf 0 ptr) 100 | (dt-base/set-constant! offset elem-count data))) 101 | dt-proto/PListLike 102 | (-add [this elem] 103 | (when (== ptr buflen) 104 | (let [new-buf (dt-cmc/make-container dtype (* 2 buflen)) 105 | abuf (dt-base/as-agetable new-buf) 106 | new-agetable? (boolean abuf)] 107 | (dt-base/set-value! new-buf 0 buf) 108 | (set! buf (or abuf new-buf)) 109 | (set! agetable? new-agetable?) 110 | (set! buflen (* 2 buflen)))) 111 | (if agetable? 112 | (aset buf ptr (cast-fn elem)) 113 | (dt-proto/-set-value! buf ptr elem)) 114 | (set! ptr (inc ptr)) 115 | this) 116 | (-add-all [this container] 117 | (if (indexed? container) 118 | (let [n-elems (count container)] 119 | (when (> (+ ptr n-elems) buflen) 120 | (let [new-len (* 2 (+ ptr n-elems)) 121 | new-buf (dt-cmc/make-container dtype new-len) 122 | abuf (dt-base/as-agetable new-buf) 123 | new-agetable? (boolean abuf)] 124 | (dt-base/set-value! new-buf 0 buf) 125 | (set! buf (or abuf new-buf)) 126 | (set! agetable? new-agetable?) 127 | (set! buflen new-len))) 128 | (dt-base/set-value! buf ptr container) 129 | (set! ptr (+ ptr n-elems))) 130 | (dt-base/iterate! #(dt-proto/-add this %) container)) 131 | this) 132 | (-ensure-capacity [this capacity] 133 | (when (< buflen capacity) 134 | (let [new-len buflen 135 | new-buf (dt-cmc/make-container dtype new-len) 136 | abuf (dt-base/as-agetable new-buf) 137 | new-agetable? (boolean abuf)] 138 | (dt-base/set-value! new-buf 0 buf) 139 | (set! buf (or abuf new-buf)) 140 | (set! agetable? new-agetable?) 141 | (set! buflen new-len))) 142 | this)) 143 | 144 | 145 | (defn make-primitive-list 146 | [buf & [dtype ptr metadata]] 147 | (let [dtype (or dtype (dt-proto/-elemwise-datatype buf)) 148 | ptr (or ptr 0) 149 | buflen (count buf) 150 | abuf (dt-base/as-agetable buf) 151 | agetable? (if abuf true false)] 152 | (PrimitiveList. (or abuf buf) agetable? dtype 153 | (casting/cast-fn dtype) 154 | ptr buflen metadata))) 155 | 156 | 157 | (defn make-list 158 | [dtype & [initial-bufsize]] 159 | (let [initial-bufsize (or initial-bufsize 4)] 160 | (make-primitive-list (dt-arrays/make-array dtype initial-bufsize) 161 | dtype 0))) 162 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/base.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.base 2 | (:require [tech.v3.datatype.protocols :as dt-proto] 3 | [tech.v3.datatype.casting :as casting] 4 | [tech.v3.datatype.format-sequence :as fmt] 5 | [tech.v3.datatype.argtypes :as argtypes] 6 | [ham-fisted.api :as hamf] 7 | [goog.object :as gobject] 8 | [clojure.string :as s]) 9 | (:refer-clojure :exclude [clone counted? indexed?])) 10 | 11 | 12 | (defn ecount 13 | [item] 14 | (if (nil? item) 15 | nil 16 | ;;As count is a protocol in js, we have no reason to define our own 17 | (count item))) 18 | 19 | 20 | (defn shape 21 | [item] 22 | (if (nil? item) nil (dt-proto/-shape item))) 23 | 24 | 25 | (defn clone 26 | "Here for compat with jvm system" 27 | [item] 28 | (cljs.core/clone item)) 29 | 30 | 31 | (defn elemwise-datatype 32 | [item] 33 | (if (nil? item) 34 | :object 35 | (dt-proto/-elemwise-datatype item))) 36 | 37 | 38 | (defn datatype 39 | [item] 40 | (if (nil? item) 41 | :object 42 | (dt-proto/-datatype item))) 43 | 44 | 45 | (defn as-typed-array 46 | [item] 47 | (when (and item (dt-proto/-convertible-to-typed-array? item)) 48 | (dt-proto/->typed-array item))) 49 | 50 | 51 | (defn as-js-array 52 | [item] 53 | (when (and item (dt-proto/-convertible-to-js-array? item)) 54 | (dt-proto/->js-array item))) 55 | 56 | 57 | (defn sub-buffer-copy 58 | "Create a copy of the data in the item from offset till len." 59 | [item off & [len]] 60 | (let [elen (count item) 61 | maxlen (- elen off) 62 | len (or len maxlen)] 63 | (when-not (>= elen (+ off len)) 64 | (throw (js/Error. (str "Offset " off " len " len " => item length " elen)))) 65 | (dt-proto/-sub-buffer-copy item off len))) 66 | 67 | 68 | (defn sub-buffer 69 | [item off & [len]] 70 | (let [elen (count item) 71 | maxlen (- elen off) 72 | len (or len maxlen)] 73 | (when-not (>= elen (+ off len)) 74 | (throw (js/Error. (str "Offset " off " len " len " => item length " elen)))) 75 | (dt-proto/-sub-buffer item off len))) 76 | 77 | 78 | (defn counted? 79 | [item] 80 | (when item 81 | (if (instance? js/Array item) 82 | true 83 | (cljs.core/counted? item)))) 84 | 85 | 86 | (defn indexed? 87 | [item] 88 | (when item 89 | (if (instance? js/Array item) 90 | true 91 | (cljs.core/indexed? item)))) 92 | 93 | 94 | (defn ensure-indexable 95 | [data] 96 | (if-not (or (instance? js/Array data) 97 | (indexed? data)) 98 | (vec data) 99 | data)) 100 | 101 | 102 | (defn as-agetable 103 | [data] 104 | (when (and data (dt-proto/-convertible-to-agetable? data)) 105 | (dt-proto/->agetable data))) 106 | 107 | 108 | (defn set-value! 109 | [item idx data] 110 | (when-not item 111 | (throw (js/Error. "Item is nil"))) 112 | (when-not (< idx (count item)) 113 | (throw (js/Error. "Index is out of item length"))) 114 | (when (and (counted? data) 115 | (not (<= (+ idx (count data)) (count item)))) 116 | (throw (js/Error. (str "data length + idx " (+ (count data) idx) 117 | " is out of range of item length ") (count item)))) 118 | (dt-proto/-set-value! item idx data) 119 | item) 120 | 121 | 122 | (defn set-constant! 123 | [item idx elem-count data] 124 | (when-not item 125 | (throw (js/Error. "Item is nil"))) 126 | (when-not (<= (+ idx elem-count) (count item)) 127 | (throw (js/Error. "Index is out of item length"))) 128 | (when (and (counted? data) 129 | (not (<= (+ idx (count data)) (count item)))) 130 | (throw (js/Error. (str "data length + idx " (+ (count data) idx) 131 | " is out of range of item length ") (count item)))) 132 | (dt-proto/-set-constant! item idx elem-count data) 133 | item) 134 | 135 | 136 | (defn integer-range? 137 | [item] 138 | (or (instance? IntegerRange item) 139 | (and (instance? hamf/RangeType item) 140 | (.isInteger ^JS item)))) 141 | 142 | 143 | (defn- consumer-acc 144 | [acc v] 145 | (acc v) acc) 146 | 147 | 148 | (defn as-js-iterator 149 | [data] 150 | (when data 151 | (when-let [iter-fn (gobject/get data ITER_SYMBOL)] 152 | (.call iter-fn data)))) 153 | 154 | 155 | (defn as-iterator 156 | [data] 157 | (when (and data (satisfies? IIterable data)) 158 | (-iterator data))) 159 | 160 | 161 | (defn indexed-iterate! 162 | [consume-fn item] 163 | (reduce (hamf/indexed-accum-fn 164 | (fn [acc idx v] 165 | (acc idx v) 166 | acc)) 167 | consume-fn item)) 168 | 169 | 170 | (defn iterate! 171 | [consume-fn item] 172 | (reduce consumer-acc consume-fn item)) 173 | 174 | 175 | (defn reader-data->str 176 | ([rdr dtype] 177 | ;;numpy-style first/last print of longer sequences 178 | (let [n-elems (count rdr) 179 | ellipses? (> n-elems 25) 180 | n-shortened 10 181 | rdr-data (if ellipses? 182 | (concat (sub-buffer rdr 0 n-shortened) 183 | (sub-buffer rdr (- n-elems n-shortened) n-shortened)) 184 | rdr) 185 | formatted (if (casting/numeric-type? dtype) 186 | (fmt/format-sequence rdr-data) 187 | rdr-data)] 188 | 189 | (if ellipses? 190 | (s/join " " (concat (take n-shortened formatted) 191 | ["..."] 192 | (drop n-shortened formatted))) 193 | (s/join " " formatted)))) 194 | ([rdr] 195 | (reader-data->str rdr (elemwise-datatype rdr)))) 196 | 197 | 198 | (defn reader->str 199 | [rdr typename] 200 | (let [simple-print? (get (meta rdr) :simple-print?) 201 | cnt (count rdr) 202 | dtype (elemwise-datatype rdr) 203 | rdr-str (reader-data->str rdr dtype)] 204 | (if simple-print? 205 | (str "[" rdr-str "]") 206 | (str "#" typename "[[" dtype " " cnt "][" rdr-str "]]")))) 207 | 208 | 209 | (defn list-coalesce! 210 | "Coalesce data into a container that implements add! and add-all!. Returns the container." 211 | [data container] 212 | (if (= :scalar (argtypes/argtype data)) 213 | (dt-proto/-add container data) 214 | (if (= :scalar (argtypes/argtype (first data))) 215 | (dt-proto/-add-all container data) 216 | (iterate! #(list-coalesce! % container) data))) 217 | container) 218 | 219 | 220 | (defn generalized-shape 221 | [data] 222 | (cond 223 | (or (nil? data) (= :scalar (argtypes/argtype data))) 224 | [] 225 | (satisfies? dt-proto/PShape data) 226 | (dt-proto/-shape data) 227 | :else 228 | (loop [shp (transient [(count data)]) 229 | data (first data)] 230 | (if (= :scalar (argtypes/argtype data)) 231 | (persistent! shp) 232 | (recur (conj! shp (count data)) (first data)))))) 233 | 234 | 235 | (extend-type cljs.core/PersistentVector 236 | dt-proto/PSubBuffer 237 | (-sub-buffer [item offset len] 238 | (subvec item offset (+ offset len)))) 239 | -------------------------------------------------------------------------------- /docs/Reductions.html: -------------------------------------------------------------------------------- 1 | 3 | Some Reduction Timings

Some Reduction Timings

8 |

The datatype library has some helpers that work with datasets that can make certain types of 9 | reductions much faster.

10 |

Filter On One Column, Reduce Another

11 |

This is a very common operation so let's take a closer look. The generic dataset 12 | pathway would be:

13 |
cljs.user> (require '[tech.v3.dataset :as ds])
14 | nil
15 | cljs.user> (def test-ds (ds/->dataset {:a (range 20000)
16 |                                        :b (repeatedly 20000 rand)}))
17 | #'cljs.user/test-ds
18 | cljs.user> ;;filter on a, sum b.
19 | cljs.user> (reduce + 0.0 (-> (ds/filter-column test-ds :a #(> % 10000))
20 |                              (ds/column :b)))
21 | 5000.898384571656
22 | cljs.user> (time (dotimes [idx 100] (reduce + 0.0 (-> (ds/filter-column test-ds :a #(> % 10000))
23 |                                                       (ds/column :b)))))
24 | "Elapsed time: 282.714231 msecs"
25 | "Elapsed time: 282.714231 msecs"
26 | "Elapsed time: 282.714231 msecs"
27 | 
28 |

Think transducers are fast? What about a generic transducer pathway?

29 |
cljs.user> (let [a (test-ds :a)
30 |                  b (test-ds :b)]
31 |              (transduce (comp (filter #(> (nth a %) 10000))
32 |                               (map #(nth b %)))
33 |                         (completing +)
34 |                         (range (ds/row-count test-ds))))
35 | 5000.898384571656
36 | cljs.user> (time (dotimes [idx 100] 
37 |                    (let [a (test-ds :a)
38 |                          b (test-ds :b)]
39 |                      (transduce (comp (filter #(> (nth a %) 10000))
40 |                                       (map #(nth b %)))
41 |                                 (completing +)
42 |                                 (range (ds/row-count test-ds))))))
43 | "Elapsed time: 436.235972 msecs"
44 | "Elapsed time: 436.235972 msecs"
45 | "Elapsed time: 436.235972 msecs"
46 | nil
47 | 
48 |

Transducers are fast - after looking at this pathway we found the 49 | nth call is relatively expensive. The datatype library has a way 50 | to get the fastest nth-like access available for a given container. Columns overload 51 | this pathway such that if there are no missing they use the fastest 52 | access for their buffer, else they have to wrap a missing check. Regardless, 53 | this gets us a solid improvement:

54 |
cljs.user> (require '[tech.v3.datatype :as dtype])
55 | nil
56 | cljs.user> (time (dotimes [idx 100] 
57 |                    (let [a (dtype/->fast-nth (test-ds :a))
58 |                          b (dtype/->fast-nth (test-ds :b))]
59 |                      (transduce (comp (filter #(> (a %) 10000))
60 |                                       (map #(b %)))
61 |                                 (completing +)
62 |                                 (range (ds/row-count test-ds))))))
63 | "Elapsed time: 77.823553 msecs"
64 | "Elapsed time: 77.823553 msecs"
65 | "Elapsed time: 77.823553 msecs"
66 | nil
67 | 
68 |

OK - there is another more dangerous approach. dtype has another query, 69 | as-agetable, that either returns something for which aget works or 70 | nil. If you know your dataset's columns have no missing data and their 71 | backing store data itself is agetable - then you can get an agetable. This 72 | doesn't have a fallback so you risk null ptr issues - but it is the fastest 73 | possible pathway.

74 |
cljs.user> (time (dotimes [idx 100] 
75 |                    (let [a (dtype/as-agetable (test-ds :a))
76 |                          b (dtype/as-agetable (test-ds :b))]
77 |                      (transduce (comp (filter #(> (aget a %) 10000))
78 |                                       (map #(aget b %)))
79 |                                 (completing +)
80 |                                 (range (ds/row-count test-ds))))))
81 | "Elapsed time: 57.404783 msecs"
82 | "Elapsed time: 57.404783 msecs"
83 | "Elapsed time: 57.404783 msecs"
84 | nil
85 | 
86 |

In this simple example we find that a transducing pathway is indeed a quite bit faster but only 87 | when it is coupled with an efficient per-element access pattern.

88 |
-------------------------------------------------------------------------------- /src/tech/v3/dataset/io/column_parsers.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.io.column-parsers 2 | "A column parser is a parser that takes an indexed stream of values 3 | and returns a packed datastructure and a missing set. nil values, 4 | #NaN values, and values of :tech.v3.dataset/missing are all interpreted as 5 | nil values." 6 | (:require [tech.v3.datatype :as dtype] 7 | [tech.v3.datatype.casting :as casting] 8 | [tech.v3.datatype.protocols :as dt-proto] 9 | [tech.v3.dataset.impl.column :as dt-col])) 10 | 11 | 12 | (defprotocol PParser 13 | (-add-value! [this idx val]) 14 | (-finalize [this rowcount])) 15 | 16 | 17 | (defn- add-missing! 18 | [rowcount missing-val data missing] 19 | (let [cur-count (count data) 20 | n-prev (- rowcount cur-count)] 21 | ;;add all missing values so far 22 | (dotimes [idx n-prev] 23 | (.add missing (+ idx cur-count)) 24 | (dt-proto/-add data missing-val)))) 25 | 26 | 27 | (defn- missing? 28 | [val missing-val] 29 | (cond 30 | (number? val) 31 | (js/isNaN val) 32 | (nil? val) true 33 | (= val :tech.v3.dataset/missing) true 34 | (not (boolean? val)) 35 | (= val missing-val))) 36 | 37 | 38 | (defn fixed-type-parser 39 | [colname dtype] 40 | (let [data (dt-col/make-container dtype) 41 | missing-val (if (casting/numeric-type? dtype) 42 | ##NaN 43 | (dt-col/datatype->missing-value dtype)) 44 | missing (js/Set.)] 45 | (reify 46 | ICounted 47 | (-count [this] (count data)) 48 | dt-proto/PElemwiseDatatype 49 | (-elemwise-datatype [this] dtype) 50 | dt-proto/PListLike 51 | (-add [this val] 52 | (if (missing? val missing-val) 53 | (do 54 | (.add missing (count data)) 55 | (dt-proto/-add data missing-val)) 56 | (dt-proto/-add data val))) 57 | (-add-all [this new-data] 58 | (if-let [aget-data (dtype/as-datatype-accurate-agetable new-data)] 59 | ;;Do scanning/adding of data separate from checking for missing 60 | (let [cur-off (count data)] 61 | (dt-proto/-add-all data aget-data) 62 | (dotimes [idx (count aget-data)] 63 | (let [dval (aget aget-data idx)] 64 | (when (missing? dval missing-val) 65 | (.add missing (+ idx cur-off) dval))))) 66 | ;;if we have absolutely no idea what this is. 67 | (dtype/iterate! #(dt-proto/-add this %) new-data))) 68 | PParser 69 | (-add-value! [this idx val] 70 | (add-missing! idx missing-val data missing) 71 | (dt-proto/-add this val)) 72 | (-finalize [this rowcount] 73 | (add-missing! rowcount missing-val data missing) 74 | (let [data (or (dtype/as-datatype-accurate-agetable data) data)] 75 | (dt-col/new-column data 76 | missing 77 | {:name colname} 78 | (casting/numeric-type? 79 | (dtype/elemwise-datatype data))))) 80 | INamed 81 | (-name [this] colname) 82 | IIndexed 83 | (-nth [this idx] 84 | (if (or (>= idx (count data)) 85 | (.has missing idx)) 86 | missing-val 87 | (-nth data idx))) 88 | (-nth [this idx dv] 89 | (if (or (>= idx (count data)) 90 | (.has missing idx)) 91 | dv 92 | (-nth data idx)))))) 93 | 94 | 95 | (deftype ObjParse [^:unsynchronized-mutable container 96 | ^:unsynchronized-mutable container-dtype 97 | ^:unsynchronized-mutable missing-val 98 | missing 99 | colname] 100 | ICounted 101 | (-count [_this] (count container)) 102 | dt-proto/PElemwiseDatatype 103 | (-elemwise-datatype [_this] (dtype/elemwise-datatype container)) 104 | dt-proto/PListLike 105 | (-add [_this val] 106 | (if (missing? val missing-val) 107 | (do 108 | (.add missing (count container)) 109 | (dt-proto/-add container missing-val)) 110 | (let [val-dt (dtype/datatype val)] 111 | (if (or (= container-dtype :object) 112 | (= val-dt container-dtype)) 113 | (dt-proto/-add container val) 114 | ;;Container has no meaningful data 115 | (let [n-missing (count missing)] 116 | (if (>= n-missing (count container)) 117 | (let [new-container (dt-col/make-container val-dt) 118 | new-missing (dt-col/datatype->missing-value val-dt)] 119 | (dotimes [_idx n-missing] 120 | (dt-proto/-add new-container new-missing)) 121 | (set! container new-container) 122 | (set! container-dtype val-dt) 123 | (set! missing-val new-missing)) 124 | ;;Else container has meaningful data- promote to object 125 | (let [new-container (dt-col/make-container :object) 126 | new-missing nil] 127 | (dotimes [idx (count container)] 128 | (if (.has missing idx) 129 | (dt-proto/-add new-container new-missing) 130 | (dt-proto/-add new-container (nth container idx)))) 131 | (set! container new-container) 132 | (set! container-dtype :object) 133 | (set! missing-val new-missing))) 134 | (dt-proto/-add container val)))))) 135 | (-add-all [this data] 136 | (let [agetable-data (dtype/as-datatype-accurate-agetable data)] 137 | ;;Potentially we can just straight line this data. This is really purely a fastpath 138 | ;;for numeric data 139 | (if (and agetable-data 140 | (or (>= (count missing) (count container)) 141 | (= container-dtype (dtype/elemwise-datatype agetable-data)))) 142 | (do 143 | ;;when we have to change our container datatype 144 | (when (not= container-dtype (dtype/elemwise-datatype agetable-data)) 145 | (let [data-dtype (dtype/elemwise-datatype agetable-data) 146 | new-container (dt-col/make-container data-dtype) 147 | new-missing (dt-col/datatype->missing-value data-dtype) 148 | n-missing (count missing)] 149 | (dotimes [_idx n-missing] 150 | (dt-proto/-add new-container new-missing)) 151 | (set! container new-container) 152 | (set! container-dtype data-dtype) 153 | (set! missing-val new-missing))) 154 | (let [cur-off (count container)] 155 | (dt-proto/-add-all container agetable-data) 156 | ;;scan agetable data for missing 157 | (when (#{:float32 :float64 :object} (dtype/elemwise-datatype agetable-data)) 158 | (dotimes [idx (count agetable-data)] 159 | (let [dval (aget agetable-data idx)] 160 | (when (or (nil? dval) 161 | (js/isNaN dval)) 162 | ;;record NaN 163 | (.add missing (+ idx cur-off)))))))) 164 | ;;fallback to basic iteration 165 | (dtype/iterate! #(dt-proto/-add this %) data)))) 166 | PParser 167 | (-add-value! [this idx val] 168 | (add-missing! idx missing-val container missing) 169 | (dt-proto/-add this val)) 170 | (-finalize [_this rowcount] 171 | (add-missing! rowcount missing-val container missing) 172 | (let [data (or (dtype/as-datatype-accurate-agetable container) container)] 173 | (dt-col/new-column data 174 | missing 175 | {:name colname} 176 | (casting/numeric-type? 177 | (dtype/elemwise-datatype data))))) 178 | INamed 179 | (-name [this] colname) 180 | IIndexed 181 | (-nth [this idx] 182 | (if (or (>= idx (count container)) 183 | (.has missing idx)) 184 | missing-val 185 | (-nth container idx))) 186 | (-nth [this idx dv] 187 | (if (or (>= idx (count container)) 188 | (.has missing idx)) 189 | dv 190 | (-nth container idx)))) 191 | 192 | 193 | (defn promotional-object-parser 194 | [colname] 195 | (ObjParse. (dt-col/make-container :boolean) :boolean false 196 | (js/Set.) colname)) 197 | -------------------------------------------------------------------------------- /src/tech/v3/dataset/impl/column.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.dataset.impl.column 2 | "Column implementation and defaults" 3 | (:require [tech.v3.datatype.casting :as casting] 4 | [tech.v3.datatype :as dtype] 5 | [tech.v3.datatype.base :as dt-base] 6 | [tech.v3.datatype.argtypes :as argtypes] 7 | [tech.v3.datatype.argops :as argops] 8 | [tech.v3.datatype.arrays :as dt-arrays] 9 | [tech.v3.datatype.protocols :as dt-proto] 10 | [tech.v3.dataset.string-table :as strt] 11 | [tech.v3.dataset.protocols :as ds-proto] 12 | [ham-fisted.api :as hamf])) 13 | 14 | 15 | (defn datatype->missing-value 16 | [dtype] 17 | ;;these need to match the jvm values 18 | (case dtype 19 | :boolean false 20 | :int8 -128 21 | :int16 -32768 22 | :int32 -2147483648 23 | ;;really not enough bits for this but whatevs 24 | :int64 -9223372036854775808 25 | :float32 ##NaN 26 | :float64 ##NaN 27 | (if (casting/numeric-type? dtype) 28 | 0 29 | nil))) 30 | 31 | 32 | (defn make-container 33 | [dtype] 34 | (if (= :string dtype) 35 | (strt/make-string-table) 36 | (dtype/make-list dtype))) 37 | 38 | (defn- inclusive-in-range 39 | [val start end] 40 | (and (>= val start) 41 | (<= val end))) 42 | 43 | (defn- clamp 44 | [val start end] 45 | (max start (min val end))) 46 | 47 | (defn- clip-integer-range 48 | [n-rows rng] 49 | (let [rstart (aget rng "start") 50 | rend (aget rng "end")] 51 | (if (and (inclusive-in-range rstart 0 (unchecked-dec n-rows)) 52 | (inclusive-in-range rend -1 (unchecked-inc n-rows))) 53 | rng 54 | (let [rstart (clamp rstart 0 (unchecked-dec n-rows)) 55 | rend (clamp rend -1 n-rows)] 56 | (hamf/range rstart rend (aget rng "step")))))) 57 | 58 | 59 | (defn- scan-indexes 60 | [n-rows rowidxs] 61 | (with-meta 62 | (transduce (map #(let [rv (if (< % 0) 63 | (+ % n-rows) 64 | %)] 65 | (when (or (< rv 0) (>= rv n-rows)) 66 | (throw (js/Error. (str "Index (" % ") is out of range: [0-" n-rows ")")))) 67 | rv)) 68 | argops/index-reducer-rf 69 | rowidxs) 70 | {:tech.v3.datatype.argops/processed true})) 71 | 72 | 73 | (defn process-row-indexes 74 | [n-rows rowidxs] 75 | ;;Avoid double processing 76 | (if (get (meta rowidxs) :tech.v3.datatype.argops/processed) 77 | rowidxs 78 | (if (dt-base/integer-range? rowidxs) 79 | (let [start (aget rowidxs "start") 80 | step (aget rowidxs "step") 81 | ne (count rowidxs)] 82 | (if (and (>= 0 start) (pos? step)) 83 | (vary-meta (clip-integer-range n-rows rowidxs) 84 | assoc :tech.v3.datatype.argops/processed true) 85 | (scan-indexes n-rows rowidxs))) 86 | ;;Convert negative indexes to positive. 87 | (scan-indexes n-rows rowidxs)))) 88 | 89 | 90 | (declare new-column) 91 | 92 | 93 | (deftype Column [buf missing metadata numeric?] 94 | Object 95 | (toString [coll] 96 | (pr-str* coll)) 97 | (equiv [this other] 98 | (-equiv this other)) 99 | ICounted 100 | (-count [_this] (count buf)) 101 | ICloneable 102 | (-clone [_this] (new-column (clone buf) (clone missing) metadata numeric?)) 103 | IFn 104 | (-invoke [this n] (nth this n)) 105 | IIndexed 106 | (-nth [_this n] 107 | (dt-arrays/nth-impl n (count buf) ::dt-arrays/exception 108 | (fn [buf n] 109 | (if (.has missing n) 110 | (if numeric? ##NaN nil) 111 | (nth buf n))) 112 | buf)) 113 | (-nth [_this n not-found] 114 | (dt-arrays/nth-impl n (count buf) not-found 115 | (fn [buf n] 116 | (if (.has missing n) 117 | (if numeric? ##NaN nil) 118 | (nth buf n))) 119 | buf)) 120 | ISeqable 121 | (-seq [this] 122 | (let [ec (count this)] 123 | (when-not (== 0 ec) 124 | (map #(nth this %) (range ec))))) 125 | IWithMeta 126 | (-with-meta [coll new-meta] 127 | (if (identical? new-meta metadata) 128 | coll 129 | (new-column buf missing new-meta numeric?))) 130 | IMeta 131 | (-meta [_coll] 132 | (assoc metadata 133 | :row-count (count buf) 134 | :datatype (dtype/elemwise-datatype buf))) 135 | IPrintWithWriter 136 | (-pr-writer [array writer _opts] 137 | (-write writer (dt-base/reader->str array "column"))) 138 | INamed 139 | (-name [_this] (:name metadata)) 140 | ISequential 141 | IHash 142 | (-hash [o] (hamf/hash-ordered o)) 143 | IEquiv 144 | (-equiv [this other] 145 | (dt-arrays/equiv-nthable this other)) 146 | IIterable 147 | (-iterator [this] (dt-arrays/nth-iter this)) 148 | IReduce 149 | (-reduce [this f] 150 | (if (== 0 (count missing)) 151 | (-reduce buf f) 152 | (let [missing-value (if numeric? ##NaN nil)] 153 | (reduce (hamf/indexed-accum-fn 154 | (fn [acc idx v] 155 | (if (.has missing idx) 156 | (f acc missing-value) 157 | (f acc v)))) 158 | buf)))) 159 | (-reduce [this f start] 160 | (if (== 0 (count missing)) 161 | (-reduce buf f start) 162 | (let [missing-value (if numeric? ##NaN nil)] 163 | (reduce (hamf/indexed-accum-fn 164 | (fn [acc idx v] 165 | (if (.has missing idx) 166 | (f acc missing-value) 167 | (f acc v)))) 168 | start 169 | buf)))) 170 | dt-proto/PElemwiseDatatype 171 | (-elemwise-datatype [_this] (dtype/elemwise-datatype buf)) 172 | dt-proto/PDatatype 173 | (-datatype [_this] :column) 174 | dt-proto/PToJSArray 175 | (-convertible-to-js-array? [_this] (and (dt-proto/-convertible-to-js-array? buf) 176 | (== 0 (aget missing "size")))) 177 | (->js-array [_this] (dt-proto/->js-array buf)) 178 | dt-proto/PToTypedArray 179 | (-convertible-to-typed-array? [_this] (and (dt-proto/-convertible-to-typed-array? buf) 180 | (== 0 (aget missing "size")))) 181 | (->typed-array [_this] (dt-proto/->typed-array buf)) 182 | dt-proto/PAgetable 183 | (-convertible-to-agetable? [_this] (and (dt-proto/-convertible-to-agetable? buf) 184 | (== 0 (aget missing "size")))) 185 | (->agetable [_this] (dt-proto/->agetable buf)) 186 | dt-proto/PSetValue 187 | (-set-value! [_this idx data] 188 | (if (= :reader (argtypes/argtype data)) 189 | (let [n-elems (count data)] 190 | (dotimes [elidx n-elems] 191 | (.remove missing (+ idx elidx)))) 192 | (.remove missing idx)) 193 | (dt-proto/-set-value! buf idx data)) 194 | dt-proto/PSetConstant 195 | (-set-constant! [_this idx elem-count value] 196 | (dotimes [elidx elem-count] 197 | (.remove missing (+ elidx idx))) 198 | (dt-proto/-set-constant! buf idx elem-count value)) 199 | dt-proto/PSubBuffer 200 | (-sub-buffer [col off len] 201 | (let [new-buf (dt-base/sub-buffer buf off len) 202 | new-missing (js/Set.)] 203 | (dotimes [idx len] 204 | (when (.has missing (+ off idx)) 205 | (.add new-missing idx))) 206 | (new-column new-buf new-missing (meta col) numeric?))) 207 | dt-proto/PFastAccessor 208 | (->fast-nth [this] 209 | (let [buf-nth (dt-proto/->fast-nth buf)] 210 | (if (== 0 (count missing)) 211 | buf-nth 212 | (if numeric? 213 | (fn [n] 214 | (if (.has missing n) 215 | ##NaN 216 | (buf-nth n))) 217 | (fn [n] 218 | (if (.has missing n) 219 | nil 220 | (buf-nth n))))))) 221 | ds-proto/PColumn 222 | (-is-column? [_this] true) 223 | (-column-buffer [_this] buf) 224 | ds-proto/PRowCount 225 | (-row-count [_this] (count buf)) 226 | ds-proto/PMissing 227 | (-missing [_this] missing) 228 | ds-proto/PSelectRows 229 | (-select-rows [_this rowidxs] 230 | (let [rowidxs (process-row-indexes (dtype/ecount buf) rowidxs) 231 | new-missing (js/Set.)] 232 | (when-not (== 0 (count missing)) 233 | (dtype/indexed-iterate! 234 | #(when (.has missing %2) 235 | (.add new-missing %1)) 236 | rowidxs)) 237 | (new-column (dtype/indexed-buffer rowidxs buf) new-missing metadata numeric?)))) 238 | 239 | 240 | (defn new-column 241 | [buf missing metadata numeric?] 242 | (Column. buf missing metadata numeric?)) 243 | -------------------------------------------------------------------------------- /docs/tech.v3.libs.cljs-ajax.html: -------------------------------------------------------------------------------- 1 | 3 | tech.v3.libs.cljs-ajax documentation

tech.v3.libs.cljs-ajax

Bindings to use the dataset handlers in cljs GET/POST calls.

8 |

add-java-time-handlers!

(add-java-time-handlers!)

Add handlers for java.time.LocalDate and java.time.Instant

9 |

add-transit-io-handlers!

(add-transit-io-handlers! datatype tag read-fn write-fn)

GET

(GET url options)

Drop in replacement for cljs-ajax.core/GET

10 |

opt-map

(opt-map)

Options map that must be included in the cljs-ajax request in order 11 | to activate dataset->transit pathways.

12 |

POST

(POST url options)

Drop in replacement for cljs-ajax.core/POST

13 |

read-handlers*

response-format

(response-format & [content-type])

cljs-ajax interceptor that hardwires content-type to application/transit+json 14 | and uses ds/transit-read-handler-map.

15 |

transit-request

(transit-request method url options)

Perform a cljs-ajax request using the select cljs-ajax.core method and merging 16 | opt-map first into the options.

17 |

write-handlers*

writer

(writer)

Transit writer used for writing transit-json datasets and uses 18 | ds/transit-write-handler-map

19 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataFrame and Numerics for ClojureScript 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/com.cnuernber/tmdjs.svg)](https://clojars.org/com.cnuernber/tmdjs) 4 | 5 | * [API Documentation](https://cnuernber.github.io/tmdjs/) 6 | * For those of you dealing with a stream of maps - [mapseq-parser](https://cnuernber.github.io/tmdjs/tech.v3.dataset.html#var-mapseq-parser). 7 | * Please see our topic on [Reductions](https://cnuernber.github.io/tmdjs/Reductions.html). 8 | 9 | Minimal cljs implementation of [tech.v3.datatype](https://cnuernber.github.io/tmdjs/tech.v3.datatype.html), 10 | [tech.v3.datatype.functional](https://cnuernber.github.io/tmdjs/tech.v3.datatype.functional.html), 11 | [tech.v3.datatype.argops](https://cnuernber.github.io/tmdjs/tech.v3.datatype.argops.html), and 12 | [tech.v3.dataset](https://cnuernber.github.io/tmdjs/tech.v3.dataset.html). This implementation is based 13 | on typed-arrays for numeric data and js arrays for everything else so it should 14 | support all your favorite datatypes. Support for columns of type `java.time.Instant` and 15 | `java.time.LocalDate` is included. 16 | 17 | Datasets serialize and deserialize much faster than sequences of maps. They use less 18 | memory and they allow faster columnwise operations. To transform a sequence of maps 19 | into a dataset use [ds/->dataset](https://cnuernber.github.io/tmdjs/tech.v3.dataset.html#var--.3Edataset). 20 | To get a sequence of maps back use [ds/rows](https://cnuernber.github.io/tmdjs/tech.v3.dataset.html#var-rows). 21 | 22 | 23 | This library is mainly going to useful if you are dealing with large amounts of primarily 24 | numeric data such as timeseries data coming off of a sensor. In that case you can specify 25 | exactly the datatype of the column which will get you major benefits in terms of 26 | memory and serialization size. I developed this library when working with such data 27 | in a react-native application. 28 | 29 | 30 | Unlike the jvm-version this is a very minimal exposition of these concepts. Since the 31 | underlying vm itself is typeless there was no need for a complex macro system to do 32 | unboxed math in loops so I could stay much closer to core clojure and in fact ICounted 33 | and IIndexed are the primary interfaces and 34 | [tech.v3.datatype/reify-reader](https://cnuernber.github.io/tmdjs/tech.v3.datatype.html#var-reify-reader) 35 | creates a persistent-vector hash and equiv compatible object. 36 | 37 | 38 | If you are totally new to all of this please check out my talk on [High Performance Data with Clojure](https://www.youtube.com/watch?v=5mUGu4RlwKE). 39 | Many more talks are available from my [GitHub page](https://github.com/cnuernber). 40 | 41 | 42 | ## Example 43 | 44 | 45 | #### Server Side 46 | 47 | There is a new namespace, tech.v3.libs.transit that contains a transit-safe 48 | dataset->data function. There are also transit handlers defined if you know how to 49 | override your transit handlers in your middleware. 50 | 51 | ```clojure 52 | (defn generate-data 53 | [request] 54 | (-> (ds/->dataset (repeatedly 10000 #(hash-map :time (rand) 55 | :temp (rand) 56 | :temp1 (rand) 57 | :temp2 (rand) 58 | :valid? (if (> (rand) 0.5) 59 | true 60 | false)))) 61 | (ds-t/dataset->data) 62 | (response/response))) 63 | ``` 64 | 65 | #### Client Side 66 | 67 | The same namespace tech.v3.dataset is exposed for clojurescript containing most of the 68 | usual functions - columns, rows, select-rows, group-by, etc. The version of 69 | dataset->data in this namespace corresponds with the version on the jvm side above. 70 | There are also transit handlers defined if you have access to override the transit 71 | handlers in your middleware stack. 72 | 73 | ```clojure 74 | (GET "/data" {:handler #(let [ds (ds/data->dataset %)] 75 | (swap! app* assoc 76 | :ds ds 77 | :raw (mapv (partial into {}) (ds/rows ds))))}) 78 | ``` 79 | 80 | 81 | #### Quick Walkthough 82 | 83 | ```clojure 84 | ;;Create a dataset from a map of columns 85 | cljs.user> (def ds (ds/->dataset {:a (range 100) 86 | :b (take 100 (cycle [:a :b :c])) 87 | :c (take 100 (cycle ["one" "two" "three"]))})) 88 | 89 | #'cljs.user/ds 90 | cljs.user> ds 91 | #dataset[unnamed [100 3] 92 | | :a | :b | :c | 93 | |---:|----|-------| 94 | | 0 | :a | one | 95 | | 1 | :b | two | 96 | | 2 | :c | three | 97 | | 3 | :a | one | 98 | | 4 | :b | two | 99 | ... 100 | 101 | ;; Control column datatypes by using parser-fn which is very thoroughly documented in 102 | ;; tech.ml.dataset api documentation 103 | 104 | cljs.user> (->> (ds/->dataset {:a (range 100) 105 | :b (take 100 (cycle [:a :b :c])) 106 | :c (take 100 (cycle ["one" "two" "three"]))} 107 | {:parser-fn {:a :int8}}) 108 | (vals) 109 | (map (comp :datatype meta))) 110 | (:int8 :keyword :string) 111 | 112 | 113 | ;; Datasets are always safe to print to your repl. Only the first 25 rows are printed. 114 | ;; You can use datasets like maps of columns - columns also are safe to print to your 115 | ;; repl at all times. 116 | cljs.user> (ds :a) 117 | #column[[:float64 100][0 1 2 3 4 5 6 7 8 9 ... 90 91 92 93 94 95 96 97 98 99] 118 | 119 | ;;Add a new column with map. 120 | cljs.user> (ds/head (assoc ds :aa (map #(* % %) (ds :a)))) 121 | #dataset[unnamed [5 4] 122 | | :a | :b | :c | :aa | 123 | |---:|----|-------|----:| 124 | | 0 | :a | one | 0 | 125 | | 1 | :b | two | 1 | 126 | | 2 | :c | three | 4 | 127 | | 3 | :a | one | 9 | 128 | | 4 | :b | two | 16 |] 129 | 130 | 131 | ;; Column map is bit more efficient 132 | cljs.user> (ds/head (ds/column-map ds :aa #(* % %) [:a])) 133 | #dataset[unnamed [5 4] 134 | | :a | :b | :c | :aa | 135 | |---:|----|-------|----:| 136 | | 0 | :a | one | 0 | 137 | | 1 | :b | two | 1 | 138 | | 2 | :c | three | 4 | 139 | | 3 | :a | one | 9 | 140 | | 4 | :b | two | 16 |] 141 | 142 | ;; Remove columns with dissoc 143 | cljs.user> (ds/head (dissoc ds :c)) 144 | #dataset[unnamed [5 2] 145 | | :a | :b | 146 | |---:|----| 147 | | 0 | :a | 148 | | 1 | :b | 149 | | 2 | :c | 150 | | 3 | :a | 151 | | 4 | :b |] 152 | 153 | ;;select particular rows 154 | cljs.user> (ds/select-rows ds [1 3 5 7 9]) 155 | #dataset[unnamed [5 3] 156 | | :a | :b | :c | 157 | |---:|----|-------| 158 | | 1 | :b | two | 159 | | 3 | :a | one | 160 | | 5 | :c | three | 161 | | 7 | :b | two | 162 | | 9 | :a | one |] 163 | ``` 164 | 165 | 166 | * Once you have a dataset using the function in the dataset namespace to do columnwise 167 | operations will usually be a bit quicker. 168 | * See [testapp](testapp) for a minimal quick walkthrough and verification that 169 | advanced optimizations do not break the api. 170 | 171 | ## Chrome Heap Measurements. 172 | 173 | For a 2-column dataset of just time and temperature of random double data dataset is about 174 | 18X smaller than a sequence of maps. More columns or choosing different datatypes for the 175 | columns will change the results in various ways but 10X isn't unreasonable. 176 | 177 | 178 | ```clojure 179 | 180 | testapp.webapp> (def ignored 181 | (->> (repeatedly 10000 #(hash-map :time (rand) :temp (rand))) 182 | (ds/->>dataset) 183 | ;;When building a dataset we save data using dynamically resizing 184 | ;;backing stores. Cloning crops the dataset to exactly the size 185 | ;;it needs 186 | (clone) 187 | (aset js/window "AAAMemTest-Dataset"))) 188 | #'testapp.webapp/ignored 189 | testapp.webapp> (def ignored (->> (repeatedly 10000 #(hash-map :time (rand) :temp (rand))) 190 | (vec) 191 | (aset js/window "AAAMemTest-Mapseq"))) 192 | #'testapp.webapp/ignored 193 | ``` 194 | 195 | ![heap profile](docs/images/memcomp.png) 196 | 197 | 198 | ## Gotchas 199 | 200 | 201 | The js `:uint64` and `:int64` typed arrays produce BigNum objects. When setting values 202 | I transparently upcast to bignum but when getting values back you get bignums so 203 | be careful. I suggest avoiding using them unless you are sure of what you are 204 | doing. You can cast a bignum back to a number, with potential numeric loss, 205 | with `(js/Number. bn)`. This is unfortunate because the default clojure number 206 | type on the jvm is a long and a dataset of longs has a column of type `:int64` and 207 | thus if you naively send it over the wire you will have issues trying to work with 208 | that dataset in your code. 209 | 210 | 211 | ## Development 212 | 213 | This is what I have so far to make development quick 214 | 215 | ### Get a REPL 216 | 217 | * clj -M:cljs node-repl 218 | * cider-connect to port 8777 once it starts 219 | * `(shadow/repl :node-repl)` 220 | 221 | ### Unit Tests 222 | 223 | There is a test script - `scripts/run-tests` that does: 224 | 225 | * clj -M:cljs compile test 226 | * node target/test.js 227 | 228 | ### Install locally and try on different project 229 | 230 | * scripts/install-local 231 | 232 | ## Talk About It 233 | 234 | * [Zulip Data Science/tech.ml.dataset](https://clojurians.zulipchat.com/#narrow/stream/151924-data-science/topic/tech.2Eml.2Edataset) 235 | * [Zulip tech.ml.dataset.dev](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev) 236 | 237 | ### License 238 | 239 | * MIT 240 | -------------------------------------------------------------------------------- /src/tech/v3/datatype/argops.cljs: -------------------------------------------------------------------------------- 1 | (ns tech.v3.datatype.argops 2 | "Index-space algorithms. Implements a subset of the jvm-version." 3 | (:require [tech.v3.datatype.base :as dt-base] 4 | [tech.v3.datatype.casting :as casting] 5 | [tech.v3.datatype.copy-make-container :as dt-cmc] 6 | [tech.v3.datatype.list :as dt-list] 7 | [tech.v3.datatype.protocols :as dt-proto] 8 | [ham-fisted.api :as hamf])) 9 | 10 | 11 | (defn argsort 12 | "Return an array of indexes that order the provided data by compare-fn. compare-fn must 13 | be a boolean function such as < or >. You can use a full custom comparator returning 14 | -1,0 or 1 by using the `:comparator` option. 15 | 16 | * `compare-fn` - Boolean binary predicate such as < or >. 17 | 18 | Options: 19 | 20 | * `:nan-strategy` - defaults to `:last` - if the data has a numeric elemwise-datatype, a 21 | nan-aware comparsing will be used that will place nan data first, last, or throw an exception 22 | as specified by the three possible options - `:first`, `:last`, and `:exception`. 23 | * `:comparator` - comparator to use. This overrides compare-fn and takes two arguments 24 | but returns a number. 25 | 26 | 27 | Examples: 28 | 29 | ```clojure 30 | cljs.user> ;;Persistent vectors do not indicate datatype so nan-aware comparison is disabled. 31 | cljs.user> (argops/argsort [##NaN 1 2 3 ##NaN]) 32 | #typed-buffer[[:int32 5][0 1 2 3 4] 33 | cljs.user> ;;But with a container that indicates datatype nan will be handled 34 | cljs.user> (argops/argsort (dtype/make-container :float32 [##NaN 1 2 3 ##NaN])) 35 | #typed-buffer[[:int32 5][1 2 3 4 0] 36 | cljs.user> ;;example setting nan strategy and using custom comparator. 37 | cljs.user> (argops/argsort nil ;;no compare fn 38 | {:nan-strategy :first 39 | :comparator #(compare %2 %1)} 40 | (dtype/make-container :float32 [##NaN 1 2 3 ##NaN])) 41 | #typed-buffer[[:int32 5][0 4 3 2 1] 42 | ```" 43 | ([compare-fn options data] 44 | (let [comp (cond 45 | compare-fn 46 | (comparator compare-fn) 47 | (:comparator options) 48 | (:comparator options) 49 | :else 50 | compare) 51 | data (dt-base/ensure-indexable data) 52 | n-data (count data) 53 | indexes (dt-cmc/make-container :int32 (range n-data)) 54 | idx-ary (dt-base/as-typed-array indexes) 55 | nan-strategy (get options :nan-strategy :last)] 56 | ;;agetable is a major optimization for sorting. element access time means a lot 57 | ;;for a large nlogn op. 58 | (let [missing? (if (casting/numeric-type? (dt-base/elemwise-datatype data)) 59 | js/isNaN 60 | nil?) 61 | [data get-fn] (if-let [aget-data (dt-base/as-agetable data)] 62 | [aget-data aget] 63 | [data nth]) 64 | sort-fn (fn [lhs-idx rhs-idx] 65 | (let [lhs (get-fn data lhs-idx) 66 | rhs (get-fn data rhs-idx) 67 | lhs-nan? (missing? lhs) 68 | rhs-nan? (missing? rhs)] 69 | (if (or lhs-nan? rhs-nan?) 70 | (condp = nan-strategy 71 | :exception 72 | (throw (js/Error "NaN detected")) 73 | :last (if lhs-nan? 1 -1) 74 | :first (if lhs-nan? -1 1)) 75 | (comp lhs rhs))))] 76 | (.sort idx-ary sort-fn)) 77 | indexes)) 78 | ([compare-fn data] 79 | (argsort compare-fn nil data)) 80 | ([data] 81 | (argsort nil nil data))) 82 | 83 | 84 | (defn numeric-truthy 85 | [val] 86 | (if (number? val) 87 | (and (not (js/isNaN val)) 88 | (not= 0 val)) 89 | val)) 90 | 91 | (deftype IndexReducer [list 92 | ^:unsynchronized-mutable start 93 | ^:unsynchronized-mutable last-val 94 | ^:unsynchronized-mutable increment] 95 | Object 96 | (accept [this elem] 97 | (if (nil? start) 98 | (set! start elem) 99 | (let [new-inc (- elem last-val)] 100 | (cond 101 | (== new-inc increment) nil ;;do nothing 102 | (js/isNaN increment) 103 | (dt-proto/-add list elem) 104 | (and (not= 0 new-inc) (nil? increment)) 105 | (set! increment new-inc) 106 | :else 107 | (when (not= increment new-inc) 108 | (cond 109 | (nil? increment) 110 | (do 111 | (dt-proto/-add list start) 112 | (dt-proto/-add list elem)) 113 | :else 114 | (do 115 | (reduce #(dt-proto/-add %1 %2) 116 | list 117 | (range start (+ last-val increment) increment)) 118 | (dt-proto/-add list elem))) 119 | (set! increment js/NaN))))) 120 | (set! last-val elem)) 121 | IDeref 122 | (-deref [this] 123 | (with-meta 124 | (cond 125 | (nil? start) [] 126 | (js/isNaN increment) 127 | (dt-base/sub-buffer list 0 (dt-base/ecount list)) 128 | :else 129 | (if (nil? increment) 130 | (hamf/range start (inc start)) 131 | (hamf/range start (+ last-val (or increment (- last-val start))) increment))) 132 | {::processed true}))) 133 | 134 | 135 | (defn index-reducer [] (IndexReducer. (dt-list/make-list :int32) nil nil nil)) 136 | 137 | 138 | (defn index-reducer-rf 139 | "Return a transduce-compatible index scanning rf." 140 | ([] (index-reducer)) 141 | ([acc v] (.accept ^JS acc v) acc) 142 | ([acc] (deref acc))) 143 | 144 | 145 | (defn argfilter 146 | "Return an array of indexes that pass the filter." 147 | ([pred options data] 148 | (-> (reduce (hamf/indexed-accum-fn 149 | (fn [acc idx v] 150 | (when (numeric-truthy (pred v)) 151 | (.accept ^JS acc idx)) 152 | acc)) 153 | (index-reducer) 154 | data) 155 | (deref))) 156 | ([pred data] (argfilter pred nil data)) 157 | ;;In this case the data itself must be truthy. 158 | ;;Avoids the use of an unnecessary predicate fn 159 | ([data] 160 | (-> (reduce (hamf/indexed-accum-fn 161 | (fn [acc idx v] 162 | (when (numeric-truthy v) 163 | (.accept ^JS acc idx)) 164 | acc)) 165 | (index-reducer) 166 | data) 167 | (deref)))) 168 | 169 | 170 | (defn arggroup 171 | "Return a map from value->indexes that hold that value." 172 | [data] 173 | (let [afn (fn [_k] (index-reducer))] 174 | (-> (reduce (hamf/indexed-accum-fn 175 | (fn [acc idx v] 176 | (let [l (.computeIfAbsent ^JS acc v afn)] 177 | (.accept ^JS l idx)) 178 | acc)) 179 | (hamf/mut-map) 180 | data) 181 | (hamf/update-values (fn [k v] (deref v))) 182 | (persistent!)))) 183 | 184 | 185 | (defn arglast-every 186 | "Return the last index where (pred (rdr idx) (rdr (dec idx))) was true by 187 | comparing every value and keeping track of the last index where pred was true." 188 | [rdr pred] 189 | (let [rdr (dt-base/ensure-indexable rdr) 190 | n-elems (count rdr)] 191 | (if-let [rdr (dt-base/as-agetable rdr)] 192 | (loop [idx 1 193 | max-idx 0 194 | max-value (aget rdr 0)] 195 | (if (== n-elems idx) 196 | max-idx 197 | (let [cur-val (aget rdr idx) 198 | found? (pred cur-val max-value)] 199 | (recur (unchecked-inc idx) 200 | (if found? idx max-idx) 201 | (if found? cur-val max-value))))) 202 | (loop [idx 1 203 | max-idx 0 204 | max-value (nth rdr 0)] 205 | (if (== n-elems idx) 206 | max-idx 207 | (let [cur-val (nth rdr idx) 208 | found? (pred cur-val max-value)] 209 | (recur (unchecked-inc idx) 210 | (if found? idx max-idx) 211 | (if found? cur-val max-value)))))))) 212 | 213 | 214 | (defn argmax 215 | "Return the last index of the max item in the reader." 216 | [rdr] 217 | (arglast-every rdr >)) 218 | 219 | 220 | (defn argmin 221 | "Return the last index of the min item in the reader." 222 | [rdr] 223 | (arglast-every rdr <)) 224 | 225 | 226 | (defn binary-search 227 | "Returns a long result that points to just before the value or exactly points to the 228 | value. In the case where the target is after the last value will return 229 | elem-count. If value is present multiple times the index will point to the first 230 | value. 231 | 232 | Options: 233 | 234 | * `:comparator` - a specific comparator to use; defaults to `comparator`." 235 | ([data target options] 236 | (let [comp (get options :comparator compare) 237 | data (dt-base/ensure-indexable data) 238 | n-elems (count data)] 239 | (loop [low (long 0) 240 | high n-elems] 241 | (if (< low high) 242 | (let [mid (+ low (quot (- high low) 2)) 243 | buf-data (nth data mid) 244 | compare-result (comp buf-data target)] 245 | (if (== 0 compare-result) 246 | (recur mid mid) 247 | (if (and (< compare-result 0) 248 | (not= mid low)) 249 | (recur mid high) 250 | (recur low mid)))) 251 | (loop [low low] 252 | (let [buf-data (nth data low) 253 | comp (comp target buf-data)] 254 | (cond 255 | (or (< comp 0) (== 0 low)) low 256 | (> comp 0) (unchecked-inc low) 257 | ;;When values are equal, track backward to first non-equal member. 258 | :else 259 | (recur (unchecked-dec low))))))))) 260 | ([data target] 261 | (binary-search data target nil))) 262 | -------------------------------------------------------------------------------- /docs/highlight/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); -------------------------------------------------------------------------------- /docs/tech.v3.datatype.argops.html: -------------------------------------------------------------------------------- 1 | 3 | tech.v3.datatype.argops documentation

tech.v3.datatype.argops

Index-space algorithms. Implements a subset of the jvm-version.

8 |

argfilter

(argfilter pred options data)(argfilter pred data)(argfilter data)

Return an array of indexes that pass the filter.

9 |

arggroup

(arggroup data)

Return a map from value->indexes that hold that value.

10 |

arglast-every

(arglast-every rdr pred)

Return the last index where (pred (rdr idx) (rdr (dec idx))) was true by 11 | comparing every value and keeping track of the last index where pred was true.

12 |

argmax

(argmax rdr)

Return the last index of the max item in the reader.

13 |

argmin

(argmin rdr)

Return the last index of the min item in the reader.

14 |

argsort

(argsort compare-fn options data)(argsort compare-fn data)(argsort data)

Return an array of indexes that order the provided data by compare-fn. compare-fn must 15 | be a boolean function such as < or >. You can use a full custom comparator returning 16 | -1,0 or 1 by using the :comparator option.

17 |
    18 |
  • compare-fn - Boolean binary predicate such as < or >.
  • 19 |
20 |

Options:

21 |
    22 |
  • :nan-strategy - defaults to :last - if the data has a numeric elemwise-datatype, a 23 | nan-aware comparsing will be used that will place nan data first, last, or throw an exception 24 | as specified by the three possible options - :first, :last, and :exception.
  • 25 |
  • :comparator - comparator to use. This overrides compare-fn and takes two arguments 26 | but returns a number.
  • 27 |
28 |

Examples:

29 |
cljs.user> ;;Persistent vectors do not indicate datatype so nan-aware comparison is disabled.
30 | cljs.user> (argops/argsort [##NaN 1 2 3 ##NaN])
31 | #typed-buffer[[:int32 5][0 1 2 3 4]
32 | cljs.user> ;;But with a container that indicates datatype nan will be handled
33 | cljs.user> (argops/argsort (dtype/make-container :float32 [##NaN 1 2 3 ##NaN]))
34 |  #typed-buffer[[:int32 5][1 2 3 4 0]
35 | cljs.user> ;;example setting nan strategy and using custom comparator.
36 | cljs.user> (argops/argsort nil  ;;no compare fn
37 |                            {:nan-strategy :first
38 |                             :comparator #(compare %2 %1)}
39 |                            (dtype/make-container :float32 [##NaN 1 2 3 ##NaN]))
40 | #typed-buffer[[:int32 5][0 4 3 2 1]
41 | 
42 |

index-reducer

(index-reducer)

index-reducer-rf

(index-reducer-rf)(index-reducer-rf acc v)(index-reducer-rf acc)

Return a transduce-compatible index scanning rf.

51 |

IndexReducer

numeric-truthy

(numeric-truthy val)
-------------------------------------------------------------------------------- /test/data/stocks.transit-json: -------------------------------------------------------------------------------- 1 | ["~#tech.v3.dataset",["^ ","~:metadata",["^ ","~:name","https://github.com/techascent/tech.ml.dataset/raw/master/test/data/stocks.csv"],"~:flavor","~:transit","~:version",1,"~:columns",[["^ ","^1",["^ ","~:categorical?",true,"^2","~:symbol","~:datatype","~:string","~:n-elems",560],"~:missing",[],"~:data",["^ ","~:strtable",["MSFT","AMZN","IBM","GOOG","AAPL"],"~:indexes","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAA="]],["^ ","^1",["^ ","^2","~:date","^9","~:local-date","^;",560],"^<",[],"^=","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"],["^ ","^1",["^ ","^2","~:price","^9","~:float64","^;",560],"^<",[],"^=","SOF6FK7nQ0DNzMzMzCxCQFyPwvUonEVAH4XrUbhePEAzMzMzM3M5QIXrUbgeRUBAZmZmZmZmPEBmZmZmZmY8QEjhehSuhzhAhetRuB4FPEDXo3A9Clc3QGZmZmZmpjFA16NwPQrXOEAAAAAAAAA4QAAAAAAAQDZAj8L1KFyPO0CkcD0K1yM8QDMzMzMzsz1ArkfhehTuOkD2KFyPwjU3QFK4HoXr0TRAZmZmZmamN0AfhetRuB46QDMzMzMz8zpA7FG4HoXrOUB7FK5H4bo3QEjhehSuhzhAw/UoXI9CNUD2KFyPwrU0QAAAAAAAQDZAhetRuB6FM0C4HoXrUfgzQArXo3A9yjFAAAAAAADANUD2KFyPwnU3QEjhehSuBzVAj8L1KFxPM0DXo3A9ClczQMP1KFyPwjNAH4XrUbjeNEDXo3A9Chc0QK5H4XoU7jRAj8L1KFyPNUBmZmZmZqY1QHE9CtejsDZAMzMzMzNzNUCamZmZmRk1QPYoXI/CdTZAcT0K16OwNkCF61G4HsU1QPYoXI/CdTRAMzMzMzNzNUBI4XoUroc1QHE9CtejcDdA4XoUrkdhN0C4HoXrUXg2QMP1KFyPwjZAhetRuB4FN0CamZmZmZk4QIXrUbgehThAXI/C9SgcOEBmZmZmZiY3QD0K16NwPTZASOF6FK5HN0BSuB6F69E3QK5H4XoU7jZApHA9CtejN0CamZmZmVk5QBSuR+F61DdAzczMzMzMN0D2KFyPwrU5QArXo3A9SjhApHA9CtcjOkAK16NwPQo5QFyPwvUoXDlAAAAAAACANkBxPQrXozA1QM3MzMzMzDVAw/UoXI+CNkDhehSuRyE4QK5H4XoUrjlA9ihcj8L1OkApXI/C9ag7QOF6FK5HITxAUrgehesRPUDhehSuR6E6QJqZmZmZWTpAzczMzMxMPEBcj8L1KBw9QDMzMzMz8ztAAAAAAACAO0DXo3A9Clc7QArXo3A9CjxApHA9CteDQUDsUbgehQtAQAAAAAAAAEFA4XoUrkchP0BSuB6F6xE6QPYoXI/CNTtA16NwPQpXO0AAAAAAAEA7QLgehetReDpAAAAAAADAOEBcj8L1KFw6QEjhehSuxzlAUrgeheuRNUApXI/C9agzQClcj8L16DJA4XoUrkehMEAfhetRuJ4vQD0K16Nw/TFA16NwPQrXM0DXo3A9Cpc0QOxRuB6FazdArkfhehQuN0CuR+F6FG44QD0K16NwfTlAexSuR+F6O0CF61G4HkU9QNejcD0KVz5AzczMzMwMPEDsUbgehas8QM3MzMzMzDxApHA9CtcjUEBI4XoUrjdRQAAAAAAAwFBAuB6F61GYS0BI4XoUridIQEjhehSuJ0JAH4XrUbgePkAAAAAAAMBEQLgehetROENAj8L1KFxPQkBxPQrXo7A4QB+F61G4Hi9Aj8L1KFxPMUDhehSuR2EkQPYoXI/CdSRAj8L1KFyPL0BxPQrXo7AwQM3MzMzMTCxAexSuR+H6KEDhehSuR+EhQOF6FK5H4RdA7FG4HoXrG0CkcD0K16MmQKRwPQrXoyVA4XoUrkdhLEAzMzMzMzMsQJqZmZmZmSxAcT0K16OwMEB7FK5H4ToyQAAAAAAAQDBAZmZmZmbmLEDhehSuR+EtQFyPwvUo3C9AXI/C9ShcM0CamZmZmVk3QKRwPQrX4zJAmpmZmZnZNUDD9ShcjwI2QEjhehSuBzpAcT0K16OwPEBSuB6F6/FBQClcj8L1KEJAUrgehevRREApXI/C9ShHQNejcD0KN0hA16NwPQo3S0Bcj8L1KPxKQI/C9ShcT0pAMzMzMzMzSUDhehSuR4FFQKRwPQrXo0VAzczMzMzMRUAAAAAAAEBIQDMzMzMzM0tA9ihcj8J1Q0BSuB6F6xFDQK5H4XoUbkRAcT0K16MQQUDXo3A9CtdDQIXrUbgeJUZAXI/C9SicRUDXo3A9CpdBQMP1KFyPIkFArkfhehQuQEDhehSuR8FBQOxRuB6Fi0BAMzMzMzOTRkCamZmZmVlFQGZmZmZmpkZArkfhehTuQ0B7FK5H4TpIQDMzMzMzk0dAKVyPwvVoRkC4HoXrUbhCQKRwPQrXQ0JAexSuR+GaQUCuR+F6FE5BQNejcD0KV0NApHA9CtfjOkAUrkfhetQ+QI/C9ShcD0BA7FG4HoULQ0DsUbgehStEQHsUrkfhukNA9ihcj8LVQkBSuB6F65FDQIXrUbge5UNACtejcD2qTkApXI/C9UhRQArXo3A9GlFAw/UoXI+iU0AK16NwPfpTQJqZmZmZSVdAmpmZmZlJVkCkcD0K16NWQClcj8L1KFdAzczMzMxsU0CuR+F6FB5QQDMzMzMz01FAuB6F61GoU0BI4XoUrmdUQIXrUbgeVVJA9ihcj8IVU0CkcD0K1zNUQHE9CtejMFJAH4XrUbieTECamZmZmVlFQKRwPQrXo0lAKVyPwvVoTUDD9ShcjzJQQFyPwvUoXFJA4XoUrkchVECPwvUoXH9TQArXo3A96lRAcT0K16NwVUBcj8L1KExUQNejcD0KV1dApHA9CtezXUCF61G4Hv1gQHE9Ctej0GBACtejcD1aX0CamZmZmZldQArXo3A9GmBA4XoUrkchWUDXo3A9CgdXQNejcD0Kh1pAzczMzMz8WECkcD0K1xNYQIXrUbgelVhAj8L1KFwvWUBI4XoUrqddQFyPwvUoTFlAAAAAAAAgVkBI4XoUrgdVQK5H4XoUHlNAcT0K16MwWUAfhetRuH5WQLgehetRqFVAzczMzMzsWUAUrkfhejRZQGZmZmZmlllASOF6FK63V0AAAAAAAJBWQBSuR+F6tFRAhetRuB5lWEAAAAAAACBaQNejcD0KV1tAw/UoXI9iWEAUrkfhejRWQJqZmZmZiVdAFK5H4Xr0UkCuR+F6FD5SQKRwPQrXU1BArkfhehTuT0DhehSuRyFRQOF6FK5HgUpAcT0K16PwUUAK16NwPcpTQIXrUbgepVFArkfhehTOUUC4HoXrUchRQBSuR+F65FFArkfhehReU0AfhetRuB5UQHsUrkfh2lJAUrgeheuRUkBI4XoUrsdSQArXo3A9OlRAPQrXo3B9VECF61G4HsVUQDMzMzMzQ1VApHA9CtfDVkDNzMzMzCxWQArXo3A9GlVAw/UoXI9CVED2KFyPwmVUQFyPwvUoTFRAXI/C9SgMVEB7FK5H4YpTQLgehetRyFNA9ihcj8K1VECamZmZmclVQArXo3A9ylZAKVyPwvWYVUBSuB6F63FVQArXo3A9KlVA4XoUrkexUUDsUbgehYtRQOxRuB6FO1FAUrgehethU0AUrkfhesRSQM3MzMzMrFJAAAAAAAAQU0AfhetRuL5UQB+F61G4LlNAKVyPwvX4UkD2KFyPwsVSQHsUrkfhSlNAMzMzMzNDU0DD9Shcj8JSQJqZmZmZCVJAzczMzMwsUkBmZmZmZhZTQHE9CtejUFNApHA9CtfDVUDNzMzMzLxVQJqZmZmZ+VZAw/UoXI9yV0DsUbgehQtWQFyPwvUoXFZAH4XrUbg+WEDD9Shcj2JZQAAAAAAAEFlAmpmZmZlZWkDD9Shcj+JbQGZmZmZmJlxAAAAAAADAW0CamZmZmTlZQM3MzMzM7FlAAAAAAACwWUApXI/C9WhbQEjhehSut1tAH4XrUbgOXUApXI/C9UhfQGZmZmZmplxAj8L1KFzvXkAK16NwPYpdQFK4HoXrYVxAj8L1KFyPVkCamZmZmelTQJqZmZmZiVRAPQrXo3BdVkAUrkfhepRWQPYoXI/CxVdAw/UoXI9SWUBmZmZmZjZaQHE9CtejwFlA9ihcj8IVXUAAAAAAAEBdQDMzMzMzo11Aw/UoXI/iXUDD9Shcj3JfQArXo3A9SmBAZmZmZmZ2XkAK16NwPcpfQDMzMzMzY19ASOF6FK6XWUAzMzMzMzNgQBSuR+F61GdAj8L1KFy/ZkDhehSuRxloQKRwPQrXc2hASOF6FK5/Z0C4HoXrUZBmQAAAAAAAgGtAuB6F61FUcUBmZmZmZmJyQFyPwvUo/HFAAAAAAADgcUCPwvUoXMdzQArXo3A9QndAw/UoXI9OeUD2KFyPwu15QMP1KFyPCntAUrgeheupdkAAAAAAAGB4QNejcD0KH3pAhetRuB49d0DhehSuRzV6QJqZmZmZKXhAFK5H4Xqod0BmZmZmZh55QArXo3A9xn1AKVyPwvVMfkBI4XoUrsd8QAAAAAAAWH9AMzMzMzMXfEDD9Shcj6J8QK5H4XoUdn1Aw/UoXI8ef0CamZmZmVWAQAAAAAAA4H9AAAAAAAAagEBcj8L1KLqBQAAAAAAAGIZAAAAAAACohUCkcD0K15uFQGZmZmZmooFAexSuR+FyfUDsUbgehYd7QLgehetR8oFAZmZmZmZOgkCPwvUoXHOAQAAAAAAAnH1AcT0K16P0fEC4HoXrUQh5QPYoXI/CdXZAj8L1KFxPckBmZmZmZjpzQBSuR+F6KHVApHA9CtcfdUApXI/C9cB1QOxRuB6Fv3hASOF6FK4TekA9CtejcFl6QM3MzMzMsHtAH4XrUbjafECamZmZmf1+QClcj8L1wIBAAAAAAAA4gkCkcD0K11+DQOxRuB6Fj4BAZmZmZmZ2gEDsUbgehYGBQHE9Ctej8DlAKVyPwvWoPECamZmZmflAQMP1KFyPAj9AAAAAAAAANUBxPQrXozA6QClcj8L1aDlAuB6F61F4PkDD9Shcj8IpQI/C9ShcjyNAAAAAAACAIEDD9Shcj8IdQB+F61G4niVAPQrXo3A9IkCPwvUoXA8mQHsUrkfheilA9ihcj8L1I0A9CtejcD0nQM3MzMzMzCJACtejcD2KIkAK16NwPQofQI/C9ShcjyFAzczMzMxMJUBmZmZmZuYlQLgehetRuChAMzMzMzOzJUCuR+F6FK4nQEjhehSuRyhAzczMzMxMJ0C4HoXrUbghQIXrUbgehR5AhetRuB6FHUAAAAAAAAAdQI/C9ShcDyBAAAAAAAAAH0CkcD0K16McQLgehetRuBxACtejcD0KHkBI4XoUrkccQHE9CtejcBxA9ihcj8L1IUCPwvUoXA8jQBSuR+F6FCVAH4XrUbieJkC4HoXrUbgkQOF6FK5H4SZAZmZmZmbmJEDhehSuR2ElQI/C9ShcjyZA7FG4HoXrJ0AK16NwPQorQEjhehSuxylAj8L1KFwPLECF61G4HkUwQOxRuB6FKzBAAAAAAABAMUDhehSuR2EzQDMzMzMzMzpApHA9CtfDQECamZmZmRlAQJqZmZmZOUNArkfhehRuRkD2KFyPwtVEQEjhehSuB0JA4XoUrkfhQ0BI4XoUrmdCQDMzMzMzU0VAUrgehetxR0CuR+F6FM5KQOxRuB6Fy0xAFK5H4Xr0UEApXI/C9fhRQHE9Ctej4FJAj8L1KFwfUUBcj8L1KFxPQClcj8L1mFFAw/UoXI/iTUDD9Shcj6JMQD0K16Nw/VBAZmZmZmb2UEAfhetRuD5TQIXrUbgeRVRACtejcD3qVkD2KFyPwjVVQB+F61G4blVA16NwPQonVUAK16NwPTpXQDMzMzMz81hAXI/C9ShMXkDD9Shcj4JeQLgehetReGBAj8L1KFxPYUDXo3A9Ci9jQGZmZmZmvmdA16NwPQrHZkDD9Shcj8JoQOxRuB6F62BA4XoUrkdBX0AAAAAAAPBhQGZmZmZmvmVAAAAAAACYZ0CuR+F6FO5kQGZmZmZm3mNAKVyPwvUwZUAK16NwPWpcQPYoXI/C5VpAexSuR+EqV0BmZmZmZlZVQLgehetRiFZApHA9CtdTVkBI4XoUrkdaQIXrUbgedV9AUrgehev5YED2KFyPws1hQBSuR+F6bGRAH4XrUbgGZUAzMzMzMytnQAAAAAAAkGdAhetRuB79aECPwvUoXFdqQFK4HoXrAWhApHA9CteTaUBxPQrXo+BrQA=="]]]] -------------------------------------------------------------------------------- /docs/css/default.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=PT+Sans'); 2 | 3 | body { 4 | font-family: 'PT Sans', Helvetica, sans-serif; 5 | font-size: 14px; 6 | } 7 | 8 | a { 9 | color: #337ab7; 10 | text-decoration: none; 11 | } 12 | 13 | a:hover { 14 | color: #30426a; 15 | text-decoration: underline; 16 | } 17 | 18 | pre, code { 19 | font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; 20 | font-size: 9pt; 21 | margin: 15px 0; 22 | } 23 | 24 | h1 { 25 | font-weight: normal; 26 | font-size: 29px; 27 | margin: 10px 0 2px 0; 28 | padding: 0; 29 | } 30 | 31 | h2 { 32 | font-weight: normal; 33 | font-size: 25px; 34 | } 35 | 36 | h3 > a:hover { 37 | text-decoration: none; 38 | } 39 | 40 | .document h1, .namespace-index h1 { 41 | font-size: 32px; 42 | margin-top: 12px; 43 | } 44 | 45 | #header, #content, .sidebar { 46 | position: fixed; 47 | } 48 | 49 | #header { 50 | top: 0; 51 | left: 0; 52 | right: 0; 53 | height: 22px; 54 | color: #f5f5f5; 55 | padding: 5px 7px; 56 | } 57 | 58 | #content { 59 | top: 32px; 60 | right: 0; 61 | bottom: 0; 62 | overflow: auto; 63 | background: #fff; 64 | color: #333; 65 | padding: 0 18px; 66 | } 67 | 68 | .sidebar { 69 | position: fixed; 70 | top: 32px; 71 | bottom: 0; 72 | overflow: auto; 73 | } 74 | 75 | .sidebar.primary { 76 | background: #30426a; 77 | border-right: solid 1px #cccccc; 78 | left: 0; 79 | width: 250px; 80 | color: white; 81 | font-size: 110%; 82 | } 83 | 84 | .sidebar.secondary { 85 | background: #f2f2f2; 86 | border-right: solid 1px #d7d7d7; 87 | left: 251px; 88 | width: 200px; 89 | font-size: 110%; 90 | } 91 | 92 | #content.namespace-index, #content.document { 93 | left: 251px; 94 | } 95 | 96 | #content.namespace-docs { 97 | left: 452px; 98 | } 99 | 100 | #content.document { 101 | padding-bottom: 10%; 102 | } 103 | 104 | #header { 105 | background: #2d3e63; 106 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 107 | z-index: 100; 108 | } 109 | 110 | #header h1 { 111 | margin: 0; 112 | padding: 0; 113 | font-size: 18px; 114 | font-weight: lighter; 115 | text-shadow: -1px -1px 0px #333; 116 | } 117 | 118 | #header h1 .project-version { 119 | font-weight: normal; 120 | } 121 | 122 | .project-version { 123 | padding-left: 0.15em; 124 | } 125 | 126 | #header a, .sidebar a { 127 | display: block; 128 | text-decoration: none; 129 | } 130 | 131 | #header a { 132 | color: #f5f5f5; 133 | } 134 | 135 | .sidebar.primary, .sidebar.primary a { 136 | color: #b2bfdc; 137 | } 138 | 139 | .sidebar.primary a:hover { 140 | color: white; 141 | } 142 | 143 | .sidebar.secondary, .sidebar.secondary a { 144 | color: #738bc0; 145 | } 146 | 147 | .sidebar.secondary a:hover { 148 | color: #2d3e63; 149 | } 150 | 151 | #header h2 { 152 | float: right; 153 | font-size: 9pt; 154 | font-weight: normal; 155 | margin: 4px 3px; 156 | padding: 0; 157 | color: #bbb; 158 | } 159 | 160 | #header h2 a { 161 | display: inline; 162 | } 163 | 164 | .sidebar h3 { 165 | margin: 0; 166 | padding: 10px 13px 0 13px; 167 | font-size: 19px; 168 | font-weight: lighter; 169 | } 170 | 171 | .sidebar.primary h3.no-link { 172 | text-transform: uppercase; 173 | font-size: 12px; 174 | color: #738bc0; 175 | } 176 | 177 | .sidebar.secondary h3 a { 178 | text-transform: uppercase; 179 | font-size: 12px; 180 | color: #2d3e63; 181 | } 182 | 183 | .sidebar ul { 184 | padding: 7px 0 6px 0; 185 | margin: 0; 186 | } 187 | 188 | .sidebar ul.index-link { 189 | padding-bottom: 4px; 190 | } 191 | 192 | .sidebar li { 193 | display: block; 194 | vertical-align: middle; 195 | } 196 | 197 | .sidebar li a, .sidebar li .no-link { 198 | border-left: 3px solid transparent; 199 | padding: 0 10px; 200 | white-space: nowrap; 201 | } 202 | 203 | .sidebar li .inner { 204 | display: inline-block; 205 | padding-top: 7px; 206 | height: 24px; 207 | } 208 | 209 | .sidebar li a, .sidebar li .tree { 210 | height: 31px; 211 | } 212 | 213 | .depth-1 .inner { padding-left: 2px; } 214 | .depth-2 .inner { padding-left: 6px; } 215 | .depth-3 .inner { padding-left: 20px; } 216 | .depth-4 .inner { padding-left: 34px; } 217 | .depth-5 .inner { padding-left: 48px; } 218 | .depth-6 .inner { padding-left: 62px; } 219 | 220 | .sidebar li .tree { 221 | display: block; 222 | float: left; 223 | position: relative; 224 | top: -10px; 225 | margin: 0 4px 0 0; 226 | padding: 0; 227 | } 228 | 229 | .sidebar li.depth-1 .tree { 230 | display: none; 231 | } 232 | 233 | .sidebar li .tree .top, .sidebar li .tree .bottom { 234 | display: block; 235 | margin: 0; 236 | padding: 0; 237 | width: 7px; 238 | } 239 | 240 | .sidebar li .tree .top { 241 | border-left: 1px solid #aaa; 242 | border-bottom: 1px solid #aaa; 243 | height: 19px; 244 | } 245 | 246 | .sidebar li .tree .bottom { 247 | height: 22px; 248 | } 249 | 250 | .sidebar li.branch .tree .bottom { 251 | border-left: 1px solid #aaa; 252 | } 253 | 254 | .sidebar.primary li.current a { 255 | border-left: 3px solid #e99d1a; 256 | color: white; 257 | } 258 | 259 | .sidebar.secondary li.current a { 260 | border-left: 3px solid #2d3e63; 261 | color: #33a; 262 | } 263 | 264 | .namespace-index h2 { 265 | margin: 30px 0 0 0; 266 | } 267 | 268 | .namespace-index h3 { 269 | font-size: 16px; 270 | font-weight: bold; 271 | margin-bottom: 0; 272 | letter-spacing: 0.05em; 273 | border-bottom: solid 1px #ddd; 274 | max-width: 680px; 275 | background-color: #fafafa; 276 | padding: 0.5em; 277 | } 278 | 279 | .namespace-index .topics { 280 | padding-left: 30px; 281 | margin: 11px 0 0 0; 282 | } 283 | 284 | .namespace-index .topics li { 285 | padding: 5px 0; 286 | } 287 | 288 | .namespace-docs h3 { 289 | font-size: 18px; 290 | font-weight: bold; 291 | } 292 | 293 | .public h3 { 294 | margin: 0; 295 | float: left; 296 | } 297 | 298 | .usage { 299 | clear: both; 300 | } 301 | 302 | .public { 303 | margin: 0; 304 | border-top: 1px solid #e0e0e0; 305 | padding-top: 14px; 306 | padding-bottom: 6px; 307 | } 308 | 309 | .public:last-child { 310 | margin-bottom: 20%; 311 | } 312 | 313 | .members .public:last-child { 314 | margin-bottom: 0; 315 | } 316 | 317 | .members { 318 | margin: 15px 0; 319 | } 320 | 321 | .members h4 { 322 | color: #555; 323 | font-weight: normal; 324 | font-variant: small-caps; 325 | margin: 0 0 5px 0; 326 | } 327 | 328 | .members .inner { 329 | padding-top: 5px; 330 | padding-left: 12px; 331 | margin-top: 2px; 332 | margin-left: 7px; 333 | border-left: 1px solid #bbb; 334 | } 335 | 336 | #content .members .inner h3 { 337 | font-size: 12pt; 338 | } 339 | 340 | .members .public { 341 | border-top: none; 342 | margin-top: 0; 343 | padding-top: 6px; 344 | padding-bottom: 0; 345 | } 346 | 347 | .members .public:first-child { 348 | padding-top: 0; 349 | } 350 | 351 | h4.type, 352 | h4.dynamic, 353 | h4.added, 354 | h4.deprecated { 355 | float: left; 356 | margin: 3px 10px 15px 0; 357 | font-size: 15px; 358 | font-weight: bold; 359 | font-variant: small-caps; 360 | } 361 | 362 | .public h4.type, 363 | .public h4.dynamic, 364 | .public h4.added, 365 | .public h4.deprecated { 366 | font-size: 13px; 367 | font-weight: bold; 368 | margin: 3px 0 0 10px; 369 | } 370 | 371 | .members h4.type, 372 | .members h4.added, 373 | .members h4.deprecated { 374 | margin-top: 1px; 375 | } 376 | 377 | h4.type { 378 | color: #717171; 379 | } 380 | 381 | h4.dynamic { 382 | color: #9933aa; 383 | } 384 | 385 | h4.added { 386 | color: #508820; 387 | } 388 | 389 | h4.deprecated { 390 | color: #880000; 391 | } 392 | 393 | .namespace { 394 | margin-bottom: 30px; 395 | } 396 | 397 | .namespace:last-child { 398 | margin-bottom: 10%; 399 | } 400 | 401 | .index { 402 | padding: 0; 403 | font-size: 80%; 404 | margin: 15px 0; 405 | line-height: 1.6em; 406 | } 407 | 408 | .index * { 409 | display: inline; 410 | } 411 | 412 | .index p { 413 | padding-right: 3px; 414 | } 415 | 416 | .index li { 417 | padding-right: 5px; 418 | } 419 | 420 | .index ul { 421 | padding-left: 0; 422 | } 423 | 424 | .type-sig { 425 | clear: both; 426 | color: #088; 427 | } 428 | 429 | .type-sig pre { 430 | padding-top: 10px; 431 | margin: 0; 432 | } 433 | 434 | .usage code { 435 | display: block; 436 | color: #008; 437 | margin: 2px 0; 438 | } 439 | 440 | .usage code:first-child { 441 | padding-top: 10px; 442 | } 443 | 444 | p { 445 | margin: 15px 0; 446 | } 447 | 448 | .public p:first-child, .public pre.plaintext { 449 | margin-top: 12px; 450 | } 451 | 452 | .doc { 453 | margin: 0 0 26px 0; 454 | clear: both; 455 | } 456 | 457 | .public .doc { 458 | margin: 0; 459 | } 460 | 461 | .namespace-index { 462 | font-size: 120%; 463 | } 464 | 465 | .namespace-index .doc { 466 | margin-bottom: 20px; 467 | } 468 | 469 | .namespace-index .namespace .doc { 470 | margin-bottom: 10px; 471 | } 472 | 473 | .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { 474 | line-height: 1.6em; 475 | } 476 | 477 | .markdown h2 { 478 | font-weight: normal; 479 | font-size: 25px; 480 | } 481 | 482 | #content .markdown h3 { 483 | font-size: 20px; 484 | } 485 | 486 | .markdown h4 { 487 | font-size: 15px; 488 | } 489 | 490 | .doc, .public, .namespace .index { 491 | max-width: 680px; 492 | overflow-x: visible; 493 | } 494 | 495 | .markdown pre > code { 496 | display: block; 497 | padding: 10px; 498 | } 499 | 500 | .markdown pre > code, .src-link a { 501 | border: 1px solid #e4e4e4; 502 | border-radius: 2px; 503 | } 504 | 505 | .src-link a { 506 | background: #f6f6f6; 507 | } 508 | 509 | .markdown code:not(.hljs) { 510 | color: #c7254e; 511 | background-color: #f9f2f4; 512 | border-radius: 4px; 513 | font-size: 90%; 514 | padding: 2px 4px; 515 | } 516 | 517 | pre.deps { 518 | display: inline-block; 519 | margin: 0 10px; 520 | border: 1px solid #e4e4e4; 521 | border-radius: 2px; 522 | padding: 10px; 523 | background-color: #f6f6f6; 524 | } 525 | 526 | .markdown hr { 527 | border-style: solid; 528 | border-top: none; 529 | color: #ccc; 530 | } 531 | 532 | .doc ul, .doc ol { 533 | padding-left: 30px; 534 | } 535 | 536 | .doc table { 537 | border-collapse: collapse; 538 | margin: 0 10px; 539 | } 540 | 541 | .doc table td, .doc table th { 542 | border: 1px solid #dddddd; 543 | padding: 4px 6px; 544 | } 545 | 546 | .doc table th { 547 | background: #f2f2f2; 548 | } 549 | 550 | .doc dl { 551 | margin: 0 10px 20px 10px; 552 | } 553 | 554 | .doc dl dt { 555 | font-weight: bold; 556 | margin: 0; 557 | padding: 3px 0; 558 | border-bottom: 1px solid #ddd; 559 | } 560 | 561 | .doc dl dd { 562 | padding: 5px 0; 563 | margin: 0 0 5px 10px; 564 | } 565 | 566 | .doc abbr { 567 | border-bottom: 1px dotted #333; 568 | font-variant: none; 569 | cursor: help; 570 | } 571 | 572 | .src-link { 573 | margin-bottom: 15px; 574 | } 575 | 576 | .src-link a { 577 | font-size: 70%; 578 | padding: 1px 4px; 579 | text-decoration: none; 580 | color: #5555bb; 581 | background-color: #f6f6f6; 582 | } 583 | 584 | blockquote { 585 | opacity: 0.6; 586 | border-left: solid 2px #ddd; 587 | margin-left: 0; 588 | padding-left: 1em; 589 | } 590 | 591 | /* Responsiveness Theme */ 592 | 593 | @media (max-device-width: 480px) { 594 | .sidebar { 595 | display:none; 596 | } 597 | 598 | #content { 599 | position: relative; 600 | left: initial !important; 601 | top: 110px; 602 | padding: 0 1em; 603 | } 604 | 605 | #header { 606 | display: flex; 607 | flex-direction: column-reverse; 608 | height: 100px; 609 | } 610 | 611 | #header > h1 { 612 | font-size: 52px; 613 | } 614 | 615 | #header h2 { 616 | float: none; 617 | font-size: 20px; 618 | } 619 | 620 | .namespace-index > h1 { 621 | display: none; 622 | } 623 | 624 | .public, .doc, .namespace > .index, .namespace > .doc, .namespace > h3 { 625 | max-width: initial; 626 | } 627 | 628 | .doc { 629 | text-align: justify; 630 | } 631 | 632 | .public { 633 | padding-top: 2em; 634 | padding-bottom: 2em; 635 | } 636 | 637 | .public > h3 { 638 | font-size: 300%; 639 | } 640 | 641 | .public > h4.type, .public > h4.added, .public > h4.deprecated { 642 | font-size: 150%; 643 | margin-top: 1em; 644 | } 645 | 646 | pre > code { 647 | font-size: 200%; 648 | } 649 | } 650 | -------------------------------------------------------------------------------- /docs/tech.v3.datatype.functional.html: -------------------------------------------------------------------------------- 1 | 3 | tech.v3.datatype.functional documentation

tech.v3.datatype.functional

Simple math primitives.

8 |

descriptive-statistics

(descriptive-statistics stats v)

Given a sequence of desired stats return a map of statname->value.

9 |

Example:

10 |
cljs.user> (dfn/descriptive-statistics [:min :max :mean :n-values] (range 10))
11 | {:min 0, :max 9, :mean 4.5, :n-values 10}
12 | 
13 |

distance

(distance x y)

Euclidian distance between x,y

14 |

distance-squared

(distance-squared x y)

Euclidian distance squared between x,y

15 |

equals

(equals lhs rhs & [error-bar])

Numeric equals - the distance between x,y must be less than error-bar which defaults 16 | to 0.001.

17 |

magnitude

(magnitude v)

Magnitude of the vector

18 |

magnitude-squared

(magnitude-squared v)

Magnitude-squared of the vector

19 |

mean

(mean v)

Nan-aware mean. Nan's will be skipped.

20 |

median

(median options v)(median v)

Nan-aware median. Nan's will be skipped.

21 |

percentiles

(percentiles percentages options v)(percentiles percentages v)

Percentiles are given in whole numbers:

22 |
tech.v3.datatype.functional> (percentiles [0 25 50 75 100] (range 10))
23 | [0.0 1.75 4.5 7.25 9.0]
24 | 
25 |

quartiles

(quartiles item)(quartiles options item)

return min, 25 50 75 max of item

26 |

reduce-max

(reduce-max v)

Nan-unaware max. tech.v3.datatype.statistics/max is nan-aware

27 |

reduce-min

(reduce-min v)

Nan-unaware min. tech.v3.datatype.statistics/min is nan-aware

28 |

scalar-eq

(scalar-eq lhs rhs)

NaN-aware eq

29 |

shift

(shift rdr n)

Shift by n and fill in with the first element for n>0 or last element for n<0.

30 |

Examples:

31 |
user> (dfn/shift (range 10) 2)
32 | [0 0 0 1 2 3 4 5 6 7]
33 | user> (dfn/shift (range 10) -2)
34 | [2 3 4 5 6 7 8 9 9 9]
35 | 
36 |

standard-deviation

(standard-deviation v)

Nan-aware standard-deviation. Nan's will be skipped.

37 |

sum

(sum v)

Nan-aware sum. Nan's will be skipped.

38 |

variance

(variance v)

Nan-aware variance. Nan's will be skipped.

39 |
--------------------------------------------------------------------------------