init research
This commit is contained in:
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
FROM clojure:temurin-21-tools-deps-1.11.3.1456-jammy
|
||||
|
||||
ARG USERNAME=vscode
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
|
||||
# Create the user
|
||||
RUN groupadd --gid $USER_GID $USERNAME \
|
||||
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
||||
#
|
||||
# [Optional] Add sudo support. Omit if you don't need to install software after connecting.
|
||||
&& apt-get update \
|
||||
&& apt-get install -y sudo \
|
||||
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
|
||||
&& chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
|
||||
# ********************************************************
|
||||
# * Anything else you want to do like clean up goes here *
|
||||
# ********************************************************
|
||||
|
||||
# [Optional] Set the default user. Omit if you want to keep the default as root.
|
||||
USER $USERNAME
|
||||
SHELL ["/bin/bash", "-ec"]
|
||||
ENTRYPOINT ["bash"]
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/apt-get-packages:1": {
|
||||
"packages": "r-base-dev,rlwrap"
|
||||
},
|
||||
"ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": {},
|
||||
"ghcr.io/rocker-org/devcontainer-features/r-apt:0": {},
|
||||
"ghcr.io/rocker-org/devcontainer-features/r-packages:1": {
|
||||
"packages": "Rserve,data.table,rmarkdown,knitr",
|
||||
"additionalRepositories": "rforge= 'https://rforge.net'",
|
||||
"installSystemRequirements": true
|
||||
},
|
||||
"ghcr.io/wxw-matt/devcontainer-features/command_runner:latest": {
|
||||
"command1": "bash < <(curl -s https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/install)",
|
||||
"command2": "bash < <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install)",
|
||||
"command3": "bash -c 'wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein -O /usr/local/bin/lein && chmod +x /usr/local/bin/lein'"
|
||||
}
|
||||
},
|
||||
"overrideFeatureInstallOrder": [
|
||||
"ghcr.io/rocker-org/devcontainer-features/r-apt",
|
||||
"ghcr.io/devcontainers-contrib/features/apt-get-packages",
|
||||
"ghcr.io/rocker-org/devcontainer-features/r-packages"
|
||||
],
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"betterthantomorrow.calva"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
((nil
|
||||
.
|
||||
((cider-clojure-cli-aliases
|
||||
.
|
||||
"dev:test"))))
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
docs/** linguist-documentation
|
||||
pr-preview/** linguist-documentation
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Build and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
# This is just a guard against two of these actions
|
||||
# running at the same time. Not likely an issue for
|
||||
# us right now, but it could happen.
|
||||
concurrency: build-and-deploy-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
deploy-docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy documentation
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
folder: docs
|
||||
branch: gh-pages # default for this action, but including to be explicit
|
||||
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
name: "PR Checks"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Install clojure tools
|
||||
uses: DeLaGuardo/setup-clojure@3.2
|
||||
with:
|
||||
cli: latest
|
||||
lein: 2.9.6 # setting b/c latest 2.9.7 broke workflow
|
||||
|
||||
- name: Install dependencies
|
||||
run: lein deps
|
||||
|
||||
- name: Leinigen version
|
||||
run: lein -v
|
||||
|
||||
- name: Test
|
||||
run: lein midje
|
||||
@@ -0,0 +1,27 @@
|
||||
name: PR Docs Preview
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**/*'
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- closed
|
||||
|
||||
concurrency: prs-doc-preview-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
deploy-docs-preview:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy preview
|
||||
uses: rossjrw/pr-preview-action@v1
|
||||
with:
|
||||
source-dir: ./docs/
|
||||
preview-branch: gh-pages
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
pom.xml
|
||||
pom.xml.asc
|
||||
*.jar
|
||||
*.class
|
||||
/lib/
|
||||
/classes/
|
||||
/target/
|
||||
/checkouts/
|
||||
.lein-deps-sum
|
||||
.lein-repl-history
|
||||
.lein-plugins/
|
||||
.lein-failures
|
||||
.nrepl-port
|
||||
.cpcache/
|
||||
*.csv*
|
||||
*.tsv*
|
||||
*.nippy*
|
||||
*.log
|
||||
.R*
|
||||
*.txt*
|
||||
.lsp/
|
||||
.clj-kondo/
|
||||
*.qmd
|
||||
.quarto
|
||||
.clay*
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
language: clojure
|
||||
before_install:
|
||||
- sudo apt-get -y install rlwrap
|
||||
- curl -O https://download.clojure.org/install/linux-install-1.10.1.536.sh
|
||||
- chmod +x linux-install-1.10.1.536.sh
|
||||
- sudo ./linux-install-1.10.1.536.sh
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
lein: 2.9.3
|
||||
script: lein do clean, check, test
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
Vendored
+513
@@ -0,0 +1,513 @@
|
||||
# Change Log
|
||||
|
||||
## [7.042]
|
||||
|
||||
* Deps updated
|
||||
* added fn 'map-column->columns' ([#178])(https://github.com/scicloj/tablecloth/issues/178)
|
||||
|
||||
## [7.029]
|
||||
|
||||
### Added
|
||||
|
||||
* `reorder-columns` can work on grouped dataset now
|
||||
|
||||
### Fixed
|
||||
|
||||
* arrays of 2 element arrays behave as expected on dataset creation ([#142](https://github.com/scicloj/tablecloth/issues/142))
|
||||
|
||||
## [7.021]
|
||||
|
||||
Deps updated
|
||||
|
||||
Documentation changed to be generated by Clay instead of RMarkdown
|
||||
|
||||
## [7.017]
|
||||
|
||||
### Fixed
|
||||
|
||||
* semi and anti joins fail on table containing missing values, multi columns and duplicated rows
|
||||
|
||||
## [7.014]
|
||||
|
||||
Deps updated to fix `j/left-join` issue.
|
||||
|
||||
## [7.012]
|
||||
|
||||
### Fixed
|
||||
|
||||
* join columns should consider `nil` as missing value only, [discussion](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/tablecloth.20join-columns.20is.20nil-ing.20false.3F.20values)
|
||||
* `:nil-missing?` in more places needed (group-by operations), [discussion](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/tablecloth.20group.20operations.20dropping.20a.20nil.20group-by.20column)
|
||||
* changes to the `group-by` documentation [PR115](https://github.com/scicloj/tablecloth/pull/115), thanks to [Marshall](https://github.com/mars0i)
|
||||
* reflection warning for `Collections/shuffle` removed
|
||||
|
||||
## [7.007]
|
||||
|
||||
### Added
|
||||
|
||||
* Extened documentation for `dataset` (copied from TMD), [#112](https://github.com/scicloj/tablecloth/issues/112)
|
||||
|
||||
### Changed
|
||||
|
||||
* `rows` accepts `:nil-missing?`(default: true) and `copying?`(default: false) options.
|
||||
|
||||
## [7.000-beta-51]
|
||||
|
||||
Deps updated
|
||||
|
||||
## [7.000-beta-50.2]
|
||||
|
||||
### Added
|
||||
|
||||
* `:hashing` is available for single column joins too
|
||||
|
||||
## [7.000-beta-50.1]
|
||||
|
||||
### Added
|
||||
|
||||
* `:hashing` option determines method of creating an index for multicolumn joins (was `hash` is `identity`)
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#108](https://github.com/scicloj/tablecloth/issues/108) - hashing replaced with packing data into the sequence
|
||||
|
||||
## [7.000-beta-50]
|
||||
|
||||
Deps updated
|
||||
|
||||
## [7.000-beta-38]
|
||||
|
||||
### Fixed
|
||||
|
||||
* dataset from singleton creation generated from wrong structure
|
||||
|
||||
## [7.000-beta-27]
|
||||
|
||||
### Added
|
||||
|
||||
* `map-rows` to map each row and produce new columns
|
||||
* `rows` can return sequence of vectors (`:as-vecs`)
|
||||
|
||||
### Fixed
|
||||
|
||||
* balanced k-fold partitioning as proposed in [#92](https://github.com/scicloj/tablecloth/issues/92) by @behrica
|
||||
|
||||
## [7.000-beta-16]
|
||||
|
||||
Updated to TMD v7
|
||||
|
||||
Differences:
|
||||
|
||||
* the order of columns is persisted in more cases
|
||||
* the order of groups in grouped dataset can be random
|
||||
|
||||
## [6.103.1]
|
||||
|
||||
### Added
|
||||
|
||||
* doc strings for every funcitons, #87, #88
|
||||
* aggregate-columns should default to all columns when called without a column selector #91
|
||||
* create functions for packing / unpacking columns to arrays #82
|
||||
|
||||
### Changed
|
||||
|
||||
* [breaking] when dataset file do not exists throw an exception #84, #85
|
||||
|
||||
## [6.103]
|
||||
|
||||
Clojure upgraded to 1.11.1
|
||||
|
||||
### Added
|
||||
|
||||
* `separate-column` infers column names when function is used and `target-columns` is `nil`, [#78](https://github.com/scicloj/tablecloth/issues/78)
|
||||
|
||||
### Changed
|
||||
|
||||
* [breaking][minor] `separate-column` repleces source column with target on every case
|
||||
|
||||
## [6.102]
|
||||
|
||||
### Fixed
|
||||
|
||||
* replace `clojure.core/pmap` with `dtype-next` version (related to [#325](https://github.com/techascent/tech.ml.dataset/issues/325))
|
||||
|
||||
## [6.101]
|
||||
|
||||
### Added
|
||||
|
||||
`get-entry` introduced
|
||||
|
||||
## [6.094.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#77] `anti-join` and `semi-join` bugs when tables contain missing values
|
||||
|
||||
## [6.094]
|
||||
|
||||
### Added
|
||||
|
||||
* `crosstab` - cross tabulation
|
||||
* `pivot->longer` `:coerce-to-number` option added
|
||||
|
||||
### Changed
|
||||
|
||||
* [breaking] `pivot->wider` no longer coerces column names to strings, it's up to user
|
||||
|
||||
### Fixed
|
||||
|
||||
* predicates should behave as in Clojure ([discussion](https://clojurians.zulipchat.com/#narrow/stream/151924-data-science/topic/tablecloth.2Fselect-rows.20predicate.20requirements.3F))
|
||||
|
||||
## [6.090]
|
||||
|
||||
TMD version bump
|
||||
|
||||
### Changed
|
||||
|
||||
[breaking]
|
||||
|
||||
`replace-missing` up/down strategies clarified. `:down` is replaced by `:downup` and `:up` is replaced by `:updown`. `:down` and `:up` work only in one direction now.
|
||||
|
||||
https://github.com/techascent/tech.ml.dataset/issues/305
|
||||
|
||||
## [6.088.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
* Wrong way of selecting columns for joins (shouldn't be a set). https://clojurians.zulipchat.com/#narrow/stream/151924-data-science/topic/complete.20ala.20R/near/286277344
|
||||
|
||||
## [6.088]
|
||||
|
||||
### Added
|
||||
|
||||
* `data frame` term in the title of docs ([discussion](https://app.slack.com/client/T03RZGPFR/threads/thread/C03RZGPG3-1649857892.946909))
|
||||
* joins can accept different names for left/right datasets
|
||||
* `cross-join`, `expand` and `complete` introduced
|
||||
|
||||
### Changed
|
||||
|
||||
* removed setting `*warn-on-reflection*`
|
||||
* [breaking] creation of singleton dataset adds an error message as a column by default ([discussion](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/Empty.20csv.20regression.3F.3F))
|
||||
|
||||
## [6.076]
|
||||
|
||||
Version bump
|
||||
|
||||
### Added
|
||||
|
||||
* docstring for `unroll` and `fold-by` by @holyjak (#60 and #61)
|
||||
|
||||
## [6.051]
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#58] - editor friendly api file
|
||||
|
||||
## [6.031]
|
||||
|
||||
### Fixed
|
||||
|
||||
- #57 - InputStream should be dispatched first (the flow now: tries to create a dataset and it fails packs an objet as a singleton
|
||||
|
||||
### Changed
|
||||
|
||||
- `select-rows` accepts `IFn` for row selection.
|
||||
- [breaking] #54, #56 - `pipeline` namespace is stripped, all functions are moved to [metamorph](https://github.com/scicloj/metamorph) library. This is temporary solution before removing this namespace completely. Pipelined versions of functions will be moved to metamorph as well later.
|
||||
|
||||
## [6.025]
|
||||
|
||||
### Added
|
||||
|
||||
* [#49] added docstring to `add-column`
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#53] summary prefix ignored for aggregate (when fn[ds] is passed)
|
||||
|
||||
## [6.023]
|
||||
|
||||
### Added
|
||||
|
||||
* Documented columns / rows functions [PR52](https://github.com/scicloj/tablecloth/pull/52)
|
||||
* Reference to original to lifted functions metadata for pipelines [PR51](https://github.com/scicloj/tablecloth/pull/51)
|
||||
|
||||
### Changed
|
||||
|
||||
* alias for api functions in reference (was: `api`, is: `tc`)
|
||||
|
||||
## [6.012]
|
||||
|
||||
### Fixed
|
||||
|
||||
* `replace-missing` on grouped dataset has swapped arguments
|
||||
|
||||
## [6.006]
|
||||
|
||||
### Fixed
|
||||
|
||||
* `update-columns` on grouped dataset
|
||||
|
||||
## [6.002]
|
||||
|
||||
### Changed
|
||||
|
||||
* [#43] Align with TMD for dataset creation from a map of sequences.
|
||||
* [breaking] creation from tensor is `:as-rows` now
|
||||
|
||||
## [6.00-beta-16]
|
||||
|
||||
### Changed
|
||||
|
||||
* [#42] [breaking] `add-column` default strategy is `:strict` now.
|
||||
|
||||
## [6.00-beta-10]
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#41] dataset name not set on tensor path
|
||||
|
||||
## [6.00-beta-7]
|
||||
|
||||
TMD upgrade, no changes in TC
|
||||
|
||||
## [5.17]
|
||||
|
||||
TMD upgrade
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#36] `reorder-columns` on empty dataset returns nil
|
||||
|
||||
## [5.11]
|
||||
|
||||
### Fixed
|
||||
|
||||
* `aggregate-columns` didn't keep column order (#35)
|
||||
|
||||
## [5.05.1]
|
||||
|
||||
### Added
|
||||
|
||||
* `pipeline` functions have `doc` copied from original ones
|
||||
|
||||
## [5.05]
|
||||
|
||||
### Added
|
||||
|
||||
* `split` can turn off shuffling now (`:shuffle?` option)
|
||||
* `split :holdouts` - sequence of consecutive holdouts
|
||||
|
||||
## [5.04]
|
||||
|
||||
tech.ml.dataset version bump, this introduces the change of the order of the groups after `group-by` operation
|
||||
|
||||
## [5.02]
|
||||
|
||||
### Added
|
||||
|
||||
* `split :holdout` supports any number of splits (minimum 2) [#28]
|
||||
* `split` supports `split-names` to provide custom names for subdatasets
|
||||
* `concat` and `concat-copying` are working with grouped datasets
|
||||
|
||||
### Fixed
|
||||
|
||||
* `kfold` split failed on small number of rows (due to `partition-all` behaviour
|
||||
|
||||
## [5.01]
|
||||
|
||||
### Added
|
||||
|
||||
* `split->seq` to return train/test splits as a sequence or datasets or as map of sequences for grouped datasets
|
||||
|
||||
### Changed
|
||||
|
||||
* [breaking] `tablecloth.pipeline` returns a map with dataset under `:metamorph/data` key (see [metamorph](https://github.com/scicloj/metamorph))
|
||||
* [breaking] `split` returns now a dataset or grouped dataset with two new columns indicating train/test and split id. See `split->seq` for previous behaviour.
|
||||
|
||||
## [5.00-beta-29.1]
|
||||
|
||||
### Added
|
||||
|
||||
* `without-grouping->` threading macro which allows operations on grouping dataset treated as a regular one.
|
||||
|
||||
### Changed
|
||||
|
||||
* `group-by` accepts any java.util.Map for a collection of indexes (use LinkedHashMap to persist an order)
|
||||
* some `tablecloth.api.group-by` functions moved to `tablecloth.api.utils`, no changes to API
|
||||
|
||||
## [5.00-beta-29]
|
||||
|
||||
### Changed
|
||||
|
||||
* `add-or-replace-column(s)` replaced by `add-column(s)` (`add-or-replace-column(s)` is marked as deprecated) (#16)
|
||||
|
||||
### Fixed
|
||||
|
||||
* `mark-as-group` wasn't visible in API (#18)
|
||||
* `map-columns` didn't propagate `new-type` for grouped case (#20)
|
||||
* broken links (#14) in readme
|
||||
|
||||
## [5.00-beta-28]
|
||||
|
||||
### Added
|
||||
|
||||
* `let-dataset` - to simulate `tibble` from R
|
||||
|
||||
### Fixed
|
||||
|
||||
* Adding a column to an empty dataset returned empty dataset
|
||||
|
||||
## [5.00-beta-27]
|
||||
|
||||
### Changed
|
||||
|
||||
* re-implementation of numerical arrays path dataset creation
|
||||
|
||||
## [5.00-beta-25]
|
||||
|
||||
### Added
|
||||
|
||||
* `rows` and `columns` new result: `:as-double-arrays` - convert rows to 2d double array
|
||||
* dataset can be created from numerical arrays [discusson](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/dataset.20.3C-.3E.20jvm.20arrays)
|
||||
|
||||
### Fixed
|
||||
|
||||
* column from single value should create valid datatype (#10)
|
||||
|
||||
## [5.00-beta-21a]
|
||||
|
||||
### Added
|
||||
|
||||
* `tablecloth.pipeline` for pipeline operations
|
||||
|
||||
## [5.00-beta-21]
|
||||
|
||||
### Added
|
||||
|
||||
* `concat-copying` exposed.
|
||||
* `split` function for splitting into train-test pairs with `:kfold`, `:bootstrap`, `:loo` and `holdout` strategies + stratified versions
|
||||
* `replace-missing` with new strategy `:midpoint`
|
||||
|
||||
## [5.00-beta-5a]
|
||||
|
||||
### Fixed
|
||||
|
||||
* column names should keep order for provided names (#9)
|
||||
|
||||
## [5.00-beta-5]
|
||||
|
||||
t.m.d update
|
||||
|
||||
## [5.00-beta-3]
|
||||
|
||||
t.m.d update
|
||||
|
||||
### Changed
|
||||
|
||||
* contribution guide in readme
|
||||
|
||||
## [5.00-beta-2]
|
||||
|
||||
t.m.d update
|
||||
|
||||
### Changed
|
||||
|
||||
* `write-nippy!` and `read-nippy` are deprecated, replaced by `write!` and `dataset`
|
||||
|
||||
## [5.0-SNAPSHOT]
|
||||
|
||||
`tech.ml.dataset` version 5.0-alpha*
|
||||
|
||||
### Added
|
||||
|
||||
* `map-columns` accepts optional target datatype
|
||||
* `ds/column->dataset` functionality introduced in `separate-column`
|
||||
* more datatypes included for conversion (`:text` among others)
|
||||
|
||||
### Changed
|
||||
|
||||
* `write-csv!` replaced by `write!` (`write-csv!` is marked as deprecated)
|
||||
* `info` field `:size` is replaced by `:n-elems`
|
||||
* [breaking] `separate-column` 3-arity version accepts `separator` instead `target-columns` now
|
||||
|
||||
### Fixed
|
||||
|
||||
* do not skip 1-row DS when folding
|
||||
* do not attempt to fold empty dataset
|
||||
|
||||
## [4.04]
|
||||
|
||||
`tech.ml.dataset` version 4.04
|
||||
|
||||
### Added
|
||||
|
||||
* tests: dataset
|
||||
|
||||
### Changed
|
||||
|
||||
* version number to match t.m.dataset version
|
||||
* documentation:
|
||||
- gfm renderer for markdown
|
||||
|
||||
### Fixed
|
||||
|
||||
* code block language alignment fix in css
|
||||
|
||||
## [1.0.0-pre-alpha9]
|
||||
|
||||
`tech.ml.dataset` version 4.03
|
||||
|
||||
### Added
|
||||
|
||||
* some operations on grouped dataset can be parallel (`parallel?` option set to `true`). These are: `aggregate`, `unique-by`, `order-by`, `join-columns`, `separate-columns`, `ungroup`
|
||||
|
||||
### Fixed
|
||||
|
||||
* #2 - docs typo
|
||||
* #3 - recover datatypes after ungrouping
|
||||
|
||||
### Changed
|
||||
|
||||
* `aggregation` uses now in-place ungrouping which is much faster
|
||||
|
||||
## [1.0.0-pre-alpha8]
|
||||
|
||||
`tech.ml.dataset` version 3.06
|
||||
|
||||
### Added
|
||||
|
||||
* `fill-range-replace` to inject data to make continuous seqence in column
|
||||
* `write-nippy!` and `read-nippy`
|
||||
|
||||
## [1.0.0-pre-alpha7]
|
||||
|
||||
`tech.ml.dataset` version 2.13
|
||||
|
||||
### Added
|
||||
|
||||
* `replace-missing` new strategies: `:mid` and `:lerp`, working also for dates.
|
||||
|
||||
### Changed
|
||||
|
||||
* [breaking] `replace-missing` has different conctract and default strategy `:mid`. `value` argument is the last argument now.
|
||||
* [breaking] `replace-missing` `:up` and `:down` strategies, when `value` is `nil` fills border missing values with nearest value.
|
||||
|
||||
## [1.0.0-pre-alpha6]
|
||||
|
||||
`tech.ml.dataset` version 2.06
|
||||
|
||||
### Added
|
||||
|
||||
* `asof-join` added
|
||||
|
||||
## [1.0.0-pre-alpha4]
|
||||
|
||||
### Added
|
||||
|
||||
* `reshape` tests
|
||||
* `pivot->wider` accepts `:drop-missing?` option (default: `true`)
|
||||
|
||||
### Changed
|
||||
|
||||
* `pivot->wider` drops missing rows by default
|
||||
* `pivto->wider` order of concatenated column names is reversed (first: colnames, last: value), was opposite.
|
||||
* `pivot->longer` `:splitter` accepts string used for splitting column name
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Scicloj
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+162
@@ -0,0 +1,162 @@
|
||||
# Tablecloth
|
||||
|
||||
Dataset (data frame) manipulation API for the tech.ml.dataset library
|
||||
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
[](https://travis-ci.org/github/scicloj/tablecloth)
|
||||
[](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Versions
|
||||
|
||||
### tech.ml.dataset 7.x (master branch)
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
|
||||
### tech.ml.dataset 4.x (4.0 branch)
|
||||
|
||||
`[scicloj/tablecloth "4.04"]`
|
||||
|
||||
## Introduction
|
||||
|
||||
[tech.ml.dataset](https://github.com/techascent/tech.ml.dataset) is a great and fast library which brings columnar dataset to the Clojure. Chris Nuernberger has been working on this library for last year as a part of bigger `tech.ml` stack.
|
||||
|
||||
I've started to test the library and help to fix uncovered bugs. My main goal was to compare functionalities with the other standards from other platforms. I focused on R solutions: [dplyr](https://dplyr.tidyverse.org/), [tidyr](https://tidyr.tidyverse.org/) and [data.table](https://rdatatable.gitlab.io/data.table/).
|
||||
|
||||
During conversions of the examples I've come up how to reorganized existing `tech.ml.dataset` functions into simple to use API. The main goals were:
|
||||
|
||||
* Focus on dataset manipulation functionality, leaving other parts of `tech.ml` like pipelines, datatypes, readers, ML, etc.
|
||||
* Single entry point for common operations - one function dispatching on given arguments.
|
||||
* `group-by` results with special kind of dataset - a dataset containing subsets created after grouping as a column.
|
||||
* Most operations recognize regular dataset and grouped dataset and process data accordingly.
|
||||
* One function form to enable thread-first on dataset.
|
||||
|
||||
Important! This library is not the replacement of `tech.ml.dataset` nor a separate library. It should be considered as a addition on the top of `tech.ml.dataset`.
|
||||
|
||||
If you want to know more about `tech.ml.dataset` and `dtype-next` please refer their documentation:
|
||||
|
||||
* [tech.ml.dataset walkthrough](https://techascent.github.io/tech.ml.dataset/walkthrough.html)
|
||||
* [dtype-next overview](https://cnuernber.github.io/dtype-next/overview.html)
|
||||
* [dtype-next cheatsheet](https://cnuernber.github.io/dtype-next/cheatsheet.html)
|
||||
|
||||
Join the discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer [detailed documentation with examples](https://scicloj.github.io/tablecloth).
|
||||
|
||||
The old documentation (till the end of 2023) is [here](https://scicloj.github.io/tablecloth/old).
|
||||
|
||||
## Usage example
|
||||
|
||||
```{clojure results="hide"}
|
||||
(require '[tablecloth.api :as tc])
|
||||
```
|
||||
|
||||
```{clojure results="asis"}
|
||||
(-> "https://raw.githubusercontent.com/techascent/tech.ml.dataset/master/test/data/stocks.csv"
|
||||
(tc/dataset {:key-fn keyword})
|
||||
(tc/group-by (fn [row]
|
||||
{:symbol (:symbol row)
|
||||
:year (tech.v3.datatype.datetime/long-temporal-field :years (:date row))}))
|
||||
(tc/aggregate #(tech.v3.datatype.functional/mean (% :price)))
|
||||
(tc/order-by [:symbol :year])
|
||||
(tc/head 10))
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
`Tablecloth` is open for contribution. The best way to start is discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api).
|
||||
|
||||
### Development tools for documentation
|
||||
|
||||
Documentation is written in the [Kindly](https://scicloj.github.io/kindly/) convention and is rendered using [Clay](https://scicloj.github.io/clay/) composed with [Quarto](https://quarto.org/).
|
||||
|
||||
The old documentation was written in RMarkdown and is kept under [docs/old/](./docs/old/).
|
||||
|
||||
Documentation contains around 600 code snippets which are run during build. There are three relevant source files:
|
||||
|
||||
* [README-source.md](./README-source.md) for README.md
|
||||
* [notebooks/index.clj](./notebooks/index.clj) for the detailed documentation
|
||||
* [clay.edn](./clay.edn) for some styling options of the docs
|
||||
|
||||
(`notebooks/index.clj` was generated by [dev/conversion.clj](dev/conversion.clj) from the earlier Rmarkdown-based `index.Rmd` with asome additional manual editing. Starting at 2024, it will diverge from that source, that will no longer be maintained.)
|
||||
|
||||
### README generation
|
||||
|
||||
To generate `README.md`, run the `generate!` function at the [dev/readme_generation.clj](./dev/readme_generation.clj) script.
|
||||
|
||||
### Detailed documentation generation
|
||||
|
||||
To generate the detailed documentation, call the following. You will need the Quarto CLI [installed](https://quarto.org/docs/get-started/) in your system.
|
||||
|
||||
Currently (April 2024), we use Quarto's [v1.5.10 pre-release](https://github.com/quarto-dev/quarto-cli/releases/tag/v1.5.10) (specifically this version, not the later ones) due to some Quarto bugs.
|
||||
|
||||
```{clojure eval=FALSE}
|
||||
(require '[scicloj.clay.v2.api :as clay])
|
||||
(clay/make! {:format [:quarto :html]
|
||||
:source-path "notebooks/index.clj"})
|
||||
```
|
||||
|
||||
### Code Generation
|
||||
|
||||
To build this project fully we need to perform some code generation operations. These are listed below:
|
||||
|
||||
1. Build the `tablecloth.api.operators` namespace
|
||||
|
||||
The `tablecloth.api.operators` namespace is generated by
|
||||
`tablecloth.api.lift_operators`. To build that namespace, you need to
|
||||
load the `tablecloth.api.lift_operators` namespace, and then execute
|
||||
the code surrounded by a comment at the bottom of the file.
|
||||
|
||||
2. Build the `tablecloth.api` (aka the Dataset API)
|
||||
|
||||
The `tablecloth.api` namespace is generated out of `api-template`. To
|
||||
build that namespace you need to load the
|
||||
`tablecloth.api.api-template` namespace, and then evaluate the code
|
||||
contained in the comment section at the bottom of the file. This will
|
||||
re-generate the `tablecloth.api` namespace.
|
||||
|
||||
3. Build the `tablecloth.column.api.operators` namespace
|
||||
|
||||
The `tablecloth.column.api.operators` namespace is generated by
|
||||
`tablecloth.column.api.lift_operators`. To build that namespace, you
|
||||
need to load the `tablecloth.api.lift_operators` namespace, and then
|
||||
execute the code surrounded by a comment at the bottom of the file.
|
||||
|
||||
4. Build the `tablecloth.column.api` (aka the Column API)
|
||||
|
||||
The `tablecloth.column.api` namespace is generated out of
|
||||
`api-template`. To build that namespace you need to load the
|
||||
`tablecloth.column.api.api-template` namespace, and then evaluate the
|
||||
code contained in the comment section at the bottom of the file. This
|
||||
will re-generate the `tablecloth.column.api` namespace.
|
||||
|
||||
|
||||
### Guideline
|
||||
|
||||
1. Before commiting changes please perform tests. I ususally do: `lein do clean, check, test` and build documentation as described above (which also tests whole library).
|
||||
2. Keep API as simple as possible:
|
||||
- first argument should be a dataset
|
||||
- if parametrizations is complex, last argument should accept a map with not obligatory function arguments
|
||||
- avoid variadic associative destructuring for function arguments
|
||||
- usually function should working on grouped dataset as well, accept `parallel?` argument then (if applied).
|
||||
3. Follow `potemkin` pattern and import functions to the API namespace using `tech.v3.datatype.export-symbols/export-symbols` function
|
||||
4. Functions which are composed out of API function to cover specific case(s) should go to `tablecloth.utils` namespace.
|
||||
5. Always update `README-source.md`, `CHANGELOG.md`, `notebooks/index.clj`, tests and function docs are highly welcomed.
|
||||
6. Always discuss changes and PRs first
|
||||
|
||||
### Tests
|
||||
|
||||
Tests are written and run using [midje](https://github.com/marick/Midje/). To run a test, evaluate a midje form. If it passes, it will return `true`, if it fails details will be printed to the REPL.
|
||||
|
||||
## TODO
|
||||
|
||||
* elaborate on tests
|
||||
* tutorials
|
||||
|
||||
## Licence
|
||||
|
||||
Copyright (c) 2020 Scicloj
|
||||
|
||||
The MIT Licence
|
||||
Vendored
+180
@@ -0,0 +1,180 @@
|
||||
# Tablecloth
|
||||
|
||||
Dataset (data frame) manipulation API for the tech.ml.dataset library
|
||||
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
[](https://travis-ci.org/github/scicloj/tablecloth)
|
||||
[](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Versions
|
||||
|
||||
### tech.ml.dataset 7.x (master branch)
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
|
||||
### tech.ml.dataset 4.x (4.0 branch)
|
||||
|
||||
`[scicloj/tablecloth "4.04"]`
|
||||
|
||||
## Introduction
|
||||
|
||||
[tech.ml.dataset](https://github.com/techascent/tech.ml.dataset) is a great and fast library which brings columnar dataset to the Clojure. Chris Nuernberger has been working on this library for last year as a part of bigger `tech.ml` stack.
|
||||
|
||||
I've started to test the library and help to fix uncovered bugs. My main goal was to compare functionalities with the other standards from other platforms. I focused on R solutions: [dplyr](https://dplyr.tidyverse.org/), [tidyr](https://tidyr.tidyverse.org/) and [data.table](https://rdatatable.gitlab.io/data.table/).
|
||||
|
||||
During conversions of the examples I've come up how to reorganized existing `tech.ml.dataset` functions into simple to use API. The main goals were:
|
||||
|
||||
* Focus on dataset manipulation functionality, leaving other parts of `tech.ml` like pipelines, datatypes, readers, ML, etc.
|
||||
* Single entry point for common operations - one function dispatching on given arguments.
|
||||
* `group-by` results with special kind of dataset - a dataset containing subsets created after grouping as a column.
|
||||
* Most operations recognize regular dataset and grouped dataset and process data accordingly.
|
||||
* One function form to enable thread-first on dataset.
|
||||
|
||||
Important! This library is not the replacement of `tech.ml.dataset` nor a separate library. It should be considered as a addition on the top of `tech.ml.dataset`.
|
||||
|
||||
If you want to know more about `tech.ml.dataset` and `dtype-next` please refer their documentation:
|
||||
|
||||
* [tech.ml.dataset walkthrough](https://techascent.github.io/tech.ml.dataset/walkthrough.html)
|
||||
* [dtype-next overview](https://cnuernber.github.io/dtype-next/overview.html)
|
||||
* [dtype-next cheatsheet](https://cnuernber.github.io/dtype-next/cheatsheet.html)
|
||||
|
||||
Join the discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer [detailed documentation with examples](https://scicloj.github.io/tablecloth).
|
||||
|
||||
The old documentation (till the end of 2023) is [here](https://scicloj.github.io/tablecloth/old).
|
||||
|
||||
## Usage example
|
||||
|
||||
```{clojure}
|
||||
(require '[tablecloth.api :as tc])
|
||||
```
|
||||
|
||||
|
||||
```{clojure}
|
||||
(-> "https://raw.githubusercontent.com/techascent/tech.ml.dataset/master/test/data/stocks.csv"
|
||||
(tc/dataset {:key-fn keyword})
|
||||
(tc/group-by (fn [row]
|
||||
{:symbol (:symbol row)
|
||||
:year (tech.v3.datatype.datetime/long-temporal-field :years (:date row))}))
|
||||
(tc/aggregate #(tech.v3.datatype.functional/mean (% :price)))
|
||||
(tc/order-by [:symbol :year])
|
||||
(tc/head 10))
|
||||
```
|
||||
_unnamed [10 3]:
|
||||
|
||||
| :symbol | :year | summary |
|
||||
|---------|------:|-------------:|
|
||||
| AAPL | 2000 | 21.74833333 |
|
||||
| AAPL | 2001 | 10.17583333 |
|
||||
| AAPL | 2002 | 9.40833333 |
|
||||
| AAPL | 2003 | 9.34750000 |
|
||||
| AAPL | 2004 | 18.72333333 |
|
||||
| AAPL | 2005 | 48.17166667 |
|
||||
| AAPL | 2006 | 72.04333333 |
|
||||
| AAPL | 2007 | 133.35333333 |
|
||||
| AAPL | 2008 | 138.48083333 |
|
||||
| AAPL | 2009 | 150.39333333 |
|
||||
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
`Tablecloth` is open for contribution. The best way to start is discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api).
|
||||
|
||||
### Development tools for documentation
|
||||
|
||||
Documentation is written in the [Kindly](https://scicloj.github.io/kindly/) convention and is rendered using [Clay](https://scicloj.github.io/clay/) composed with [Quarto](https://quarto.org/).
|
||||
|
||||
The old documentation was written in RMarkdown and is kept under [docs/old/](./docs/old/).
|
||||
|
||||
Documentation contains around 600 code snippets which are run during build. There are three relevant source files:
|
||||
|
||||
* [README-source.md](./README-source.md) for README.md
|
||||
* [notebooks/index.clj](./notebooks/index.clj) for the detailed documentation
|
||||
* [clay.edn](./clay.edn) for some styling options of the docs
|
||||
|
||||
(`notebooks/index.clj` was generated by [dev/conversion.clj](dev/conversion.clj) from the earlier Rmarkdown-based `index.Rmd` with asome additional manual editing. Starting at 2024, it will diverge from that source, that will no longer be maintained.)
|
||||
|
||||
### README generation
|
||||
|
||||
To generate `README.md`, run the `generate!` function at the [dev/readme_generation.clj](./dev/readme_generation.clj) script.
|
||||
|
||||
### Detailed documentation generation
|
||||
|
||||
To generate the detailed documentation, call the following. You will need the Quarto CLI [installed](https://quarto.org/docs/get-started/) in your system.
|
||||
|
||||
Currently (April 2024), we use Quarto's [v1.5.10 pre-release](https://github.com/quarto-dev/quarto-cli/releases/tag/v1.5.10) (specifically this version, not the later ones) due to some Quarto bugs.
|
||||
|
||||
```{clojure}
|
||||
(require '[scicloj.clay.v2.api :as clay])
|
||||
(clay/make! {:format [:quarto :html]
|
||||
:source-path "notebooks/index.clj"})
|
||||
```
|
||||
|
||||
|
||||
### Code Generation
|
||||
|
||||
To build this project fully we need to perform some code generation operations. These are listed below:
|
||||
|
||||
1. Build the `tablecloth.api.operators` namespace
|
||||
|
||||
The `tablecloth.api.operators` namespace is generated by
|
||||
`tablecloth.api.lift_operators`. To build that namespace, you need to
|
||||
load the `tablecloth.api.lift_operators` namespace, and then execute
|
||||
the code surrounded by a comment at the bottom of the file.
|
||||
|
||||
2. Build the `tablecloth.api` (aka the Dataset API)
|
||||
|
||||
The `tablecloth.api` namespace is generated out of `api-template`. To
|
||||
build that namespace you need to load the
|
||||
`tablecloth.api.api-template` namespace, and then evaluate the code
|
||||
contained in the comment section at the bottom of the file. This will
|
||||
re-generate the `tablecloth.api` namespace.
|
||||
|
||||
3. Build the `tablecloth.column.api.operators` namespace
|
||||
|
||||
The `tablecloth.column.api.operators` namespace is generated by
|
||||
`tablecloth.column.api.lift_operators`. To build that namespace, you
|
||||
need to load the `tablecloth.api.lift_operators` namespace, and then
|
||||
execute the code surrounded by a comment at the bottom of the file.
|
||||
|
||||
4. Build the `tablecloth.column.api` (aka the Column API)
|
||||
|
||||
The `tablecloth.column.api` namespace is generated out of
|
||||
`api-template`. To build that namespace you need to load the
|
||||
`tablecloth.column.api.api-template` namespace, and then evaluate the
|
||||
code contained in the comment section at the bottom of the file. This
|
||||
will re-generate the `tablecloth.column.api` namespace.
|
||||
|
||||
|
||||
### Guideline
|
||||
|
||||
1. Before commiting changes please perform tests. I ususally do: `lein do clean, check, test` and build documentation as described above (which also tests whole library).
|
||||
2. Keep API as simple as possible:
|
||||
- first argument should be a dataset
|
||||
- if parametrizations is complex, last argument should accept a map with not obligatory function arguments
|
||||
- avoid variadic associative destructuring for function arguments
|
||||
- usually function should working on grouped dataset as well, accept `parallel?` argument then (if applied).
|
||||
3. Follow `potemkin` pattern and import functions to the API namespace using `tech.v3.datatype.export-symbols/export-symbols` function
|
||||
4. Functions which are composed out of API function to cover specific case(s) should go to `tablecloth.utils` namespace.
|
||||
5. Always update `README-source.md`, `CHANGELOG.md`, `notebooks/index.clj`, tests and function docs are highly welcomed.
|
||||
6. Always discuss changes and PRs first
|
||||
|
||||
### Tests
|
||||
|
||||
Tests are written and run using [midje](https://github.com/marick/Midje/). To run a test, evaluate a midje form. If it passes, it will return `true`, if it fails details will be printed to the REPL.
|
||||
|
||||
## TODO
|
||||
|
||||
* elaborate on tests
|
||||
* tutorials
|
||||
|
||||
## Licence
|
||||
|
||||
Copyright (c) 2020 Scicloj
|
||||
|
||||
The MIT Licence
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{:remote-repo {:git-url "https://github.com/scicloj/tablecloth"
|
||||
:branch "master"}
|
||||
:quarto {:format {:html {:toc true
|
||||
:toc-depth 4
|
||||
:theme [:cosmo "notebooks/custom.scss"]}}
|
||||
:fontsize "0.8em"
|
||||
:highlight-style :solarized
|
||||
:code-block-background true
|
||||
:include-in-header {:text "<link rel = \"icon\" href = \"data:,\" />"}
|
||||
:title "Tablecloth documentation"}}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{:extra-paths ["data"]
|
||||
:deps {org.clojure/clojure {:mvn/version "1.12.2"}
|
||||
techascent/tech.ml.dataset {:mvn/version "7.062"}}
|
||||
:aliases {:dev {:extra-deps {org.scicloj/clay {:mvn/version "2-beta54"}
|
||||
org.scicloj/note-to-test {:mvn/version "1-alpha7"}}}
|
||||
:test {:extra-deps {org.scicloj/clay {:mvn/version "2-beta54"}
|
||||
org.scicloj/note-to-test {:mvn/version "1-alpha7"}
|
||||
midje/midje {:mvn/version "1.10.10"}}
|
||||
:extra-paths ["test"]}
|
||||
;; run `clojure -M:profile` to produce a flamegraph
|
||||
:profile {:extra-paths ["dev"]
|
||||
:extra-deps {com.clojure-goes-fast/clj-async-profiler {:mvn/version "1.6.2"}}
|
||||
:jvm-opts ["-Djdk.attach.allowAttachSelf"]
|
||||
:main-opts ["-m" "profile"]}}}
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
(ns conversion
|
||||
(:require [clojure.string :as string]))
|
||||
|
||||
;; This namespace has been used to convert the old `docs/index.Rmd` documentation to the new `notebooks/index.clj` documentation. Some additional manual editing was needed to make things work afterwards.
|
||||
|
||||
(set! *print-length* 1000)
|
||||
|
||||
(def example-doc
|
||||
(slurp "docs/index.Rmd"
|
||||
#_"https://raw.githubusercontent.com/scicloj/tablecloth/master/docs/index.Rmd"))
|
||||
|
||||
(defn chunk-end? [line]
|
||||
(= line "```"))
|
||||
|
||||
(defn clojure-chunk-beginning? [line]
|
||||
(re-matches #"```\{clojure.*\}" line))
|
||||
|
||||
(defn clojure-chunk-options [line]
|
||||
(-> line
|
||||
(string/replace #"^```\{clojure" "")
|
||||
(string/replace #"\}$" "")))
|
||||
|
||||
(defn markdown->markdown-with-extracted-clojure-chunks [markdown]
|
||||
(loop [lines (string/split markdown #"\n")
|
||||
current-clojure-chunk-code nil
|
||||
current-clojure-chunk-options nil
|
||||
result []]
|
||||
(if-let [current-line (first lines)]
|
||||
(if current-clojure-chunk-code
|
||||
(if (chunk-end? current-line)
|
||||
(recur (rest lines)
|
||||
nil
|
||||
nil
|
||||
(conj result {:clojure-chunk? true
|
||||
:code current-clojure-chunk-code
|
||||
:options current-clojure-chunk-options}))
|
||||
(recur (rest lines)
|
||||
(conj current-clojure-chunk-code current-line)
|
||||
current-clojure-chunk-options
|
||||
result))
|
||||
(if (clojure-chunk-beginning? current-line)
|
||||
(recur (rest lines)
|
||||
[]
|
||||
(clojure-chunk-options current-line)
|
||||
result)
|
||||
(recur (rest lines)
|
||||
nil
|
||||
nil
|
||||
(conj result current-line))))
|
||||
result)))
|
||||
|
||||
(defn clojure-chunk->markdown [{:keys [clojure-chunk? code options]}]
|
||||
(when clojure-chunk?
|
||||
(str "```{clojure"
|
||||
options
|
||||
"}\n"
|
||||
(string/join "\n" code)
|
||||
"\n```")))
|
||||
|
||||
(defn markdown-with-extracted-clojure-chunks->markdown [mwecc]
|
||||
(->> mwecc
|
||||
(map #(or (clojure-chunk->markdown %)
|
||||
%))
|
||||
(string/join "\n")
|
||||
(format "%s\n")))
|
||||
|
||||
(comment
|
||||
(->> example-doc
|
||||
(spit "/tmp/0.Rmd"))
|
||||
|
||||
(->> example-doc
|
||||
markdown->markdown-with-extracted-clojure-chunks
|
||||
markdown-with-extracted-clojure-chunks->markdown
|
||||
(spit "/tmp/a.Rmd"))
|
||||
|
||||
(->> example-doc
|
||||
markdown->markdown-with-extracted-clojure-chunks
|
||||
markdown-with-extracted-clojure-chunks->markdown
|
||||
(= example-doc))
|
||||
|
||||
(->> example-doc
|
||||
markdown->markdown-with-extracted-clojure-chunks
|
||||
(partition-by string?)))
|
||||
|
||||
|
||||
(defn inner-pr-str [s]
|
||||
(let [s1 (pr-str s)
|
||||
n (count s1)]
|
||||
(subs s1 1 (dec n))))
|
||||
|
||||
(defn markdown-with-extracted-clojure-chunks->clojure [mwecc]
|
||||
(->> mwecc
|
||||
(partition-by string?)
|
||||
(mapcat (fn [part]
|
||||
(cond (-> part first string?) [(->> part
|
||||
(map inner-pr-str)
|
||||
(string/join "\n")
|
||||
((fn [s]
|
||||
(if (seq s)
|
||||
(format "(md \"%s\")" s)
|
||||
""))))]
|
||||
(-> part first :clojure-chunk?) [(->> part
|
||||
(mapcat
|
||||
(fn [{:keys [code options]}]
|
||||
code
|
||||
#_(cons (format "^{:chunk-options \"%s\"}[]\n"
|
||||
options)
|
||||
code)))
|
||||
(string/join "\n"))])))
|
||||
(string/join "\n\n\n")
|
||||
(format "
|
||||
(ns index
|
||||
(:require [scicloj.kindly.v3.kind :as kind]
|
||||
[scicloj.kindly-default.v1.api :refer [md]]
|
||||
[tablecloth.api :as tc]
|
||||
[scicloj.note-to-test.v1.api :as note-to-test]))
|
||||
|
||||
%s")))
|
||||
|
||||
(->> example-doc
|
||||
markdown->markdown-with-extracted-clojure-chunks
|
||||
markdown-with-extracted-clojure-chunks->clojure
|
||||
(spit "notebooks/index.clj"))
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
(ns gendocs
|
||||
(:require [scicloj.clay.v2.api :as clay]))
|
||||
|
||||
(clay/make! {:format [:quarto :html]
|
||||
:source-path "notebooks/index.clj"
|
||||
:base-target-path "docs"})
|
||||
|
||||
;; beta54
|
||||
(clay/make! {:format [:quarto :html]
|
||||
:base-source-path "notebooks"
|
||||
:source-path "index.clj"
|
||||
:base-target-path "docs"})
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
(ns profile
|
||||
(:require [clj-async-profiler.core :as prof]))
|
||||
|
||||
(defn -main []
|
||||
(println "Profiling...")
|
||||
(prof/profile (require '[tablecloth.api]))
|
||||
(println "Done. See /tmp/clj-async-profiler/results/"))
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
(ns readme-generation
|
||||
(:require [clojure.string :as str]
|
||||
[clojure.pprint :as pp]))
|
||||
|
||||
(set! *print-length* 1000)
|
||||
|
||||
(defn chunk-end? [line]
|
||||
(= line "```"))
|
||||
|
||||
(defn clojure-chunk-beginning? [line]
|
||||
(re-matches #"```\{clojure.*\}" line))
|
||||
|
||||
(defn clojure-chunk-options [line]
|
||||
(-> line
|
||||
(str/replace #"^```\{clojure" "")
|
||||
(str/replace #"\}$" "")))
|
||||
|
||||
(defn markdown->markdown-with-extracted-clojure-chunks [markdown]
|
||||
(loop [lines (str/split markdown #"\n")
|
||||
current-clojure-chunk-code nil
|
||||
current-clojure-chunk-options nil
|
||||
result []]
|
||||
(if-let [current-line (first lines)]
|
||||
(if current-clojure-chunk-code
|
||||
(if (chunk-end? current-line)
|
||||
(recur (rest lines)
|
||||
nil
|
||||
nil
|
||||
(conj result {:clojure-chunk? true
|
||||
:code current-clojure-chunk-code
|
||||
:options current-clojure-chunk-options}))
|
||||
(recur (rest lines)
|
||||
(conj current-clojure-chunk-code current-line)
|
||||
current-clojure-chunk-options
|
||||
result))
|
||||
(if (clojure-chunk-beginning? current-line)
|
||||
(recur (rest lines)
|
||||
[]
|
||||
(clojure-chunk-options current-line)
|
||||
result)
|
||||
(recur (rest lines)
|
||||
nil
|
||||
nil
|
||||
(conj result current-line))))
|
||||
result)))
|
||||
|
||||
(defn markdown-with-extracted-clojure-chunks->markdown-evaluated [mwecc]
|
||||
(->> mwecc
|
||||
(map (fn [element]
|
||||
(if (string? element)
|
||||
element
|
||||
(let [{:keys [code options]} element
|
||||
options-map (-> options
|
||||
str/trim
|
||||
(str/split #"=")
|
||||
(->> (map read-string)
|
||||
(apply hash-map)))]
|
||||
(str (format "```{clojure}\n%s\n```\n"
|
||||
(->> code
|
||||
(str/join "\n")))
|
||||
(if (-> options-map
|
||||
(get 'eval)
|
||||
(= 'FALSE))
|
||||
""
|
||||
(let [result (->> code
|
||||
(str/join "\n")
|
||||
load-string)]
|
||||
(case (-> options-map
|
||||
(get 'results))
|
||||
"hide" ""
|
||||
"asis" (-> result
|
||||
pp/pprint
|
||||
with-out-str)))))))))
|
||||
(str/join "\n")))
|
||||
|
||||
|
||||
(defn generate! []
|
||||
(->> "README-source.md"
|
||||
slurp
|
||||
markdown->markdown-with-extracted-clojure-chunks
|
||||
markdown-with-extracted-clojure-chunks->markdown-evaluated
|
||||
(spit "README.md")))
|
||||
|
||||
(comment
|
||||
(generate!))
|
||||
Vendored
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
|
||||
// add bootstrap table styles to pandoc tables
|
||||
function bootstrapStylePandocTables2() {
|
||||
$('tr.header').parent('thead').parent('table').addClass('table table-striped table-hover table-condensed table-responsive');
|
||||
}
|
||||
$(document).ready(function () { bootstrapStylePandocTables2(); });
|
||||
|
||||
</script>
|
||||
Vendored
+716
@@ -0,0 +1,716 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="quarto-1.3.450">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
|
||||
|
||||
<title>column_api</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
||||
div.column{flex: auto; overflow-x: auto;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
ul.task-list li input[type="checkbox"] {
|
||||
width: 0.8em;
|
||||
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* CSS for syntax highlighting */
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
.sourceCode { overflow: visible; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script src="column_api_files/libs/clipboard/clipboard.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/quarto.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/popper.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/tippy.umd.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/anchor.min.js"></script>
|
||||
<link href="column_api_files/libs/quarto-html/tippy.css" rel="stylesheet">
|
||||
<link href="column_api_files/libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
|
||||
<script src="column_api_files/libs/bootstrap/bootstrap.min.js"></script>
|
||||
<link href="column_api_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="column_api_files/libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
|
||||
<link rel="icon" href="data:,">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
|
||||
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
|
||||
<nav id="TOC" role="doc-toc" class="toc-active">
|
||||
<h2 id="toc-title">Table of contents</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="#column-api" id="toc-column-api" class="nav-link active" data-scroll-target="#column-api">Column API</a>
|
||||
<ul>
|
||||
<li><a href="#column-creation" id="toc-column-creation" class="nav-link" data-scroll-target="#column-creation">Column Creation</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#ones-zeros" id="toc-ones-zeros" class="nav-link" data-scroll-target="#ones-zeros">Ones & Zeros</a></li>
|
||||
<li><a href="#column" id="toc-column" class="nav-link" data-scroll-target="#column">Column?</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#types-and-type-detection" id="toc-types-and-type-detection" class="nav-link" data-scroll-target="#types-and-type-detection">Types and Type detection</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#typeof-typeof" id="toc-typeof-typeof" class="nav-link" data-scroll-target="#typeof-typeof">Typeof & Typeof?</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#column-access-manipulation" id="toc-column-access-manipulation" class="nav-link" data-scroll-target="#column-access-manipulation">Column Access & Manipulation</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#column-access" id="toc-column-access" class="nav-link" data-scroll-target="#column-access">Column Access</a></li>
|
||||
<li><a href="#slice" id="toc-slice" class="nav-link" data-scroll-target="#slice">Slice</a></li>
|
||||
<li><a href="#select" id="toc-select" class="nav-link" data-scroll-target="#select">Select</a></li>
|
||||
<li><a href="#sort" id="toc-sort" class="nav-link" data-scroll-target="#sort">Sort</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#column-operations" id="toc-column-operations" class="nav-link" data-scroll-target="#column-operations">Column Operations</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<main class="content" id="quarto-document-content">
|
||||
|
||||
|
||||
|
||||
|
||||
<style>table {
|
||||
border-style: thin;
|
||||
}
|
||||
th, td {
|
||||
padding: 6px;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
text-align: center;
|
||||
background-color: #ddd;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
<style>.printedClojure .sourceCode {
|
||||
background-color: transparent;
|
||||
border-style: none;
|
||||
}
|
||||
</style>
|
||||
<script src="column_api_files/md-default0.js" type="text/javascript"></script>
|
||||
<script src="column_api_files/md-default1.js" type="text/javascript"></script>
|
||||
<section id="column-api" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="column-api">Column API</h2>
|
||||
<p>A <code>column</code> in tablecloth is a named sequence of typed data. This special type is defined in the <code>tech.ml.dataset</code>. It is roughly comparable to a R vector.</p>
|
||||
<section id="column-creation" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-creation">Column Creation</h3>
|
||||
<p>Empty column</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>(tcc/column)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[0]</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>[]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Column from a vector or a sequence</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>(tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>(tcc/column `(<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<section id="ones-zeros" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="ones-zeros">Ones & Zeros</h4>
|
||||
<p>You can also quickly create columns of ones or zeros:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>(tcc/ones <span class="dv">10</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[10]</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>(tcc/zeros <span class="dv">10</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[10]</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="column" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="column">Column?</h4>
|
||||
<p>Finally, you can use the <code>column?</code> function to check if an item is a column:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="va">false</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? (tcc/column))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Tablecloth’s datasets of course consists of columns:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? (<span class="kw">-></span> (tc/dataset {<span class="at">:a</span> [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>]})</span>
|
||||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:a</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="types-and-type-detection" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="types-and-type-detection">Types and Type detection</h3>
|
||||
<p>The default set of types for a column are defined in the underlying “tech ml” system. We can see the set here:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb17"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>(tech.v3.datatype.casting/all-datatypes)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb18"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>(<span class="at">:int32</span></span>
|
||||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:int16</span></span>
|
||||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:float32</span></span>
|
||||
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:float64</span></span>
|
||||
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:int64</span></span>
|
||||
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint64</span></span>
|
||||
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:string</span></span>
|
||||
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint16</span></span>
|
||||
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:int8</span></span>
|
||||
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint32</span></span>
|
||||
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:keyword</span></span>
|
||||
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:decimal</span></span>
|
||||
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:uuid</span></span>
|
||||
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:boolean</span></span>
|
||||
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:object</span></span>
|
||||
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:char</span></span>
|
||||
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint8</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<section id="typeof-typeof" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="typeof-typeof">Typeof & Typeof?</h4>
|
||||
<p>When you create a column, the underlying system will try to autodetect its type. We can see that here using the <code>tcc/typeof</code> function to check the type of a column:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb19"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb20"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="at">:int64</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb21"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:a</span> <span class="at">:b</span> <span class="at">:c</span> <span class="at">:d</span> <span class="at">:e</span>])</span>
|
||||
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb22"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="at">:keyword</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Columns containing heterogenous data will receive type <code>:object</code>:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb23"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="at">:b</span> <span class="dv">3</span> <span class="at">:c</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb24"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="at">:object</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can also use the <code>tcc/typeof?</code> function to check the value of a function as an asssertion:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb25"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:boolean</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb26"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="va">false</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb27"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:int64</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb28"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Tablecloth has a concept of “concrete” and “general” types. A general type is the broad category of type and the concrete type is the actual type in memory. For example, a concrete type is a 64-bit integer <code>:int64</code>, which is also of the general type <code>:integer</code>. The <code>typeof?</code> function supports checking both.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb29"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:int64</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb30"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb31"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:integer</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb32"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="column-access-manipulation" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-access-manipulation">Column Access & Manipulation</h3>
|
||||
<section id="column-access" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="column-access">Column Access</h4>
|
||||
<p>The method for accessing a particular index position in a column is the same as for Clojure vectors:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb33"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">get</span> <span class="dv">3</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb34"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="dv">4</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb35"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">nth</span> <span class="dv">3</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb36"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="dv">4</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="slice" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="slice">Slice</h4>
|
||||
<p>You can also slice a column</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb37"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">5</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb38"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">5</span>, <span class="dv">6</span>, <span class="dv">7</span>, <span class="dv">8</span>, <span class="dv">9</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb39"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">1</span> <span class="dv">4</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb40"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[4]</span></span>
|
||||
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb41"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">0</span> <span class="dv">9</span> <span class="dv">2</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb42"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>, <span class="dv">4</span>, <span class="dv">6</span>, <span class="dv">8</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>For clarity, the <code>slice</code> method supports the <code>:end</code> and <code>:start</code> keywords:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb43"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="at">:start</span> <span class="at">:end</span> <span class="dv">2</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb44"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>, <span class="dv">4</span>, <span class="dv">6</span>, <span class="dv">8</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>If you need to create a discontinuous subset of the column, you can use the <code>select</code> function. This method accepts an array of index positions or an array of booleans. When using boolean select, a true value will select the value at the index positions containing true values:</p>
|
||||
</section>
|
||||
<section id="select" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="select">Select</h4>
|
||||
<p>Select the values at index positions 1 and 9:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb45"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> (tcc/select [<span class="dv">1</span> <span class="dv">9</span>]))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb46"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[2]</span></span>
|
||||
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">9</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Select the values at index positions 0 and 2 using booelan select:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb47"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a> (tcc/select (tcc/column [<span class="va">true</span> <span class="va">false</span> <span class="va">true</span>])))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb48"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[2]</span></span>
|
||||
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="sort" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="sort">Sort</h4>
|
||||
<p>Use <code>sort-column</code> to sort a column: Default sort is in ascending order:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb49"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:c</span> <span class="at">:z</span> <span class="at">:a</span> <span class="at">:f</span>])</span>
|
||||
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb50"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;keyword&gt;[4]</span></span>
|
||||
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>[<span class="at">:a</span>, <span class="at">:c</span>, <span class="at">:f</span>, <span class="at">:z</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can provide the <code>:desc</code> and <code>:asc</code> keywords to change the default behavior:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb51"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:c</span> <span class="at">:z</span> <span class="at">:a</span> <span class="at">:f</span>])</span>
|
||||
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column <span class="at">:desc</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb52"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;keyword&gt;[4]</span></span>
|
||||
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>[<span class="at">:z</span>, <span class="at">:f</span>, <span class="at">:c</span>, <span class="at">:a</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can also provide a comparator fn:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb53"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [{<span class="at">:position</span> <span class="dv">2</span></span>
|
||||
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:text</span> <span class="st">"and then stopped"</span>}</span>
|
||||
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:position</span> <span class="dv">1</span></span>
|
||||
<span id="cb53-4"><a href="#cb53-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:text</span> <span class="st">"I ran fast"</span>}])</span>
|
||||
<span id="cb53-5"><a href="#cb53-5" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column (<span class="kw">fn</span> [a b] (<span class="kw"><</span> (<span class="at">:position</span> a) (<span class="at">:position</span> b)))))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb54"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;persistent-map&gt;[2]</span></span>
|
||||
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a>[{<span class="at">:position</span> <span class="dv">1</span>, <span class="at">:text</span> <span class="st">"I ran fast"</span>}, {<span class="at">:position</span> <span class="dv">2</span>, <span class="at">:text</span> <span class="st">"and then stopped"</span>}]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="column-operations" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-operations">Column Operations</h3>
|
||||
<p>The Column API contains a large number of operations. These operations all take one or more columns as an argument, and they return either a scalar value or a new column, depending on the operations. These operations all take a column as the first argument so they are easy to use with the pipe <code>-></code> macro, as with all functions in Tablecloth.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb55"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> a </span>(tcc/column [<span class="dv">20</span> <span class="dv">30</span> <span class="dv">40</span> <span class="dv">50</span>]))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb56"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> b </span>(tcc/column (<span class="kw">range</span> <span class="dv">4</span>)))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb57"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a>(tcc/- a b)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb58"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[4]</span></span>
|
||||
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">20</span>, <span class="dv">29</span>, <span class="dv">38</span>, <span class="dv">47</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb59"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>(tcc/pow a <span class="dv">2</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb60"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;float64&gt;[4]</span></span>
|
||||
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>[<span class="fl">400.0</span>, <span class="fl">900.0</span>, <span class="dv">1600</span>, <span class="dv">2500</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb61"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>(tcc/* <span class="dv">10</span> (tcc/sin a))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb62"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;float64&gt;[4]</span></span>
|
||||
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>[<span class="fl">9.129</span>, -<span class="fl">9.880</span>, <span class="fl">7.451</span>, -<span class="fl">2.624</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb63"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>(tcc/< a <span class="dv">35</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb64"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[4]</span></span>
|
||||
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a>[<span class="va">true</span>, <span class="va">true</span>, <span class="va">false</span>, <span class="va">false</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>All these operations take a column as their first argument and return a column, so they can be chained easily.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb65"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> a</span>
|
||||
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a> (tcc/* b)</span>
|
||||
<span id="cb65-3"><a href="#cb65-3" aria-hidden="true" tabindex="-1"></a> (tcc/< <span class="dv">70</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb66"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[4]</span></span>
|
||||
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb66-3"><a href="#cb66-3" aria-hidden="true" tabindex="-1"></a>[<span class="va">true</span>, <span class="va">true</span>, <span class="va">false</span>, <span class="va">false</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div style="background-color:grey;height:2px;width:100%;">
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<pre><small><small>source: <a href="https://github.com/scicloj/tablecloth/blob/master/notebooks/column_api.clj">notebooks/column_api.clj</a></small></small></pre>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<!-- /main column -->
|
||||
<script id="quarto-html-after-body" type="application/javascript">
|
||||
window.document.addEventListener("DOMContentLoaded", function (event) {
|
||||
const toggleBodyColorMode = (bsSheetEl) => {
|
||||
const mode = bsSheetEl.getAttribute("data-mode");
|
||||
const bodyEl = window.document.querySelector("body");
|
||||
if (mode === "dark") {
|
||||
bodyEl.classList.add("quarto-dark");
|
||||
bodyEl.classList.remove("quarto-light");
|
||||
} else {
|
||||
bodyEl.classList.add("quarto-light");
|
||||
bodyEl.classList.remove("quarto-dark");
|
||||
}
|
||||
}
|
||||
const toggleBodyColorPrimary = () => {
|
||||
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
|
||||
if (bsSheetEl) {
|
||||
toggleBodyColorMode(bsSheetEl);
|
||||
}
|
||||
}
|
||||
toggleBodyColorPrimary();
|
||||
const icon = "";
|
||||
const anchorJS = new window.AnchorJS();
|
||||
anchorJS.options = {
|
||||
placement: 'right',
|
||||
icon: icon
|
||||
};
|
||||
anchorJS.add('.anchored');
|
||||
const isCodeAnnotation = (el) => {
|
||||
for (const clz of el.classList) {
|
||||
if (clz.startsWith('code-annotation-')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const clipboard = new window.ClipboardJS('.code-copy-button', {
|
||||
text: function(trigger) {
|
||||
const codeEl = trigger.previousElementSibling.cloneNode(true);
|
||||
for (const childEl of codeEl.children) {
|
||||
if (isCodeAnnotation(childEl)) {
|
||||
childEl.remove();
|
||||
}
|
||||
}
|
||||
return codeEl.innerText;
|
||||
}
|
||||
});
|
||||
clipboard.on('success', function(e) {
|
||||
// button target
|
||||
const button = e.trigger;
|
||||
// don't keep focus
|
||||
button.blur();
|
||||
// flash "checked"
|
||||
button.classList.add('code-copy-button-checked');
|
||||
var currentTitle = button.getAttribute("title");
|
||||
button.setAttribute("title", "Copied!");
|
||||
let tooltip;
|
||||
if (window.bootstrap) {
|
||||
button.setAttribute("data-bs-toggle", "tooltip");
|
||||
button.setAttribute("data-bs-placement", "left");
|
||||
button.setAttribute("data-bs-title", "Copied!");
|
||||
tooltip = new bootstrap.Tooltip(button,
|
||||
{ trigger: "manual",
|
||||
customClass: "code-copy-button-tooltip",
|
||||
offset: [0, -8]});
|
||||
tooltip.show();
|
||||
}
|
||||
setTimeout(function() {
|
||||
if (tooltip) {
|
||||
tooltip.hide();
|
||||
button.removeAttribute("data-bs-title");
|
||||
button.removeAttribute("data-bs-toggle");
|
||||
button.removeAttribute("data-bs-placement");
|
||||
}
|
||||
button.setAttribute("title", currentTitle);
|
||||
button.classList.remove('code-copy-button-checked');
|
||||
}, 1000);
|
||||
// clear code selection
|
||||
e.clearSelection();
|
||||
});
|
||||
function tippyHover(el, contentFn) {
|
||||
const config = {
|
||||
allowHTML: true,
|
||||
content: contentFn,
|
||||
maxWidth: 500,
|
||||
delay: 100,
|
||||
arrow: false,
|
||||
appendTo: function(el) {
|
||||
return el.parentElement;
|
||||
},
|
||||
interactive: true,
|
||||
interactiveBorder: 10,
|
||||
theme: 'quarto',
|
||||
placement: 'bottom-start'
|
||||
};
|
||||
window.tippy(el, config);
|
||||
}
|
||||
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
|
||||
for (var i=0; i<noterefs.length; i++) {
|
||||
const ref = noterefs[i];
|
||||
tippyHover(ref, function() {
|
||||
// use id or data attribute instead here
|
||||
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
|
||||
try { href = new URL(href).hash; } catch {}
|
||||
const id = href.replace(/^#\/?/, "");
|
||||
const note = window.document.getElementById(id);
|
||||
return note.innerHTML;
|
||||
});
|
||||
}
|
||||
let selectedAnnoteEl;
|
||||
const selectorForAnnotation = ( cell, annotation) => {
|
||||
let cellAttr = 'data-code-cell="' + cell + '"';
|
||||
let lineAttr = 'data-code-annotation="' + annotation + '"';
|
||||
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
|
||||
return selector;
|
||||
}
|
||||
const selectCodeLines = (annoteEl) => {
|
||||
const doc = window.document;
|
||||
const targetCell = annoteEl.getAttribute("data-target-cell");
|
||||
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
|
||||
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
|
||||
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
|
||||
const lineIds = lines.map((line) => {
|
||||
return targetCell + "-" + line;
|
||||
})
|
||||
let top = null;
|
||||
let height = null;
|
||||
let parent = null;
|
||||
if (lineIds.length > 0) {
|
||||
//compute the position of the single el (top and bottom and make a div)
|
||||
const el = window.document.getElementById(lineIds[0]);
|
||||
top = el.offsetTop;
|
||||
height = el.offsetHeight;
|
||||
parent = el.parentElement.parentElement;
|
||||
if (lineIds.length > 1) {
|
||||
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
|
||||
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
|
||||
height = bottom - top;
|
||||
}
|
||||
if (top !== null && height !== null && parent !== null) {
|
||||
// cook up a div (if necessary) and position it
|
||||
let div = window.document.getElementById("code-annotation-line-highlight");
|
||||
if (div === null) {
|
||||
div = window.document.createElement("div");
|
||||
div.setAttribute("id", "code-annotation-line-highlight");
|
||||
div.style.position = 'absolute';
|
||||
parent.appendChild(div);
|
||||
}
|
||||
div.style.top = top - 2 + "px";
|
||||
div.style.height = height + 4 + "px";
|
||||
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
|
||||
if (gutterDiv === null) {
|
||||
gutterDiv = window.document.createElement("div");
|
||||
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
|
||||
gutterDiv.style.position = 'absolute';
|
||||
const codeCell = window.document.getElementById(targetCell);
|
||||
const gutter = codeCell.querySelector('.code-annotation-gutter');
|
||||
gutter.appendChild(gutterDiv);
|
||||
}
|
||||
gutterDiv.style.top = top - 2 + "px";
|
||||
gutterDiv.style.height = height + 4 + "px";
|
||||
}
|
||||
selectedAnnoteEl = annoteEl;
|
||||
}
|
||||
};
|
||||
const unselectCodeLines = () => {
|
||||
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
|
||||
elementsIds.forEach((elId) => {
|
||||
const div = window.document.getElementById(elId);
|
||||
if (div) {
|
||||
div.remove();
|
||||
}
|
||||
});
|
||||
selectedAnnoteEl = undefined;
|
||||
};
|
||||
// Attach click handler to the DT
|
||||
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
|
||||
for (const annoteDlNode of annoteDls) {
|
||||
annoteDlNode.addEventListener('click', (event) => {
|
||||
const clickedEl = event.target;
|
||||
if (clickedEl !== selectedAnnoteEl) {
|
||||
unselectCodeLines();
|
||||
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
|
||||
if (activeEl) {
|
||||
activeEl.classList.remove('code-annotation-active');
|
||||
}
|
||||
selectCodeLines(clickedEl);
|
||||
clickedEl.classList.add('code-annotation-active');
|
||||
} else {
|
||||
// Unselect the line
|
||||
unselectCodeLines();
|
||||
clickedEl.classList.remove('code-annotation-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
const findCites = (el) => {
|
||||
const parentEl = el.parentElement;
|
||||
if (parentEl) {
|
||||
const cites = parentEl.dataset.cites;
|
||||
if (cites) {
|
||||
return {
|
||||
el,
|
||||
cites: cites.split(' ')
|
||||
};
|
||||
} else {
|
||||
return findCites(el.parentElement)
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
|
||||
for (var i=0; i<bibliorefs.length; i++) {
|
||||
const ref = bibliorefs[i];
|
||||
const citeInfo = findCites(ref);
|
||||
if (citeInfo) {
|
||||
tippyHover(citeInfo.el, function() {
|
||||
var popup = window.document.createElement('div');
|
||||
citeInfo.cites.forEach(function(cite) {
|
||||
var citeDiv = window.document.createElement('div');
|
||||
citeDiv.classList.add('hanging-indent');
|
||||
citeDiv.classList.add('csl-entry');
|
||||
var biblioDiv = window.document.getElementById('ref-' + cite);
|
||||
if (biblioDiv) {
|
||||
citeDiv.innerHTML = biblioDiv.innerHTML;
|
||||
}
|
||||
popup.appendChild(citeDiv);
|
||||
});
|
||||
return popup.innerHTML;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div> <!-- /content -->
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+189
@@ -0,0 +1,189 @@
|
||||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-kw-color: #859900;
|
||||
--quarto-hl-fu-color: #268bd2;
|
||||
--quarto-hl-va-color: #268bd2;
|
||||
--quarto-hl-cf-color: #859900;
|
||||
--quarto-hl-op-color: #859900;
|
||||
--quarto-hl-bu-color: #cb4b16;
|
||||
--quarto-hl-ex-color: #268bd2;
|
||||
--quarto-hl-pp-color: #cb4b16;
|
||||
--quarto-hl-at-color: #268bd2;
|
||||
--quarto-hl-ch-color: #2aa198;
|
||||
--quarto-hl-sc-color: #dc322f;
|
||||
--quarto-hl-st-color: #2aa198;
|
||||
--quarto-hl-vs-color: #2aa198;
|
||||
--quarto-hl-ss-color: #dc322f;
|
||||
--quarto-hl-im-color: #2aa198;
|
||||
--quarto-hl-dt-color: #b58900;
|
||||
--quarto-hl-dv-color: #2aa198;
|
||||
--quarto-hl-bn-color: #2aa198;
|
||||
--quarto-hl-fl-color: #2aa198;
|
||||
--quarto-hl-cn-color: #2aa198;
|
||||
--quarto-hl-co-color: #93a1a1;
|
||||
--quarto-hl-do-color: #dc322f;
|
||||
--quarto-hl-an-color: #268bd2;
|
||||
--quarto-hl-cv-color: #2aa198;
|
||||
--quarto-hl-re-color: #268bd2;
|
||||
--quarto-hl-in-color: #b58900;
|
||||
--quarto-hl-wa-color: #cb4b16;
|
||||
--quarto-hl-al-color: #d33682;
|
||||
--quarto-hl-er-color: #dc322f;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
code span.bu {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.ex {
|
||||
color: #268bd2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #b58900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #2aa198;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #93a1a1;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.re {
|
||||
color: #268bd2;
|
||||
background-color: #eee8d5;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #d33682;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #dc322f;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
|
||||
@@ -0,0 +1,902 @@
|
||||
const sectionChanged = new CustomEvent("quarto-sectionChanged", {
|
||||
detail: {},
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
});
|
||||
|
||||
const layoutMarginEls = () => {
|
||||
// Find any conflicting margin elements and add margins to the
|
||||
// top to prevent overlap
|
||||
const marginChildren = window.document.querySelectorAll(
|
||||
".column-margin.column-container > * "
|
||||
);
|
||||
|
||||
let lastBottom = 0;
|
||||
for (const marginChild of marginChildren) {
|
||||
if (marginChild.offsetParent !== null) {
|
||||
// clear the top margin so we recompute it
|
||||
marginChild.style.marginTop = null;
|
||||
const top = marginChild.getBoundingClientRect().top + window.scrollY;
|
||||
console.log({
|
||||
childtop: marginChild.getBoundingClientRect().top,
|
||||
scroll: window.scrollY,
|
||||
top,
|
||||
lastBottom,
|
||||
});
|
||||
if (top < lastBottom) {
|
||||
const margin = lastBottom - top;
|
||||
marginChild.style.marginTop = `${margin}px`;
|
||||
}
|
||||
const styles = window.getComputedStyle(marginChild);
|
||||
const marginTop = parseFloat(styles["marginTop"]);
|
||||
|
||||
console.log({
|
||||
top,
|
||||
height: marginChild.getBoundingClientRect().height,
|
||||
marginTop,
|
||||
total: top + marginChild.getBoundingClientRect().height + marginTop,
|
||||
});
|
||||
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.document.addEventListener("DOMContentLoaded", function (_event) {
|
||||
// Recompute the position of margin elements anytime the body size changes
|
||||
if (window.ResizeObserver) {
|
||||
const resizeObserver = new window.ResizeObserver(
|
||||
throttle(layoutMarginEls, 50)
|
||||
);
|
||||
resizeObserver.observe(window.document.body);
|
||||
}
|
||||
|
||||
const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
|
||||
const sidebarEl = window.document.getElementById("quarto-sidebar");
|
||||
const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
|
||||
const marginSidebarEl = window.document.getElementById(
|
||||
"quarto-margin-sidebar"
|
||||
);
|
||||
// function to determine whether the element has a previous sibling that is active
|
||||
const prevSiblingIsActiveLink = (el) => {
|
||||
const sibling = el.previousElementSibling;
|
||||
if (sibling && sibling.tagName === "A") {
|
||||
return sibling.classList.contains("active");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
|
||||
function fireSlideEnter(e) {
|
||||
const event = window.document.createEvent("Event");
|
||||
event.initEvent("slideenter", true, true);
|
||||
window.document.dispatchEvent(event);
|
||||
}
|
||||
const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("shown.bs.tab", fireSlideEnter);
|
||||
});
|
||||
|
||||
// fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
|
||||
document.addEventListener("tabby", fireSlideEnter, false);
|
||||
|
||||
// Track scrolling and mark TOC links as active
|
||||
// get table of contents and sidebar (bail if we don't have at least one)
|
||||
const tocLinks = tocEl
|
||||
? [...tocEl.querySelectorAll("a[data-scroll-target]")]
|
||||
: [];
|
||||
const makeActive = (link) => tocLinks[link].classList.add("active");
|
||||
const removeActive = (link) => tocLinks[link].classList.remove("active");
|
||||
const removeAllActive = () =>
|
||||
[...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
|
||||
|
||||
// activate the anchor for a section associated with this TOC entry
|
||||
tocLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
if (link.href.indexOf("#") !== -1) {
|
||||
const anchor = link.href.split("#")[1];
|
||||
const heading = window.document.querySelector(
|
||||
`[data-anchor-id=${anchor}]`
|
||||
);
|
||||
if (heading) {
|
||||
// Add the class
|
||||
heading.classList.add("reveal-anchorjs-link");
|
||||
|
||||
// function to show the anchor
|
||||
const handleMouseout = () => {
|
||||
heading.classList.remove("reveal-anchorjs-link");
|
||||
heading.removeEventListener("mouseout", handleMouseout);
|
||||
};
|
||||
|
||||
// add a function to clear the anchor when the user mouses out of it
|
||||
heading.addEventListener("mouseout", handleMouseout);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sections = tocLinks.map((link) => {
|
||||
const target = link.getAttribute("data-scroll-target");
|
||||
if (target.startsWith("#")) {
|
||||
return window.document.getElementById(decodeURI(`${target.slice(1)}`));
|
||||
} else {
|
||||
return window.document.querySelector(decodeURI(`${target}`));
|
||||
}
|
||||
});
|
||||
|
||||
const sectionMargin = 200;
|
||||
let currentActive = 0;
|
||||
// track whether we've initialized state the first time
|
||||
let init = false;
|
||||
|
||||
const updateActiveLink = () => {
|
||||
// The index from bottom to top (e.g. reversed list)
|
||||
let sectionIndex = -1;
|
||||
if (
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
window.document.body.offsetHeight
|
||||
) {
|
||||
sectionIndex = 0;
|
||||
} else {
|
||||
sectionIndex = [...sections].reverse().findIndex((section) => {
|
||||
if (section) {
|
||||
return window.pageYOffset >= section.offsetTop - sectionMargin;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sectionIndex > -1) {
|
||||
const current = sections.length - sectionIndex - 1;
|
||||
if (current !== currentActive) {
|
||||
removeAllActive();
|
||||
currentActive = current;
|
||||
makeActive(current);
|
||||
if (init) {
|
||||
window.dispatchEvent(sectionChanged);
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const inHiddenRegion = (top, bottom, hiddenRegions) => {
|
||||
for (const region of hiddenRegions) {
|
||||
if (top <= region.bottom && bottom >= region.top) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const categorySelector = "header.quarto-title-block .quarto-category";
|
||||
const activateCategories = (href) => {
|
||||
// Find any categories
|
||||
// Surround them with a link pointing back to:
|
||||
// #category=Authoring
|
||||
try {
|
||||
const categoryEls = window.document.querySelectorAll(categorySelector);
|
||||
for (const categoryEl of categoryEls) {
|
||||
const categoryText = categoryEl.textContent;
|
||||
if (categoryText) {
|
||||
const link = `${href}#category=${encodeURIComponent(categoryText)}`;
|
||||
const linkEl = window.document.createElement("a");
|
||||
linkEl.setAttribute("href", link);
|
||||
for (const child of categoryEl.childNodes) {
|
||||
linkEl.append(child);
|
||||
}
|
||||
categoryEl.appendChild(linkEl);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
};
|
||||
function hasTitleCategories() {
|
||||
return window.document.querySelector(categorySelector) !== null;
|
||||
}
|
||||
|
||||
function offsetRelativeUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
return offset ? offset + url : url;
|
||||
}
|
||||
|
||||
function offsetAbsoluteUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
const baseUrl = new URL(offset, window.location);
|
||||
|
||||
const projRelativeUrl = url.replace(baseUrl, "");
|
||||
if (projRelativeUrl.startsWith("/")) {
|
||||
return projRelativeUrl;
|
||||
} else {
|
||||
return "/" + projRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// read a meta tag value
|
||||
function getMeta(metaName) {
|
||||
const metas = window.document.getElementsByTagName("meta");
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
if (metas[i].getAttribute("name") === metaName) {
|
||||
return metas[i].getAttribute("content");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
async function findAndActivateCategories() {
|
||||
const currentPagePath = offsetAbsoluteUrl(window.location.href);
|
||||
const response = await fetch(offsetRelativeUrl("listings.json"));
|
||||
if (response.status == 200) {
|
||||
return response.json().then(function (listingPaths) {
|
||||
const listingHrefs = [];
|
||||
for (const listingPath of listingPaths) {
|
||||
const pathWithoutLeadingSlash = listingPath.listing.substring(1);
|
||||
for (const item of listingPath.items) {
|
||||
if (
|
||||
item === currentPagePath ||
|
||||
item === currentPagePath + "index.html"
|
||||
) {
|
||||
// Resolve this path against the offset to be sure
|
||||
// we already are using the correct path to the listing
|
||||
// (this adjusts the listing urls to be rooted against
|
||||
// whatever root the page is actually running against)
|
||||
const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
|
||||
const baseUrl = window.location;
|
||||
const resolvedPath = new URL(relative, baseUrl);
|
||||
listingHrefs.push(resolvedPath.pathname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const nearestListing = findNearestParentListing(
|
||||
offsetAbsoluteUrl(window.location.pathname),
|
||||
listingHrefs
|
||||
);
|
||||
if (nearestListing) {
|
||||
activateCategories(nearestListing);
|
||||
} else {
|
||||
// See if the referrer is a listing page for this item
|
||||
const referredRelativePath = offsetAbsoluteUrl(document.referrer);
|
||||
const referrerListing = listingHrefs.find((listingHref) => {
|
||||
const isListingReferrer =
|
||||
listingHref === referredRelativePath ||
|
||||
listingHref === referredRelativePath + "index.html";
|
||||
return isListingReferrer;
|
||||
});
|
||||
|
||||
if (referrerListing) {
|
||||
// Try to use the referrer if possible
|
||||
activateCategories(referrerListing);
|
||||
} else if (listingHrefs.length > 0) {
|
||||
// Otherwise, just fall back to the first listing
|
||||
activateCategories(listingHrefs[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (hasTitleCategories()) {
|
||||
findAndActivateCategories();
|
||||
}
|
||||
|
||||
const findNearestParentListing = (href, listingHrefs) => {
|
||||
if (!href || !listingHrefs) {
|
||||
return undefined;
|
||||
}
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const relativeParts = href.substring(1).split("/");
|
||||
while (relativeParts.length > 0) {
|
||||
const path = relativeParts.join("/");
|
||||
for (const listingHref of listingHrefs) {
|
||||
if (listingHref.startsWith(path)) {
|
||||
return listingHref;
|
||||
}
|
||||
}
|
||||
relativeParts.pop();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const manageSidebarVisiblity = (el, placeholderDescriptor) => {
|
||||
let isVisible = true;
|
||||
let elRect;
|
||||
|
||||
return (hiddenRegions) => {
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the last element of the TOC
|
||||
const lastChildEl = el.lastElementChild;
|
||||
|
||||
if (lastChildEl) {
|
||||
// Converts the sidebar to a menu
|
||||
const convertToMenu = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 0;
|
||||
child.style.overflow = "hidden";
|
||||
}
|
||||
|
||||
nexttick(() => {
|
||||
const toggleContainer = window.document.createElement("div");
|
||||
toggleContainer.style.width = "100%";
|
||||
toggleContainer.classList.add("zindex-over-content");
|
||||
toggleContainer.classList.add("quarto-sidebar-toggle");
|
||||
toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
|
||||
toggleContainer.id = placeholderDescriptor.id;
|
||||
toggleContainer.style.position = "fixed";
|
||||
|
||||
const toggleIcon = window.document.createElement("i");
|
||||
toggleIcon.classList.add("quarto-sidebar-toggle-icon");
|
||||
toggleIcon.classList.add("bi");
|
||||
toggleIcon.classList.add("bi-caret-down-fill");
|
||||
|
||||
const toggleTitle = window.document.createElement("div");
|
||||
const titleEl = window.document.body.querySelector(
|
||||
placeholderDescriptor.titleSelector
|
||||
);
|
||||
if (titleEl) {
|
||||
toggleTitle.append(
|
||||
titleEl.textContent || titleEl.innerText,
|
||||
toggleIcon
|
||||
);
|
||||
}
|
||||
toggleTitle.classList.add("zindex-over-content");
|
||||
toggleTitle.classList.add("quarto-sidebar-toggle-title");
|
||||
toggleContainer.append(toggleTitle);
|
||||
|
||||
const toggleContents = window.document.createElement("div");
|
||||
toggleContents.classList = el.classList;
|
||||
toggleContents.classList.add("zindex-over-content");
|
||||
toggleContents.classList.add("quarto-sidebar-toggle-contents");
|
||||
for (const child of el.children) {
|
||||
if (child.id === "toc-title") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clone = child.cloneNode(true);
|
||||
clone.style.opacity = 1;
|
||||
clone.style.display = null;
|
||||
toggleContents.append(clone);
|
||||
}
|
||||
toggleContents.style.height = "0px";
|
||||
const positionToggle = () => {
|
||||
// position the element (top left of parent, same width as parent)
|
||||
if (!elRect) {
|
||||
elRect = el.getBoundingClientRect();
|
||||
}
|
||||
toggleContainer.style.left = `${elRect.left}px`;
|
||||
toggleContainer.style.top = `${elRect.top}px`;
|
||||
toggleContainer.style.width = `${elRect.width}px`;
|
||||
};
|
||||
positionToggle();
|
||||
|
||||
toggleContainer.append(toggleContents);
|
||||
el.parentElement.prepend(toggleContainer);
|
||||
|
||||
// Process clicks
|
||||
let tocShowing = false;
|
||||
// Allow the caller to control whether this is dismissed
|
||||
// when it is clicked (e.g. sidebar navigation supports
|
||||
// opening and closing the nav tree, so don't dismiss on click)
|
||||
const clickEl = placeholderDescriptor.dismissOnClick
|
||||
? toggleContainer
|
||||
: toggleTitle;
|
||||
|
||||
const closeToggle = () => {
|
||||
if (tocShowing) {
|
||||
toggleContainer.classList.remove("expanded");
|
||||
toggleContents.style.height = "0px";
|
||||
tocShowing = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get rid of any expanded toggle if the user scrolls
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
closeToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
// Handle positioning of the toggle
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
elRect = undefined;
|
||||
positionToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
window.addEventListener("quarto-hrChanged", () => {
|
||||
elRect = undefined;
|
||||
});
|
||||
|
||||
// Process the click
|
||||
clickEl.onclick = () => {
|
||||
if (!tocShowing) {
|
||||
toggleContainer.classList.add("expanded");
|
||||
toggleContents.style.height = null;
|
||||
tocShowing = true;
|
||||
} else {
|
||||
closeToggle();
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Converts a sidebar from a menu back to a sidebar
|
||||
const convertToSidebar = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 1;
|
||||
child.style.overflow = null;
|
||||
}
|
||||
|
||||
const placeholderEl = window.document.getElementById(
|
||||
placeholderDescriptor.id
|
||||
);
|
||||
if (placeholderEl) {
|
||||
placeholderEl.remove();
|
||||
}
|
||||
|
||||
el.classList.remove("rollup");
|
||||
};
|
||||
|
||||
if (isReaderMode()) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
} else {
|
||||
// Find the top and bottom o the element that is being managed
|
||||
const elTop = el.offsetTop;
|
||||
const elBottom =
|
||||
elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
|
||||
|
||||
if (!isVisible) {
|
||||
// If the element is current not visible reveal if there are
|
||||
// no conflicts with overlay regions
|
||||
if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToSidebar();
|
||||
isVisible = true;
|
||||
}
|
||||
} else {
|
||||
// If the element is visible, hide it if it conflicts with overlay regions
|
||||
// and insert a placeholder toggle (or if we're in reader mode)
|
||||
if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
for (const tabEl of tabEls) {
|
||||
const id = tabEl.getAttribute("data-bs-target");
|
||||
if (id) {
|
||||
const columnEl = document.querySelector(
|
||||
`${id} .column-margin, .tabset-margin-content`
|
||||
);
|
||||
if (columnEl)
|
||||
tabEl.addEventListener("shown.bs.tab", function (event) {
|
||||
const el = event.srcElement;
|
||||
if (el) {
|
||||
const visibleCls = `${el.id}-margin-content`;
|
||||
// walk up until we find a parent tabset
|
||||
let panelTabsetEl = el.parentElement;
|
||||
while (panelTabsetEl) {
|
||||
if (panelTabsetEl.classList.contains("panel-tabset")) {
|
||||
break;
|
||||
}
|
||||
panelTabsetEl = panelTabsetEl.parentElement;
|
||||
}
|
||||
|
||||
if (panelTabsetEl) {
|
||||
const prevSib = panelTabsetEl.previousElementSibling;
|
||||
if (
|
||||
prevSib &&
|
||||
prevSib.classList.contains("tabset-margin-container")
|
||||
) {
|
||||
const childNodes = prevSib.querySelectorAll(
|
||||
".tabset-margin-content"
|
||||
);
|
||||
for (const childEl of childNodes) {
|
||||
if (childEl.classList.contains(visibleCls)) {
|
||||
childEl.classList.remove("collapse");
|
||||
} else {
|
||||
childEl.classList.add("collapse");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutMarginEls();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Manage the visibility of the toc and the sidebar
|
||||
const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
|
||||
id: "quarto-toc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
|
||||
id: "quarto-sidebarnav-toggle",
|
||||
titleSelector: ".title",
|
||||
dismissOnClick: false,
|
||||
});
|
||||
let tocLeftScrollVisibility;
|
||||
if (leftTocEl) {
|
||||
tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
|
||||
id: "quarto-lefttoc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Find the first element that uses formatting in special columns
|
||||
const conflictingEls = window.document.body.querySelectorAll(
|
||||
'[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
|
||||
);
|
||||
|
||||
// Filter all the possibly conflicting elements into ones
|
||||
// the do conflict on the left or ride side
|
||||
const arrConflictingEls = Array.from(conflictingEls);
|
||||
const leftSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return false;
|
||||
}
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("right") &&
|
||||
!className.endsWith("container") &&
|
||||
className !== "column-margin"
|
||||
);
|
||||
});
|
||||
});
|
||||
const rightSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasMarginCaption = Array.from(el.classList).find((className) => {
|
||||
return className == "margin-caption";
|
||||
});
|
||||
if (hasMarginCaption) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
!className.endsWith("container") &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("left")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const kOverlapPaddingSize = 10;
|
||||
function toRegions(els) {
|
||||
return els.map((el) => {
|
||||
const boundRect = el.getBoundingClientRect();
|
||||
const top =
|
||||
boundRect.top +
|
||||
document.documentElement.scrollTop -
|
||||
kOverlapPaddingSize;
|
||||
return {
|
||||
top,
|
||||
bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let hasObserved = false;
|
||||
const visibleItemObserver = (els) => {
|
||||
let visibleElements = [...els];
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries, _observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (visibleElements.indexOf(entry.target) === -1) {
|
||||
visibleElements.push(entry.target);
|
||||
}
|
||||
} else {
|
||||
visibleElements = visibleElements.filter((visibleEntry) => {
|
||||
return visibleEntry !== entry;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasObserved) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
hasObserved = true;
|
||||
},
|
||||
{}
|
||||
);
|
||||
els.forEach((el) => {
|
||||
intersectionObserver.observe(el);
|
||||
});
|
||||
|
||||
return {
|
||||
getVisibleEntries: () => {
|
||||
return visibleElements;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const rightElementObserver = visibleItemObserver(rightSideConflictEls);
|
||||
const leftElementObserver = visibleItemObserver(leftSideConflictEls);
|
||||
|
||||
const hideOverlappedSidebars = () => {
|
||||
marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
|
||||
sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
|
||||
if (tocLeftScrollVisibility) {
|
||||
tocLeftScrollVisibility(
|
||||
toRegions(leftElementObserver.getVisibleEntries())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.quartoToggleReader = () => {
|
||||
// Applies a slow class (or removes it)
|
||||
// to update the transition speed
|
||||
const slowTransition = (slow) => {
|
||||
const manageTransition = (id, slow) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (slow) {
|
||||
el.classList.add("slow");
|
||||
} else {
|
||||
el.classList.remove("slow");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
manageTransition("TOC", slow);
|
||||
manageTransition("quarto-sidebar", slow);
|
||||
};
|
||||
const readerMode = !isReaderMode();
|
||||
setReaderModeValue(readerMode);
|
||||
|
||||
// If we're entering reader mode, slow the transition
|
||||
if (readerMode) {
|
||||
slowTransition(readerMode);
|
||||
}
|
||||
highlightReaderToggle(readerMode);
|
||||
hideOverlappedSidebars();
|
||||
|
||||
// If we're exiting reader mode, restore the non-slow transition
|
||||
if (!readerMode) {
|
||||
slowTransition(!readerMode);
|
||||
}
|
||||
};
|
||||
|
||||
const highlightReaderToggle = (readerMode) => {
|
||||
const els = document.querySelectorAll(".quarto-reader-toggle");
|
||||
if (els) {
|
||||
els.forEach((el) => {
|
||||
if (readerMode) {
|
||||
el.classList.add("reader");
|
||||
} else {
|
||||
el.classList.remove("reader");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setReaderModeValue = (val) => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
window.localStorage.setItem("quarto-reader-mode", val);
|
||||
} else {
|
||||
localReaderMode = val;
|
||||
}
|
||||
};
|
||||
|
||||
const isReaderMode = () => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
return window.localStorage.getItem("quarto-reader-mode") === "true";
|
||||
} else {
|
||||
return localReaderMode;
|
||||
}
|
||||
};
|
||||
let localReaderMode = null;
|
||||
|
||||
const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
|
||||
const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
|
||||
|
||||
// Walk the TOC and collapse/expand nodes
|
||||
// Nodes are expanded if:
|
||||
// - they are top level
|
||||
// - they have children that are 'active' links
|
||||
// - they are directly below an link that is 'active'
|
||||
const walk = (el, depth) => {
|
||||
// Tick depth when we enter a UL
|
||||
if (el.tagName === "UL") {
|
||||
depth = depth + 1;
|
||||
}
|
||||
|
||||
// It this is active link
|
||||
let isActiveNode = false;
|
||||
if (el.tagName === "A" && el.classList.contains("active")) {
|
||||
isActiveNode = true;
|
||||
}
|
||||
|
||||
// See if there is an active child to this element
|
||||
let hasActiveChild = false;
|
||||
for (child of el.children) {
|
||||
hasActiveChild = walk(child, depth) || hasActiveChild;
|
||||
}
|
||||
|
||||
// Process the collapse state if this is an UL
|
||||
if (el.tagName === "UL") {
|
||||
if (tocOpenDepth === -1 && depth > 1) {
|
||||
el.classList.add("collapse");
|
||||
} else if (
|
||||
depth <= tocOpenDepth ||
|
||||
hasActiveChild ||
|
||||
prevSiblingIsActiveLink(el)
|
||||
) {
|
||||
el.classList.remove("collapse");
|
||||
} else {
|
||||
el.classList.add("collapse");
|
||||
}
|
||||
|
||||
// untick depth when we leave a UL
|
||||
depth = depth - 1;
|
||||
}
|
||||
return hasActiveChild || isActiveNode;
|
||||
};
|
||||
|
||||
// walk the TOC and expand / collapse any items that should be shown
|
||||
|
||||
if (tocEl) {
|
||||
walk(tocEl, 0);
|
||||
updateActiveLink();
|
||||
}
|
||||
|
||||
// Throttle the scroll event and walk peridiocally
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 5)
|
||||
);
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 10)
|
||||
);
|
||||
hideOverlappedSidebars();
|
||||
highlightReaderToggle(isReaderMode());
|
||||
});
|
||||
|
||||
// grouped tabsets
|
||||
window.addEventListener("pageshow", (_event) => {
|
||||
function getTabSettings() {
|
||||
const data = localStorage.getItem("quarto-persistent-tabsets-data");
|
||||
if (!data) {
|
||||
localStorage.setItem("quarto-persistent-tabsets-data", "{}");
|
||||
return {};
|
||||
}
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
function setTabSettings(data) {
|
||||
localStorage.setItem(
|
||||
"quarto-persistent-tabsets-data",
|
||||
JSON.stringify(data)
|
||||
);
|
||||
}
|
||||
|
||||
function setTabState(groupName, groupValue) {
|
||||
const data = getTabSettings();
|
||||
data[groupName] = groupValue;
|
||||
setTabSettings(data);
|
||||
}
|
||||
|
||||
function toggleTab(tab, active) {
|
||||
const tabPanelId = tab.getAttribute("aria-controls");
|
||||
const tabPanel = document.getElementById(tabPanelId);
|
||||
if (active) {
|
||||
tab.classList.add("active");
|
||||
tabPanel.classList.add("active");
|
||||
} else {
|
||||
tab.classList.remove("active");
|
||||
tabPanel.classList.remove("active");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAll(selectedGroup, selectorsToSync) {
|
||||
for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
|
||||
const active = selectedGroup === thisGroup;
|
||||
for (const tab of tabs) {
|
||||
toggleTab(tab, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findSelectorsToSyncByLanguage() {
|
||||
const result = {};
|
||||
const tabs = Array.from(
|
||||
document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
|
||||
);
|
||||
for (const item of tabs) {
|
||||
const div = item.parentElement.parentElement.parentElement;
|
||||
const group = div.getAttribute("data-group");
|
||||
if (!result[group]) {
|
||||
result[group] = {};
|
||||
}
|
||||
const selectorsToSync = result[group];
|
||||
const value = item.innerHTML;
|
||||
if (!selectorsToSync[value]) {
|
||||
selectorsToSync[value] = [];
|
||||
}
|
||||
selectorsToSync[value].push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setupSelectorSync() {
|
||||
const selectorsToSync = findSelectorsToSyncByLanguage();
|
||||
Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
|
||||
Object.entries(tabSetsByValue).forEach(([value, items]) => {
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("click", (_event) => {
|
||||
setTabState(group, value);
|
||||
toggleAll(value, selectorsToSync[group]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return selectorsToSync;
|
||||
}
|
||||
|
||||
const selectorsToSync = setupSelectorSync();
|
||||
for (const [group, selectedName] of Object.entries(getTabSettings())) {
|
||||
const selectors = selectorsToSync[group];
|
||||
// it's possible that stale state gives us empty selections, so we explicitly check here.
|
||||
if (selectors) {
|
||||
toggleAll(selectedName, selectors);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function throttle(func, wait) {
|
||||
let waiting = false;
|
||||
return function () {
|
||||
if (!waiting) {
|
||||
func.apply(this, arguments);
|
||||
waiting = true;
|
||||
setTimeout(function () {
|
||||
waiting = false;
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function nexttick(func) {
|
||||
return setTimeout(func, 0);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+238
@@ -0,0 +1,238 @@
|
||||
;; # Tablecloth Column Exploration
|
||||
|
||||
^{:kind/hidden true}
|
||||
(ns intro
|
||||
(:require [tablecloth.api :as tc]
|
||||
[scicloj.clay.v2.api :as clay]
|
||||
[scicloj.kindly.v3.api :as kindly]
|
||||
[scicloj.kindly.v3.kind :as kind]))
|
||||
|
||||
^{:kind/hidden true}
|
||||
(clay/start!)
|
||||
|
||||
^{:kind/hidden true}
|
||||
(comment
|
||||
(clay/show-doc! "docs/column_exploration.clj" {:hide-doc? true})
|
||||
(clay/write-html! "docs/column_exploration.html")
|
||||
,)
|
||||
|
||||
;; ## What is this exploration?
|
||||
;;
|
||||
;; We want to add a `column` entity to tablecloth that parallels `dataset`. It will make
|
||||
;; the column a first-class entity within tablecloth.
|
||||
|
||||
;; ## Usage
|
||||
|
||||
(require '[tablecloth.column.api :refer [column] :as col])
|
||||
|
||||
;; ### Column creation
|
||||
|
||||
;; We can create an empty column like this:
|
||||
|
||||
(column)
|
||||
|
||||
;; We can check if it it's a column.
|
||||
|
||||
(col/column? (column))
|
||||
|
||||
;; We can create a columns with data in a number of ways
|
||||
|
||||
(column [1 2 3 4])
|
||||
|
||||
(column (range 10))
|
||||
|
||||
;; When you do this the types of the resulting array is determined
|
||||
;; automatically from the items provided.
|
||||
|
||||
(let [int-column (column (range 10))]
|
||||
(col/typeof int-column))
|
||||
|
||||
(let [string-column (column ["foo" "bar"])]
|
||||
(col/typeof string-column))
|
||||
|
||||
|
||||
;; ### Basic Operations
|
||||
|
||||
;; Operations are right now in their own namespace
|
||||
(require '[tablecloth.column.api.operators :as ops])
|
||||
|
||||
;; With that imported we can perform a large number of operations:
|
||||
|
||||
(def a (column [20 30 40 50]))
|
||||
(def b (column (range 4)))
|
||||
|
||||
(ops/- a b)
|
||||
|
||||
(ops/pow a 2)
|
||||
|
||||
(ops/* 10 (ops/sin a))
|
||||
|
||||
(ops/< a 35)
|
||||
|
||||
;; All these operations take a column as their first argument and
|
||||
;; return a column, so they can be chained easily.
|
||||
|
||||
(-> a
|
||||
(ops/* b)
|
||||
(ops/< 70))
|
||||
|
||||
;; ### Subsetting and accesssing
|
||||
|
||||
;; You can access an element in a column in exactly the same ways you
|
||||
;; would in Clojure.
|
||||
|
||||
(def myclm (column (range 5)))
|
||||
|
||||
myclm
|
||||
|
||||
(myclm 2)
|
||||
|
||||
(nth myclm 2)
|
||||
|
||||
(get myclm 2)
|
||||
|
||||
;; #### Selecting multiple elements
|
||||
|
||||
;; There are two ways to select multiple elements from a column:
|
||||
;; * If you need to select a continuous subset, you can use `slice`;
|
||||
;; * if you may need to select diverse elements, use `select`.
|
||||
;;
|
||||
|
||||
;; **Slice**
|
||||
|
||||
;; The `slice` method allows you to use indexes to specify a portion
|
||||
;; of the column to extract.
|
||||
|
||||
(def myclm
|
||||
(column (repeatedly 10 #(rand-int 10))))
|
||||
|
||||
myclm
|
||||
|
||||
(col/slice myclm 3 5)
|
||||
|
||||
|
||||
;; It also supports negative indexing, making it possible to slice
|
||||
;; from the end of the column:
|
||||
|
||||
(col/slice myclm -7 -5)
|
||||
|
||||
;; It's also possible to slice from one direction to the beginning or
|
||||
;; end:
|
||||
|
||||
(col/slice myclm 7 :end)
|
||||
|
||||
(col/slice myclm -3 :end)
|
||||
|
||||
(col/slice myclm :start 7)
|
||||
|
||||
(col/slice myclm :start -3)
|
||||
|
||||
;; **Select**
|
||||
;;
|
||||
;; The `select` fn works by taking a list of index positions:
|
||||
|
||||
(col/select myclm [1 3 5 8])
|
||||
|
||||
;; We can combine this type of selection with the operations just
|
||||
;; demonstrated to select certain values.
|
||||
|
||||
|
||||
myclm
|
||||
|
||||
;; Let's see which positions are greter than 5.
|
||||
(ops/> myclm 5)
|
||||
|
||||
|
||||
;; We can use a column of boolean values like the one above with the `select` function as well. `select` will choose all the positions that are true. It's like supplying select a list of the index positions that hold true values.
|
||||
(col/select myclm (ops/> myclm 5))
|
||||
|
||||
|
||||
;; ### Iterating over a column
|
||||
|
||||
;; Many operations that you might want to perform on a column are
|
||||
;; available in the `tablecloth.column.api.operators` namespace.
|
||||
;; However, when there is a need to do something custom, you can also
|
||||
;; interate over the column.
|
||||
|
||||
(defn calc-percent [x]
|
||||
(/ x 100.0))
|
||||
|
||||
(col/column-map myclm calc-percent)
|
||||
|
||||
;; It's also possible to iterate over multiple columns by supplying a
|
||||
;; vector of columns:
|
||||
|
||||
(-> [(column [5 6 7 8 9])
|
||||
(column [1 2 3 4 5])]
|
||||
(col/column-map (partial *)))
|
||||
|
||||
|
||||
(comment
|
||||
(-> (column [1 nil 2 3 nil 0])
|
||||
(ops/* 10))
|
||||
|
||||
(-> (column [1 nil 2 3 nil 0])
|
||||
(ops/max [10 10 10 10 10 10]))
|
||||
|
||||
|
||||
(tech.v3.dataset.column/missing))
|
||||
|
||||
;; ### Sorting a column
|
||||
|
||||
;; You can use `sort-column` to sort a colum
|
||||
|
||||
(def myclm (column (repeatedly 10 #(rand-int 100))))
|
||||
|
||||
myclm
|
||||
|
||||
(col/sort-column myclm)
|
||||
|
||||
;; As you can see, sort-columns sorts in ascending order by default,
|
||||
;; but you can also specify a different order using ordering keywords
|
||||
;; `:asc` and `:desc`:
|
||||
|
||||
|
||||
(col/sort-column myclm :desc)
|
||||
|
||||
;; Finally, sort can also accept a `comparator-fn`:
|
||||
|
||||
(let [c (column ["1" "100" "4" "-10"])]
|
||||
(col/sort-column c (fn [a b]
|
||||
(let [a (parse-long a)
|
||||
b (parse-long b)]
|
||||
(< a b)))))
|
||||
|
||||
|
||||
;; ### Missing values
|
||||
|
||||
;; The column has built-in support for basic awareness and handling of
|
||||
;; missing values. Columns will be scanned for missing values when
|
||||
;; created.
|
||||
|
||||
(def myclm (column [10 nil -4 20 1000 nil -233]))
|
||||
|
||||
;; You can identify the set of index positions of missing values:
|
||||
|
||||
(col/missing myclm)
|
||||
|
||||
(col/count-missing myclm)
|
||||
|
||||
;; You can remove missing values:
|
||||
|
||||
(col/drop-missing myclm)
|
||||
|
||||
;; Or you can replace them:
|
||||
|
||||
(col/replace-missing myclm)
|
||||
|
||||
;; There are a range of built-in strategies:
|
||||
|
||||
(col/replace-missing myclm :midpoint)
|
||||
|
||||
|
||||
;; And you can provide your own value using a specific value or fn:
|
||||
|
||||
(col/replace-missing myclm :value 555)
|
||||
|
||||
(col/replace-missing myclm :value (fn [col-without-missing]
|
||||
(ops/mean col-without-missing)))
|
||||
+3813
File diff suppressed because one or more lines are too long
Vendored
+40366
File diff suppressed because it is too large
Load Diff
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Vendored
+12
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+189
@@ -0,0 +1,189 @@
|
||||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-kw-color: #859900;
|
||||
--quarto-hl-fu-color: #268bd2;
|
||||
--quarto-hl-va-color: #268bd2;
|
||||
--quarto-hl-cf-color: #859900;
|
||||
--quarto-hl-op-color: #859900;
|
||||
--quarto-hl-bu-color: #cb4b16;
|
||||
--quarto-hl-ex-color: #268bd2;
|
||||
--quarto-hl-pp-color: #cb4b16;
|
||||
--quarto-hl-at-color: #268bd2;
|
||||
--quarto-hl-ch-color: #2aa198;
|
||||
--quarto-hl-sc-color: #dc322f;
|
||||
--quarto-hl-st-color: #2aa198;
|
||||
--quarto-hl-vs-color: #2aa198;
|
||||
--quarto-hl-ss-color: #dc322f;
|
||||
--quarto-hl-im-color: #2aa198;
|
||||
--quarto-hl-dt-color: #b58900;
|
||||
--quarto-hl-dv-color: #2aa198;
|
||||
--quarto-hl-bn-color: #2aa198;
|
||||
--quarto-hl-fl-color: #2aa198;
|
||||
--quarto-hl-cn-color: #2aa198;
|
||||
--quarto-hl-co-color: #93a1a1;
|
||||
--quarto-hl-do-color: #dc322f;
|
||||
--quarto-hl-an-color: #268bd2;
|
||||
--quarto-hl-cv-color: #2aa198;
|
||||
--quarto-hl-re-color: #268bd2;
|
||||
--quarto-hl-in-color: #b58900;
|
||||
--quarto-hl-wa-color: #cb4b16;
|
||||
--quarto-hl-al-color: #d33682;
|
||||
--quarto-hl-er-color: #dc322f;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
code span.bu {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.ex {
|
||||
color: #268bd2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #b58900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #2aa198;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #93a1a1;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.re {
|
||||
color: #268bd2;
|
||||
background-color: #eee8d5;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #d33682;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #dc322f;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=2b9cc840a65eda1fcc822bf0b5308604.css.map */
|
||||
@@ -0,0 +1,189 @@
|
||||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-kw-color: #859900;
|
||||
--quarto-hl-fu-color: #268bd2;
|
||||
--quarto-hl-va-color: #268bd2;
|
||||
--quarto-hl-cf-color: #859900;
|
||||
--quarto-hl-op-color: #859900;
|
||||
--quarto-hl-bu-color: #cb4b16;
|
||||
--quarto-hl-ex-color: #268bd2;
|
||||
--quarto-hl-pp-color: #cb4b16;
|
||||
--quarto-hl-at-color: #268bd2;
|
||||
--quarto-hl-ch-color: #2aa198;
|
||||
--quarto-hl-sc-color: #dc322f;
|
||||
--quarto-hl-st-color: #2aa198;
|
||||
--quarto-hl-vs-color: #2aa198;
|
||||
--quarto-hl-ss-color: #dc322f;
|
||||
--quarto-hl-im-color: #2aa198;
|
||||
--quarto-hl-dt-color: #b58900;
|
||||
--quarto-hl-dv-color: #2aa198;
|
||||
--quarto-hl-bn-color: #2aa198;
|
||||
--quarto-hl-fl-color: #2aa198;
|
||||
--quarto-hl-cn-color: #2aa198;
|
||||
--quarto-hl-co-color: #93a1a1;
|
||||
--quarto-hl-do-color: #dc322f;
|
||||
--quarto-hl-an-color: #268bd2;
|
||||
--quarto-hl-cv-color: #2aa198;
|
||||
--quarto-hl-re-color: #268bd2;
|
||||
--quarto-hl-in-color: #b58900;
|
||||
--quarto-hl-wa-color: #cb4b16;
|
||||
--quarto-hl-al-color: #d33682;
|
||||
--quarto-hl-er-color: #dc322f;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
code span.bu {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.ex {
|
||||
color: #268bd2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #b58900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #2aa198;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #93a1a1;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.re {
|
||||
color: #268bd2;
|
||||
background-color: #eee8d5;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #d33682;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #dc322f;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
|
||||
@@ -0,0 +1,823 @@
|
||||
import * as tabsets from "./tabsets/tabsets.js";
|
||||
|
||||
const sectionChanged = new CustomEvent("quarto-sectionChanged", {
|
||||
detail: {},
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
});
|
||||
|
||||
const layoutMarginEls = () => {
|
||||
// Find any conflicting margin elements and add margins to the
|
||||
// top to prevent overlap
|
||||
const marginChildren = window.document.querySelectorAll(
|
||||
".column-margin.column-container > *, .margin-caption, .aside"
|
||||
);
|
||||
|
||||
let lastBottom = 0;
|
||||
for (const marginChild of marginChildren) {
|
||||
if (marginChild.offsetParent !== null) {
|
||||
// clear the top margin so we recompute it
|
||||
marginChild.style.marginTop = null;
|
||||
const top = marginChild.getBoundingClientRect().top + window.scrollY;
|
||||
if (top < lastBottom) {
|
||||
const marginChildStyle = window.getComputedStyle(marginChild);
|
||||
const marginBottom = parseFloat(marginChildStyle["marginBottom"]);
|
||||
const margin = lastBottom - top + marginBottom;
|
||||
marginChild.style.marginTop = `${margin}px`;
|
||||
}
|
||||
const styles = window.getComputedStyle(marginChild);
|
||||
const marginTop = parseFloat(styles["marginTop"]);
|
||||
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.document.addEventListener("DOMContentLoaded", function (_event) {
|
||||
// Recompute the position of margin elements anytime the body size changes
|
||||
if (window.ResizeObserver) {
|
||||
const resizeObserver = new window.ResizeObserver(
|
||||
throttle(() => {
|
||||
layoutMarginEls();
|
||||
if (
|
||||
window.document.body.getBoundingClientRect().width < 990 &&
|
||||
isReaderMode()
|
||||
) {
|
||||
quartoToggleReader();
|
||||
}
|
||||
}, 50)
|
||||
);
|
||||
resizeObserver.observe(window.document.body);
|
||||
}
|
||||
|
||||
const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
|
||||
const sidebarEl = window.document.getElementById("quarto-sidebar");
|
||||
const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
|
||||
const marginSidebarEl = window.document.getElementById(
|
||||
"quarto-margin-sidebar"
|
||||
);
|
||||
// function to determine whether the element has a previous sibling that is active
|
||||
const prevSiblingIsActiveLink = (el) => {
|
||||
const sibling = el.previousElementSibling;
|
||||
if (sibling && sibling.tagName === "A") {
|
||||
return sibling.classList.contains("active");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
|
||||
function fireSlideEnter(e) {
|
||||
const event = window.document.createEvent("Event");
|
||||
event.initEvent("slideenter", true, true);
|
||||
window.document.dispatchEvent(event);
|
||||
}
|
||||
const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("shown.bs.tab", fireSlideEnter);
|
||||
});
|
||||
|
||||
// fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
|
||||
document.addEventListener("tabby", fireSlideEnter, false);
|
||||
|
||||
// Track scrolling and mark TOC links as active
|
||||
// get table of contents and sidebar (bail if we don't have at least one)
|
||||
const tocLinks = tocEl
|
||||
? [...tocEl.querySelectorAll("a[data-scroll-target]")]
|
||||
: [];
|
||||
const makeActive = (link) => tocLinks[link].classList.add("active");
|
||||
const removeActive = (link) => tocLinks[link].classList.remove("active");
|
||||
const removeAllActive = () =>
|
||||
[...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
|
||||
|
||||
// activate the anchor for a section associated with this TOC entry
|
||||
tocLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
if (link.href.indexOf("#") !== -1) {
|
||||
const anchor = link.href.split("#")[1];
|
||||
const heading = window.document.querySelector(
|
||||
`[data-anchor-id="${anchor}"]`
|
||||
);
|
||||
if (heading) {
|
||||
// Add the class
|
||||
heading.classList.add("reveal-anchorjs-link");
|
||||
|
||||
// function to show the anchor
|
||||
const handleMouseout = () => {
|
||||
heading.classList.remove("reveal-anchorjs-link");
|
||||
heading.removeEventListener("mouseout", handleMouseout);
|
||||
};
|
||||
|
||||
// add a function to clear the anchor when the user mouses out of it
|
||||
heading.addEventListener("mouseout", handleMouseout);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sections = tocLinks.map((link) => {
|
||||
const target = link.getAttribute("data-scroll-target");
|
||||
if (target.startsWith("#")) {
|
||||
return window.document.getElementById(decodeURI(`${target.slice(1)}`));
|
||||
} else {
|
||||
return window.document.querySelector(decodeURI(`${target}`));
|
||||
}
|
||||
});
|
||||
|
||||
const sectionMargin = 200;
|
||||
let currentActive = 0;
|
||||
// track whether we've initialized state the first time
|
||||
let init = false;
|
||||
|
||||
const updateActiveLink = () => {
|
||||
// The index from bottom to top (e.g. reversed list)
|
||||
let sectionIndex = -1;
|
||||
if (
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
window.document.body.offsetHeight
|
||||
) {
|
||||
// This is the no-scroll case where last section should be the active one
|
||||
sectionIndex = 0;
|
||||
} else {
|
||||
// This finds the last section visible on screen that should be made active
|
||||
sectionIndex = [...sections].reverse().findIndex((section) => {
|
||||
if (section) {
|
||||
return window.pageYOffset >= section.offsetTop - sectionMargin;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sectionIndex > -1) {
|
||||
const current = sections.length - sectionIndex - 1;
|
||||
if (current !== currentActive) {
|
||||
removeAllActive();
|
||||
currentActive = current;
|
||||
makeActive(current);
|
||||
if (init) {
|
||||
window.dispatchEvent(sectionChanged);
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const inHiddenRegion = (top, bottom, hiddenRegions) => {
|
||||
for (const region of hiddenRegions) {
|
||||
if (top <= region.bottom && bottom >= region.top) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const categorySelector = "header.quarto-title-block .quarto-category";
|
||||
const activateCategories = (href) => {
|
||||
// Find any categories
|
||||
// Surround them with a link pointing back to:
|
||||
// #category=Authoring
|
||||
try {
|
||||
const categoryEls = window.document.querySelectorAll(categorySelector);
|
||||
for (const categoryEl of categoryEls) {
|
||||
const categoryText = categoryEl.textContent;
|
||||
if (categoryText) {
|
||||
const link = `${href}#category=${encodeURIComponent(categoryText)}`;
|
||||
const linkEl = window.document.createElement("a");
|
||||
linkEl.setAttribute("href", link);
|
||||
for (const child of categoryEl.childNodes) {
|
||||
linkEl.append(child);
|
||||
}
|
||||
categoryEl.appendChild(linkEl);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
};
|
||||
function hasTitleCategories() {
|
||||
return window.document.querySelector(categorySelector) !== null;
|
||||
}
|
||||
|
||||
function offsetRelativeUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
return offset ? offset + url : url;
|
||||
}
|
||||
|
||||
function offsetAbsoluteUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
const baseUrl = new URL(offset, window.location);
|
||||
|
||||
const projRelativeUrl = url.replace(baseUrl, "");
|
||||
if (projRelativeUrl.startsWith("/")) {
|
||||
return projRelativeUrl;
|
||||
} else {
|
||||
return "/" + projRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// read a meta tag value
|
||||
function getMeta(metaName) {
|
||||
const metas = window.document.getElementsByTagName("meta");
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
if (metas[i].getAttribute("name") === metaName) {
|
||||
return metas[i].getAttribute("content");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
async function findAndActivateCategories() {
|
||||
// Categories search with listing only use path without query
|
||||
const currentPagePath = offsetAbsoluteUrl(
|
||||
window.location.origin + window.location.pathname
|
||||
);
|
||||
const response = await fetch(offsetRelativeUrl("listings.json"));
|
||||
if (response.status == 200) {
|
||||
return response.json().then(function (listingPaths) {
|
||||
const listingHrefs = [];
|
||||
for (const listingPath of listingPaths) {
|
||||
const pathWithoutLeadingSlash = listingPath.listing.substring(1);
|
||||
for (const item of listingPath.items) {
|
||||
const encodedItem = encodeURI(item);
|
||||
if (
|
||||
encodedItem === currentPagePath ||
|
||||
encodedItem === currentPagePath + "index.html"
|
||||
) {
|
||||
// Resolve this path against the offset to be sure
|
||||
// we already are using the correct path to the listing
|
||||
// (this adjusts the listing urls to be rooted against
|
||||
// whatever root the page is actually running against)
|
||||
const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
|
||||
const baseUrl = window.location;
|
||||
const resolvedPath = new URL(relative, baseUrl);
|
||||
listingHrefs.push(resolvedPath.pathname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const nearestListing = findNearestParentListing(
|
||||
offsetAbsoluteUrl(window.location.pathname),
|
||||
listingHrefs
|
||||
);
|
||||
if (nearestListing) {
|
||||
activateCategories(nearestListing);
|
||||
} else {
|
||||
// See if the referrer is a listing page for this item
|
||||
const referredRelativePath = offsetAbsoluteUrl(document.referrer);
|
||||
const referrerListing = listingHrefs.find((listingHref) => {
|
||||
const isListingReferrer =
|
||||
listingHref === referredRelativePath ||
|
||||
listingHref === referredRelativePath + "index.html";
|
||||
return isListingReferrer;
|
||||
});
|
||||
|
||||
if (referrerListing) {
|
||||
// Try to use the referrer if possible
|
||||
activateCategories(referrerListing);
|
||||
} else if (listingHrefs.length > 0) {
|
||||
// Otherwise, just fall back to the first listing
|
||||
activateCategories(listingHrefs[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (hasTitleCategories()) {
|
||||
findAndActivateCategories();
|
||||
}
|
||||
|
||||
const findNearestParentListing = (href, listingHrefs) => {
|
||||
if (!href || !listingHrefs) {
|
||||
return undefined;
|
||||
}
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const relativeParts = href.substring(1).split("/");
|
||||
while (relativeParts.length > 0) {
|
||||
const path = relativeParts.join("/");
|
||||
for (const listingHref of listingHrefs) {
|
||||
if (listingHref.startsWith(path)) {
|
||||
return listingHref;
|
||||
}
|
||||
}
|
||||
relativeParts.pop();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const manageSidebarVisiblity = (el, placeholderDescriptor) => {
|
||||
let isVisible = true;
|
||||
let elRect;
|
||||
|
||||
return (hiddenRegions) => {
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the last element of the TOC
|
||||
const lastChildEl = el.lastElementChild;
|
||||
|
||||
if (lastChildEl) {
|
||||
// Converts the sidebar to a menu
|
||||
const convertToMenu = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 0;
|
||||
child.style.overflow = "hidden";
|
||||
child.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
nexttick(() => {
|
||||
const toggleContainer = window.document.createElement("div");
|
||||
toggleContainer.style.width = "100%";
|
||||
toggleContainer.classList.add("zindex-over-content");
|
||||
toggleContainer.classList.add("quarto-sidebar-toggle");
|
||||
toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
|
||||
toggleContainer.id = placeholderDescriptor.id;
|
||||
toggleContainer.style.position = "fixed";
|
||||
|
||||
const toggleIcon = window.document.createElement("i");
|
||||
toggleIcon.classList.add("quarto-sidebar-toggle-icon");
|
||||
toggleIcon.classList.add("bi");
|
||||
toggleIcon.classList.add("bi-caret-down-fill");
|
||||
|
||||
const toggleTitle = window.document.createElement("div");
|
||||
const titleEl = window.document.body.querySelector(
|
||||
placeholderDescriptor.titleSelector
|
||||
);
|
||||
if (titleEl) {
|
||||
toggleTitle.append(
|
||||
titleEl.textContent || titleEl.innerText,
|
||||
toggleIcon
|
||||
);
|
||||
}
|
||||
toggleTitle.classList.add("zindex-over-content");
|
||||
toggleTitle.classList.add("quarto-sidebar-toggle-title");
|
||||
toggleContainer.append(toggleTitle);
|
||||
|
||||
const toggleContents = window.document.createElement("div");
|
||||
toggleContents.classList = el.classList;
|
||||
toggleContents.classList.add("zindex-over-content");
|
||||
toggleContents.classList.add("quarto-sidebar-toggle-contents");
|
||||
for (const child of el.children) {
|
||||
if (child.id === "toc-title") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clone = child.cloneNode(true);
|
||||
clone.style.opacity = 1;
|
||||
clone.style.pointerEvents = null;
|
||||
clone.style.display = null;
|
||||
toggleContents.append(clone);
|
||||
}
|
||||
toggleContents.style.height = "0px";
|
||||
const positionToggle = () => {
|
||||
// position the element (top left of parent, same width as parent)
|
||||
if (!elRect) {
|
||||
elRect = el.getBoundingClientRect();
|
||||
}
|
||||
toggleContainer.style.left = `${elRect.left}px`;
|
||||
toggleContainer.style.top = `${elRect.top}px`;
|
||||
toggleContainer.style.width = `${elRect.width}px`;
|
||||
};
|
||||
positionToggle();
|
||||
|
||||
toggleContainer.append(toggleContents);
|
||||
el.parentElement.prepend(toggleContainer);
|
||||
|
||||
// Process clicks
|
||||
let tocShowing = false;
|
||||
// Allow the caller to control whether this is dismissed
|
||||
// when it is clicked (e.g. sidebar navigation supports
|
||||
// opening and closing the nav tree, so don't dismiss on click)
|
||||
const clickEl = placeholderDescriptor.dismissOnClick
|
||||
? toggleContainer
|
||||
: toggleTitle;
|
||||
|
||||
const closeToggle = () => {
|
||||
if (tocShowing) {
|
||||
toggleContainer.classList.remove("expanded");
|
||||
toggleContents.style.height = "0px";
|
||||
tocShowing = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get rid of any expanded toggle if the user scrolls
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
closeToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
// Handle positioning of the toggle
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
elRect = undefined;
|
||||
positionToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
window.addEventListener("quarto-hrChanged", () => {
|
||||
elRect = undefined;
|
||||
});
|
||||
|
||||
// Process the click
|
||||
clickEl.onclick = () => {
|
||||
if (!tocShowing) {
|
||||
toggleContainer.classList.add("expanded");
|
||||
toggleContents.style.height = null;
|
||||
tocShowing = true;
|
||||
} else {
|
||||
closeToggle();
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Converts a sidebar from a menu back to a sidebar
|
||||
const convertToSidebar = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 1;
|
||||
child.style.overflow = null;
|
||||
child.style.pointerEvents = null;
|
||||
}
|
||||
|
||||
const placeholderEl = window.document.getElementById(
|
||||
placeholderDescriptor.id
|
||||
);
|
||||
if (placeholderEl) {
|
||||
placeholderEl.remove();
|
||||
}
|
||||
|
||||
el.classList.remove("rollup");
|
||||
};
|
||||
|
||||
if (isReaderMode()) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
} else {
|
||||
// Find the top and bottom o the element that is being managed
|
||||
const elTop = el.offsetTop;
|
||||
const elBottom =
|
||||
elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
|
||||
|
||||
if (!isVisible) {
|
||||
// If the element is current not visible reveal if there are
|
||||
// no conflicts with overlay regions
|
||||
if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToSidebar();
|
||||
isVisible = true;
|
||||
}
|
||||
} else {
|
||||
// If the element is visible, hide it if it conflicts with overlay regions
|
||||
// and insert a placeholder toggle (or if we're in reader mode)
|
||||
if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
for (const tabEl of tabEls) {
|
||||
const id = tabEl.getAttribute("data-bs-target");
|
||||
if (id) {
|
||||
const columnEl = document.querySelector(
|
||||
`${id} .column-margin, .tabset-margin-content`
|
||||
);
|
||||
if (columnEl)
|
||||
tabEl.addEventListener("shown.bs.tab", function (event) {
|
||||
const el = event.srcElement;
|
||||
if (el) {
|
||||
const visibleCls = `${el.id}-margin-content`;
|
||||
// walk up until we find a parent tabset
|
||||
let panelTabsetEl = el.parentElement;
|
||||
while (panelTabsetEl) {
|
||||
if (panelTabsetEl.classList.contains("panel-tabset")) {
|
||||
break;
|
||||
}
|
||||
panelTabsetEl = panelTabsetEl.parentElement;
|
||||
}
|
||||
|
||||
if (panelTabsetEl) {
|
||||
const prevSib = panelTabsetEl.previousElementSibling;
|
||||
if (
|
||||
prevSib &&
|
||||
prevSib.classList.contains("tabset-margin-container")
|
||||
) {
|
||||
const childNodes = prevSib.querySelectorAll(
|
||||
".tabset-margin-content"
|
||||
);
|
||||
for (const childEl of childNodes) {
|
||||
if (childEl.classList.contains(visibleCls)) {
|
||||
childEl.classList.remove("collapse");
|
||||
} else {
|
||||
childEl.classList.add("collapse");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutMarginEls();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Manage the visibility of the toc and the sidebar
|
||||
const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
|
||||
id: "quarto-toc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
|
||||
id: "quarto-sidebarnav-toggle",
|
||||
titleSelector: ".title",
|
||||
dismissOnClick: false,
|
||||
});
|
||||
let tocLeftScrollVisibility;
|
||||
if (leftTocEl) {
|
||||
tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
|
||||
id: "quarto-lefttoc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Find the first element that uses formatting in special columns
|
||||
const conflictingEls = window.document.body.querySelectorAll(
|
||||
'[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
|
||||
);
|
||||
|
||||
// Filter all the possibly conflicting elements into ones
|
||||
// the do conflict on the left or ride side
|
||||
const arrConflictingEls = Array.from(conflictingEls);
|
||||
const leftSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return false;
|
||||
}
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("right") &&
|
||||
!className.endsWith("container") &&
|
||||
className !== "column-margin"
|
||||
);
|
||||
});
|
||||
});
|
||||
const rightSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasMarginCaption = Array.from(el.classList).find((className) => {
|
||||
return className == "margin-caption";
|
||||
});
|
||||
if (hasMarginCaption) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
!className.endsWith("container") &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("left")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const kOverlapPaddingSize = 10;
|
||||
function toRegions(els) {
|
||||
return els.map((el) => {
|
||||
const boundRect = el.getBoundingClientRect();
|
||||
const top =
|
||||
boundRect.top +
|
||||
document.documentElement.scrollTop -
|
||||
kOverlapPaddingSize;
|
||||
return {
|
||||
top,
|
||||
bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let hasObserved = false;
|
||||
const visibleItemObserver = (els) => {
|
||||
let visibleElements = [...els];
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries, _observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (visibleElements.indexOf(entry.target) === -1) {
|
||||
visibleElements.push(entry.target);
|
||||
}
|
||||
} else {
|
||||
visibleElements = visibleElements.filter((visibleEntry) => {
|
||||
return visibleEntry !== entry;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasObserved) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
hasObserved = true;
|
||||
},
|
||||
{}
|
||||
);
|
||||
els.forEach((el) => {
|
||||
intersectionObserver.observe(el);
|
||||
});
|
||||
|
||||
return {
|
||||
getVisibleEntries: () => {
|
||||
return visibleElements;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const rightElementObserver = visibleItemObserver(rightSideConflictEls);
|
||||
const leftElementObserver = visibleItemObserver(leftSideConflictEls);
|
||||
|
||||
const hideOverlappedSidebars = () => {
|
||||
marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
|
||||
sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
|
||||
if (tocLeftScrollVisibility) {
|
||||
tocLeftScrollVisibility(
|
||||
toRegions(leftElementObserver.getVisibleEntries())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.quartoToggleReader = () => {
|
||||
// Applies a slow class (or removes it)
|
||||
// to update the transition speed
|
||||
const slowTransition = (slow) => {
|
||||
const manageTransition = (id, slow) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (slow) {
|
||||
el.classList.add("slow");
|
||||
} else {
|
||||
el.classList.remove("slow");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
manageTransition("TOC", slow);
|
||||
manageTransition("quarto-sidebar", slow);
|
||||
};
|
||||
const readerMode = !isReaderMode();
|
||||
setReaderModeValue(readerMode);
|
||||
|
||||
// If we're entering reader mode, slow the transition
|
||||
if (readerMode) {
|
||||
slowTransition(readerMode);
|
||||
}
|
||||
highlightReaderToggle(readerMode);
|
||||
hideOverlappedSidebars();
|
||||
|
||||
// If we're exiting reader mode, restore the non-slow transition
|
||||
if (!readerMode) {
|
||||
slowTransition(!readerMode);
|
||||
}
|
||||
};
|
||||
|
||||
const highlightReaderToggle = (readerMode) => {
|
||||
const els = document.querySelectorAll(".quarto-reader-toggle");
|
||||
if (els) {
|
||||
els.forEach((el) => {
|
||||
if (readerMode) {
|
||||
el.classList.add("reader");
|
||||
} else {
|
||||
el.classList.remove("reader");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setReaderModeValue = (val) => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
window.localStorage.setItem("quarto-reader-mode", val);
|
||||
} else {
|
||||
localReaderMode = val;
|
||||
}
|
||||
};
|
||||
|
||||
const isReaderMode = () => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
return window.localStorage.getItem("quarto-reader-mode") === "true";
|
||||
} else {
|
||||
return localReaderMode;
|
||||
}
|
||||
};
|
||||
let localReaderMode = null;
|
||||
|
||||
const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
|
||||
const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
|
||||
|
||||
// Walk the TOC and collapse/expand nodes
|
||||
// Nodes are expanded if:
|
||||
// - they are top level
|
||||
// - they have children that are 'active' links
|
||||
// - they are directly below an link that is 'active'
|
||||
const walk = (el, depth) => {
|
||||
// Tick depth when we enter a UL
|
||||
if (el.tagName === "UL") {
|
||||
depth = depth + 1;
|
||||
}
|
||||
|
||||
// It this is active link
|
||||
let isActiveNode = false;
|
||||
if (el.tagName === "A" && el.classList.contains("active")) {
|
||||
isActiveNode = true;
|
||||
}
|
||||
|
||||
// See if there is an active child to this element
|
||||
let hasActiveChild = false;
|
||||
for (const child of el.children) {
|
||||
hasActiveChild = walk(child, depth) || hasActiveChild;
|
||||
}
|
||||
|
||||
// Process the collapse state if this is an UL
|
||||
if (el.tagName === "UL") {
|
||||
if (tocOpenDepth === -1 && depth > 1) {
|
||||
// toc-expand: false
|
||||
el.classList.add("collapse");
|
||||
} else if (
|
||||
depth <= tocOpenDepth ||
|
||||
hasActiveChild ||
|
||||
prevSiblingIsActiveLink(el)
|
||||
) {
|
||||
el.classList.remove("collapse");
|
||||
} else {
|
||||
el.classList.add("collapse");
|
||||
}
|
||||
|
||||
// untick depth when we leave a UL
|
||||
depth = depth - 1;
|
||||
}
|
||||
return hasActiveChild || isActiveNode;
|
||||
};
|
||||
|
||||
// walk the TOC and expand / collapse any items that should be shown
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
|
||||
// Throttle the scroll event and walk peridiocally
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 5)
|
||||
);
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 10)
|
||||
);
|
||||
hideOverlappedSidebars();
|
||||
highlightReaderToggle(isReaderMode());
|
||||
});
|
||||
|
||||
tabsets.init();
|
||||
|
||||
function throttle(func, wait) {
|
||||
let waiting = false;
|
||||
return function () {
|
||||
if (!waiting) {
|
||||
func.apply(this, arguments);
|
||||
waiting = true;
|
||||
setTimeout(function () {
|
||||
waiting = false;
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function nexttick(func) {
|
||||
return setTimeout(func, 0);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// grouped tabsets
|
||||
|
||||
export function init() {
|
||||
window.addEventListener("pageshow", (_event) => {
|
||||
function getTabSettings() {
|
||||
const data = localStorage.getItem("quarto-persistent-tabsets-data");
|
||||
if (!data) {
|
||||
localStorage.setItem("quarto-persistent-tabsets-data", "{}");
|
||||
return {};
|
||||
}
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
function setTabSettings(data) {
|
||||
localStorage.setItem(
|
||||
"quarto-persistent-tabsets-data",
|
||||
JSON.stringify(data)
|
||||
);
|
||||
}
|
||||
|
||||
function setTabState(groupName, groupValue) {
|
||||
const data = getTabSettings();
|
||||
data[groupName] = groupValue;
|
||||
setTabSettings(data);
|
||||
}
|
||||
|
||||
function toggleTab(tab, active) {
|
||||
const tabPanelId = tab.getAttribute("aria-controls");
|
||||
const tabPanel = document.getElementById(tabPanelId);
|
||||
if (active) {
|
||||
tab.classList.add("active");
|
||||
tabPanel.classList.add("active");
|
||||
} else {
|
||||
tab.classList.remove("active");
|
||||
tabPanel.classList.remove("active");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAll(selectedGroup, selectorsToSync) {
|
||||
for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
|
||||
const active = selectedGroup === thisGroup;
|
||||
for (const tab of tabs) {
|
||||
toggleTab(tab, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findSelectorsToSyncByLanguage() {
|
||||
const result = {};
|
||||
const tabs = Array.from(
|
||||
document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
|
||||
);
|
||||
for (const item of tabs) {
|
||||
const div = item.parentElement.parentElement.parentElement;
|
||||
const group = div.getAttribute("data-group");
|
||||
if (!result[group]) {
|
||||
result[group] = {};
|
||||
}
|
||||
const selectorsToSync = result[group];
|
||||
const value = item.innerHTML;
|
||||
if (!selectorsToSync[value]) {
|
||||
selectorsToSync[value] = [];
|
||||
}
|
||||
selectorsToSync[value].push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setupSelectorSync() {
|
||||
const selectorsToSync = findSelectorsToSyncByLanguage();
|
||||
Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
|
||||
Object.entries(tabSetsByValue).forEach(([value, items]) => {
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("click", (_event) => {
|
||||
setTabState(group, value);
|
||||
toggleAll(value, selectorsToSync[group]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return selectorsToSync;
|
||||
}
|
||||
|
||||
const selectorsToSync = setupSelectorSync();
|
||||
for (const [group, selectedName] of Object.entries(getTabSettings())) {
|
||||
const selectors = selectorsToSync[group];
|
||||
// it's possible that stale state gives us empty selections, so we explicitly check here.
|
||||
if (selectors) {
|
||||
toggleAll(selectedName, selectors);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
||||
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+6
File diff suppressed because one or more lines are too long
+12
@@ -0,0 +1,12 @@
|
||||
@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css);
|
||||
|
||||
/*-- scss:rules --*/
|
||||
.table {width:auto}
|
||||
|
||||
code {font-family: 'Fira Code Medium', monospace;}
|
||||
|
||||
.clay-dataset .table {
|
||||
@extend .table-striped;
|
||||
@extend .table-hover;
|
||||
@extend .table-responsive;
|
||||
}
|
||||
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
---
|
||||
title: "Dataset (data frame) manipulation API for the tech.ml.dataset library"
|
||||
output:
|
||||
md_document:
|
||||
variant: gfm
|
||||
---
|
||||
|
||||
```{r setup, include=FALSE}
|
||||
find_nrepl_port_up <- function() {
|
||||
wd <- getwd()
|
||||
while(wd != dirname(wd)) {
|
||||
f <- paste0(wd,"/.nrepl-port")
|
||||
if(file.exists(f)) return(paste0("@",f))
|
||||
wd <- dirname(wd)
|
||||
f <- NULL
|
||||
}
|
||||
}
|
||||
port_file <- find_nrepl_port_up()
|
||||
if(is.null(port_file)) stop("nREPL port not found")
|
||||
library(knitr)
|
||||
knitr_one_string <- knitr:::one_string
|
||||
nrepl_cmd <- "rep"
|
||||
opts_chunk$set(comment=NA, highlight=TRUE)
|
||||
knit_engines$set(clojure = function(options) {
|
||||
rep_params <- if(isTRUE(options$stdout_only)) {
|
||||
"--print 'out,1,%{out}' --print 'value,1,' -p"
|
||||
} else {
|
||||
"-p"
|
||||
}
|
||||
code <- paste(rep_params, port_file, shQuote(knitr_one_string(options$code)))
|
||||
out <- if (options$eval) {
|
||||
if (options$message) message('running: ', nrepl_cmd, ' ', code)
|
||||
tryCatch(
|
||||
system2(nrepl_cmd, code, stdout = TRUE, stderr = TRUE, env = options$engine.env),
|
||||
error = function(e) {
|
||||
if (!options$error) stop(e)
|
||||
paste('Error in running command', nrepl_cmd)
|
||||
}
|
||||
)
|
||||
} else ''
|
||||
if (!options$error && !is.null(attr(out, 'status'))) stop(knitr_one_string(out))
|
||||
engine_output(options, options$code, out)})
|
||||
```
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
[](https://travis-ci.org/github/scicloj/tablecloth)
|
||||
[](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Versions
|
||||
|
||||
### tech.ml.dataset 7.x (master branch)
|
||||
|
||||
[](https://clojars.org/scicloj/tablecloth)
|
||||
|
||||
### tech.ml.dataset 4.x (4.0 branch)
|
||||
|
||||
`[scicloj/tablecloth "4.04"]`
|
||||
|
||||
## Introduction
|
||||
|
||||
[tech.ml.dataset](https://github.com/techascent/tech.ml.dataset) is a great and fast library which brings columnar dataset to the Clojure. Chris Nuernberger has been working on this library for last year as a part of bigger `tech.ml` stack.
|
||||
|
||||
I've started to test the library and help to fix uncovered bugs. My main goal was to compare functionalities with the other standards from other platforms. I focused on R solutions: [dplyr](https://dplyr.tidyverse.org/), [tidyr](https://tidyr.tidyverse.org/) and [data.table](https://rdatatable.gitlab.io/data.table/).
|
||||
|
||||
During conversions of the examples I've come up how to reorganized existing `tech.ml.dataset` functions into simple to use API. The main goals were:
|
||||
|
||||
* Focus on dataset manipulation functionality, leaving other parts of `tech.ml` like pipelines, datatypes, readers, ML, etc.
|
||||
* Single entry point for common operations - one function dispatching on given arguments.
|
||||
* `group-by` results with special kind of dataset - a dataset containing subsets created after grouping as a column.
|
||||
* Most operations recognize regular dataset and grouped dataset and process data accordingly.
|
||||
* One function form to enable thread-first on dataset.
|
||||
|
||||
Important! This library is not the replacement of `tech.ml.dataset` nor a separate library. It should be considered as a addition on the top of `tech.ml.dataset`.
|
||||
|
||||
If you want to know more about `tech.ml.dataset` and `dtype-next` please refer their documentation:
|
||||
|
||||
* [tech.ml.dataset walkthrough](https://techascent.github.io/tech.ml.dataset/walkthrough.html)
|
||||
* [dtype-next overview](https://cnuernber.github.io/dtype-next/overview.html)
|
||||
* [dtype-next cheatsheet](https://cnuernber.github.io/dtype-next/cheatsheet.html)
|
||||
|
||||
Join the discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer [detailed documentation with examples](https://scicloj.github.io/tablecloth/index.html)
|
||||
|
||||
## Usage example
|
||||
|
||||
```{clojure results="hide"}
|
||||
(require '[tablecloth.api :as tc])
|
||||
```
|
||||
|
||||
```{clojure results="asis"}
|
||||
(-> "https://raw.githubusercontent.com/techascent/tech.ml.dataset/master/test/data/stocks.csv"
|
||||
(tc/dataset {:key-fn keyword})
|
||||
(tc/group-by (fn [row]
|
||||
{:symbol (:symbol row)
|
||||
:year (tech.v3.datatype.datetime/long-temporal-field :years (:date row))}))
|
||||
(tc/aggregate #(tech.v3.datatype.functional/mean (% :price)))
|
||||
(tc/order-by [:symbol :year])
|
||||
(tc/head 10))
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
`Tablecloth` is open for contribution. The best way to start is discussion on [Zulip](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api).
|
||||
|
||||
### Development tools for documentation
|
||||
|
||||
Documentation is written in RMarkdown, that means that you need R to create html/md/pdf files.
|
||||
Documentation contains around 600 code snippets which are run during build. There are two files:
|
||||
|
||||
* `README.Rmd`
|
||||
* `docs/index.Rmd`
|
||||
|
||||
Prepare following software:
|
||||
|
||||
1. Install [R](https://www.r-project.org/)
|
||||
2. Install [rep](https://github.com/eraserhd/rep), nRepl client
|
||||
3. Install `pandoc`
|
||||
4. Run nRepl
|
||||
5. Run R and install R packages: `install.packages(c("rmarkdown","knitr"), dependencies=T)`
|
||||
6. Load rmarkdown: `library(rmarkdown)`
|
||||
7. Render readme: `render("README.Rmd","md_document")`
|
||||
8. Render documentation: `render("docs/index.Rmd","all")`
|
||||
|
||||
### API file generation
|
||||
|
||||
`tablecloth.api` namespace is generated out of `api-template`, please run it before making documentation
|
||||
|
||||
```{clojure eval=FALSE}
|
||||
(exporter/write-api! 'tablecloth.api.api-template
|
||||
'tablecloth.api
|
||||
"src/tablecloth/api.clj"
|
||||
'[group-by drop concat rand-nth first last shuffle])
|
||||
```
|
||||
|
||||
### Guideline
|
||||
|
||||
1. Before commiting changes please perform tests. I ususally do: `lein do clean, check, test` and build documentation as described above (which also tests whole library).
|
||||
2. Keep API as simple as possible:
|
||||
- first argument should be a dataset
|
||||
- if parametrizations is complex, last argument should accept a map with not obligatory function arguments
|
||||
- avoid variadic associative destructuring for function arguments
|
||||
- usually function should working on grouped dataset as well, accept `parallel?` argument then (if applied).
|
||||
3. Follow `potemkin` pattern and import functions to the API namespace using `tech.v3.datatype.export-symbols/export-symbols` function
|
||||
4. Functions which are composed out of API function to cover specific case(s) should go to `tablecloth.utils` namespace.
|
||||
5. Always update `README.Rmd`, `CHANGELOG.md`, `docs/index.Rmd`, tests and function docs are highly welcomed
|
||||
6. Always discuss changes and PRs first
|
||||
|
||||
## TODO
|
||||
|
||||
* tests
|
||||
* tutorials
|
||||
|
||||
## New experimental dev workflow
|
||||
|
||||
In this branch, we develop a new proposed dev workflow for Tablecloth:
|
||||
- namespace-as-a-notebook documentation using [Kindly](https://scicloj.github.io/kindly) and [Clay](https://scicloj.github.io/clay)
|
||||
- testing the documentation using [note-to-test](https://github.com/scicloj/note-to-test) - coming soon
|
||||
|
||||
### Relevant files
|
||||
- [notebooks/draft.clj](notebooks/draft.clj) - the tutorial as a Kindly notebook (developed with Clay)
|
||||
- [dev/conversion.clj](dev/conversion.clj) - the script used to generate the notebook from the original `Rmarkdown` tutorial (up to a few additional manual edits)
|
||||
- [docs/draft.html](docs/draft.html) - the tutorial rendered using Clay and [Quarto](https://quarto.org/)
|
||||
|
||||
### Actions
|
||||
- to render the notebook using Clay (assuming you have the Quarto CLI [installed](https://quarto.org/docs/get-started/)):
|
||||
```clj
|
||||
(require '[scicloj.clay.v2.api :as clay])
|
||||
(clay/make! {:format [:quarto :html]
|
||||
:source-path "notebooks/draft.clj"})
|
||||
```
|
||||
|
||||
## Licence
|
||||
|
||||
Copyright (c) 2020 Scicloj
|
||||
|
||||
The MIT Licence
|
||||
Vendored
+5743
File diff suppressed because it is too large
Load Diff
Vendored
+37740
File diff suppressed because one or more lines are too long
Vendored
+12982
File diff suppressed because it is too large
Load Diff
Vendored
BIN
Binary file not shown.
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css);
|
||||
|
||||
/*-- scss:rules --*/
|
||||
.table {width:auto}
|
||||
|
||||
code {font-family: 'Fira Code Medium', monospace;}
|
||||
|
||||
.clay-dataset .table {
|
||||
@extend .table-striped;
|
||||
@extend .table-hover;
|
||||
@extend .table-responsive;
|
||||
}
|
||||
Vendored
+7137
File diff suppressed because it is too large
Load Diff
Vendored
+1711
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
|
||||
// add bootstrap table styles to pandoc tables
|
||||
function bootstrapStylePandocTables2() {
|
||||
$('tr.header').parent('thead').parent('table').addClass('table table-striped table-hover table-condensed table-responsive');
|
||||
}
|
||||
$(document).ready(function () { bootstrapStylePandocTables2(); });
|
||||
|
||||
</script>
|
||||
+716
@@ -0,0 +1,716 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="quarto-1.3.450">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
|
||||
|
||||
<title>column_api</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
||||
div.column{flex: auto; overflow-x: auto;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
ul.task-list li input[type="checkbox"] {
|
||||
width: 0.8em;
|
||||
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* CSS for syntax highlighting */
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
.sourceCode { overflow: visible; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script src="column_api_files/libs/clipboard/clipboard.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/quarto.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/popper.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/tippy.umd.min.js"></script>
|
||||
<script src="column_api_files/libs/quarto-html/anchor.min.js"></script>
|
||||
<link href="column_api_files/libs/quarto-html/tippy.css" rel="stylesheet">
|
||||
<link href="column_api_files/libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
|
||||
<script src="column_api_files/libs/bootstrap/bootstrap.min.js"></script>
|
||||
<link href="column_api_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="column_api_files/libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
|
||||
<link rel="icon" href="data:,">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
|
||||
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
|
||||
<nav id="TOC" role="doc-toc" class="toc-active">
|
||||
<h2 id="toc-title">Table of contents</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="#column-api" id="toc-column-api" class="nav-link active" data-scroll-target="#column-api">Column API</a>
|
||||
<ul>
|
||||
<li><a href="#column-creation" id="toc-column-creation" class="nav-link" data-scroll-target="#column-creation">Column Creation</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#ones-zeros" id="toc-ones-zeros" class="nav-link" data-scroll-target="#ones-zeros">Ones & Zeros</a></li>
|
||||
<li><a href="#column" id="toc-column" class="nav-link" data-scroll-target="#column">Column?</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#types-and-type-detection" id="toc-types-and-type-detection" class="nav-link" data-scroll-target="#types-and-type-detection">Types and Type detection</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#typeof-typeof" id="toc-typeof-typeof" class="nav-link" data-scroll-target="#typeof-typeof">Typeof & Typeof?</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#column-access-manipulation" id="toc-column-access-manipulation" class="nav-link" data-scroll-target="#column-access-manipulation">Column Access & Manipulation</a>
|
||||
<ul class="collapse">
|
||||
<li><a href="#column-access" id="toc-column-access" class="nav-link" data-scroll-target="#column-access">Column Access</a></li>
|
||||
<li><a href="#slice" id="toc-slice" class="nav-link" data-scroll-target="#slice">Slice</a></li>
|
||||
<li><a href="#select" id="toc-select" class="nav-link" data-scroll-target="#select">Select</a></li>
|
||||
<li><a href="#sort" id="toc-sort" class="nav-link" data-scroll-target="#sort">Sort</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#column-operations" id="toc-column-operations" class="nav-link" data-scroll-target="#column-operations">Column Operations</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<main class="content" id="quarto-document-content">
|
||||
|
||||
|
||||
|
||||
|
||||
<style>table {
|
||||
border-style: thin;
|
||||
}
|
||||
th, td {
|
||||
padding: 6px;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
text-align: center;
|
||||
background-color: #ddd;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
<style>.printedClojure .sourceCode {
|
||||
background-color: transparent;
|
||||
border-style: none;
|
||||
}
|
||||
</style>
|
||||
<script src="column_api_files/md-default0.js" type="text/javascript"></script>
|
||||
<script src="column_api_files/md-default1.js" type="text/javascript"></script>
|
||||
<section id="column-api" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="column-api">Column API</h2>
|
||||
<p>A <code>column</code> in tablecloth is a named sequence of typed data. This special type is defined in the <code>tech.ml.dataset</code>. It is roughly comparable to a R vector.</p>
|
||||
<section id="column-creation" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-creation">Column Creation</h3>
|
||||
<p>Empty column</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>(tcc/column)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[0]</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>[]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Column from a vector or a sequence</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>(tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>(tcc/column `(<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<section id="ones-zeros" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="ones-zeros">Ones & Zeros</h4>
|
||||
<p>You can also quickly create columns of ones or zeros:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>(tcc/ones <span class="dv">10</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[10]</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>(tcc/zeros <span class="dv">10</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[10]</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="column" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="column">Column?</h4>
|
||||
<p>Finally, you can use the <code>column?</code> function to check if an item is a column:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="va">false</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? (tcc/column))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Tablecloth’s datasets of course consists of columns:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>(tcc/column? (<span class="kw">-></span> (tc/dataset {<span class="at">:a</span> [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>]})</span>
|
||||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:a</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="types-and-type-detection" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="types-and-type-detection">Types and Type detection</h3>
|
||||
<p>The default set of types for a column are defined in the underlying “tech ml” system. We can see the set here:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb17"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>(tech.v3.datatype.casting/all-datatypes)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb18"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>(<span class="at">:int32</span></span>
|
||||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:int16</span></span>
|
||||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:float32</span></span>
|
||||
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:float64</span></span>
|
||||
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:int64</span></span>
|
||||
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint64</span></span>
|
||||
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:string</span></span>
|
||||
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint16</span></span>
|
||||
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:int8</span></span>
|
||||
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint32</span></span>
|
||||
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:keyword</span></span>
|
||||
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:decimal</span></span>
|
||||
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:uuid</span></span>
|
||||
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:boolean</span></span>
|
||||
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:object</span></span>
|
||||
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:char</span></span>
|
||||
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a> <span class="at">:uint8</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<section id="typeof-typeof" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="typeof-typeof">Typeof & Typeof?</h4>
|
||||
<p>When you create a column, the underlying system will try to autodetect its type. We can see that here using the <code>tcc/typeof</code> function to check the type of a column:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb19"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb20"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="at">:int64</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb21"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:a</span> <span class="at">:b</span> <span class="at">:c</span> <span class="at">:d</span> <span class="at">:e</span>])</span>
|
||||
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb22"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="at">:keyword</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Columns containing heterogenous data will receive type <code>:object</code>:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb23"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="at">:b</span> <span class="dv">3</span> <span class="at">:c</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb24"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="at">:object</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can also use the <code>tcc/typeof?</code> function to check the value of a function as an asssertion:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb25"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:boolean</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb26"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="va">false</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb27"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:int64</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb28"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Tablecloth has a concept of “concrete” and “general” types. A general type is the broad category of type and the concrete type is the actual type in memory. For example, a concrete type is a 64-bit integer <code>:int64</code>, which is also of the general type <code>:integer</code>. The <code>typeof?</code> function supports checking both.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb29"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:int64</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb30"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb31"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">6</span>])</span>
|
||||
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> (tcc/typeof? <span class="at">:integer</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb32"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="va">true</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="column-access-manipulation" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-access-manipulation">Column Access & Manipulation</h3>
|
||||
<section id="column-access" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="column-access">Column Access</h4>
|
||||
<p>The method for accessing a particular index position in a column is the same as for Clojure vectors:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb33"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">get</span> <span class="dv">3</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb34"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="dv">4</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb35"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="dv">1</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">4</span> <span class="dv">5</span>])</span>
|
||||
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">nth</span> <span class="dv">3</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb36"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="dv">4</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="slice" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="slice">Slice</h4>
|
||||
<p>You can also slice a column</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb37"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">5</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb38"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">5</span>, <span class="dv">6</span>, <span class="dv">7</span>, <span class="dv">8</span>, <span class="dv">9</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb39"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">1</span> <span class="dv">4</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb40"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[4]</span></span>
|
||||
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb41"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="dv">0</span> <span class="dv">9</span> <span class="dv">2</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb42"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>, <span class="dv">4</span>, <span class="dv">6</span>, <span class="dv">8</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>For clarity, the <code>slice</code> method supports the <code>:end</code> and <code>:start</code> keywords:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb43"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a> (tcc/slice <span class="at">:start</span> <span class="at">:end</span> <span class="dv">2</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb44"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[5]</span></span>
|
||||
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>, <span class="dv">4</span>, <span class="dv">6</span>, <span class="dv">8</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>If you need to create a discontinuous subset of the column, you can use the <code>select</code> function. This method accepts an array of index positions or an array of booleans. When using boolean select, a true value will select the value at the index positions containing true values:</p>
|
||||
</section>
|
||||
<section id="select" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="select">Select</h4>
|
||||
<p>Select the values at index positions 1 and 9:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb45"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> (tcc/select [<span class="dv">1</span> <span class="dv">9</span>]))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb46"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[2]</span></span>
|
||||
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">1</span>, <span class="dv">9</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>Select the values at index positions 0 and 2 using booelan select:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb47"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column (<span class="kw">range</span> <span class="dv">10</span>))</span>
|
||||
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a> (tcc/select (tcc/column [<span class="va">true</span> <span class="va">false</span> <span class="va">true</span>])))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb48"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[2]</span></span>
|
||||
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">0</span>, <span class="dv">2</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="sort" class="level4">
|
||||
<h4 class="anchored" data-anchor-id="sort">Sort</h4>
|
||||
<p>Use <code>sort-column</code> to sort a column: Default sort is in ascending order:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb49"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:c</span> <span class="at">:z</span> <span class="at">:a</span> <span class="at">:f</span>])</span>
|
||||
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb50"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;keyword&gt;[4]</span></span>
|
||||
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>[<span class="at">:a</span>, <span class="at">:c</span>, <span class="at">:f</span>, <span class="at">:z</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can provide the <code>:desc</code> and <code>:asc</code> keywords to change the default behavior:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb51"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [<span class="at">:c</span> <span class="at">:z</span> <span class="at">:a</span> <span class="at">:f</span>])</span>
|
||||
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column <span class="at">:desc</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb52"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;keyword&gt;[4]</span></span>
|
||||
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>[<span class="at">:z</span>, <span class="at">:f</span>, <span class="at">:c</span>, <span class="at">:a</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>You can also provide a comparator fn:</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb53"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> (tcc/column [{<span class="at">:position</span> <span class="dv">2</span></span>
|
||||
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:text</span> <span class="st">"and then stopped"</span>}</span>
|
||||
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:position</span> <span class="dv">1</span></span>
|
||||
<span id="cb53-4"><a href="#cb53-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:text</span> <span class="st">"I ran fast"</span>}])</span>
|
||||
<span id="cb53-5"><a href="#cb53-5" aria-hidden="true" tabindex="-1"></a> (tcc/sort-column (<span class="kw">fn</span> [a b] (<span class="kw"><</span> (<span class="at">:position</span> a) (<span class="at">:position</span> b)))))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb54"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;persistent-map&gt;[2]</span></span>
|
||||
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a>[{<span class="at">:position</span> <span class="dv">1</span>, <span class="at">:text</span> <span class="st">"I ran fast"</span>}, {<span class="at">:position</span> <span class="dv">2</span>, <span class="at">:text</span> <span class="st">"and then stopped"</span>}]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="column-operations" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="column-operations">Column Operations</h3>
|
||||
<p>The Column API contains a large number of operations. These operations all take one or more columns as an argument, and they return either a scalar value or a new column, depending on the operations. These operations all take a column as the first argument so they are easy to use with the pipe <code>-></code> macro, as with all functions in Tablecloth.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb55"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> a </span>(tcc/column [<span class="dv">20</span> <span class="dv">30</span> <span class="dv">40</span> <span class="dv">50</span>]))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb56"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> b </span>(tcc/column (<span class="kw">range</span> <span class="dv">4</span>)))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb57"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a>(tcc/- a b)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb58"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;int64&gt;[4]</span></span>
|
||||
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a>[<span class="dv">20</span>, <span class="dv">29</span>, <span class="dv">38</span>, <span class="dv">47</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb59"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>(tcc/pow a <span class="dv">2</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb60"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;float64&gt;[4]</span></span>
|
||||
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>[<span class="fl">400.0</span>, <span class="fl">900.0</span>, <span class="dv">1600</span>, <span class="dv">2500</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb61"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>(tcc/* <span class="dv">10</span> (tcc/sin a))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb62"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;float64&gt;[4]</span></span>
|
||||
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>[<span class="fl">9.129</span>, -<span class="fl">9.880</span>, <span class="fl">7.451</span>, -<span class="fl">2.624</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb63"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>(tcc/< a <span class="dv">35</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb64"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[4]</span></span>
|
||||
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a>[<span class="va">true</span>, <span class="va">true</span>, <span class="va">false</span>, <span class="va">false</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>All these operations take a column as their first argument and return a column, so they can be chained easily.</p>
|
||||
<div class="sourceClojure">
|
||||
<div class="sourceCode" id="cb65"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">-></span> a</span>
|
||||
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a> (tcc/* b)</span>
|
||||
<span id="cb65-3"><a href="#cb65-3" aria-hidden="true" tabindex="-1"></a> (tcc/< <span class="dv">70</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="printedClojure">
|
||||
<div class="sourceCode" id="cb66"><pre class="sourceCode clojure code-with-copy"><code class="sourceCode clojure"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a>#tech.v3.dataset.column&lt<span class="co">;boolean&gt;[4]</span></span>
|
||||
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a>null</span>
|
||||
<span id="cb66-3"><a href="#cb66-3" aria-hidden="true" tabindex="-1"></a>[<span class="va">true</span>, <span class="va">true</span>, <span class="va">false</span>, <span class="va">false</span>]</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div style="background-color:grey;height:2px;width:100%;">
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<pre><small><small>source: <a href="https://github.com/scicloj/tablecloth/blob/master/notebooks/column_api.clj">notebooks/column_api.clj</a></small></small></pre>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<!-- /main column -->
|
||||
<script id="quarto-html-after-body" type="application/javascript">
|
||||
window.document.addEventListener("DOMContentLoaded", function (event) {
|
||||
const toggleBodyColorMode = (bsSheetEl) => {
|
||||
const mode = bsSheetEl.getAttribute("data-mode");
|
||||
const bodyEl = window.document.querySelector("body");
|
||||
if (mode === "dark") {
|
||||
bodyEl.classList.add("quarto-dark");
|
||||
bodyEl.classList.remove("quarto-light");
|
||||
} else {
|
||||
bodyEl.classList.add("quarto-light");
|
||||
bodyEl.classList.remove("quarto-dark");
|
||||
}
|
||||
}
|
||||
const toggleBodyColorPrimary = () => {
|
||||
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
|
||||
if (bsSheetEl) {
|
||||
toggleBodyColorMode(bsSheetEl);
|
||||
}
|
||||
}
|
||||
toggleBodyColorPrimary();
|
||||
const icon = "";
|
||||
const anchorJS = new window.AnchorJS();
|
||||
anchorJS.options = {
|
||||
placement: 'right',
|
||||
icon: icon
|
||||
};
|
||||
anchorJS.add('.anchored');
|
||||
const isCodeAnnotation = (el) => {
|
||||
for (const clz of el.classList) {
|
||||
if (clz.startsWith('code-annotation-')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const clipboard = new window.ClipboardJS('.code-copy-button', {
|
||||
text: function(trigger) {
|
||||
const codeEl = trigger.previousElementSibling.cloneNode(true);
|
||||
for (const childEl of codeEl.children) {
|
||||
if (isCodeAnnotation(childEl)) {
|
||||
childEl.remove();
|
||||
}
|
||||
}
|
||||
return codeEl.innerText;
|
||||
}
|
||||
});
|
||||
clipboard.on('success', function(e) {
|
||||
// button target
|
||||
const button = e.trigger;
|
||||
// don't keep focus
|
||||
button.blur();
|
||||
// flash "checked"
|
||||
button.classList.add('code-copy-button-checked');
|
||||
var currentTitle = button.getAttribute("title");
|
||||
button.setAttribute("title", "Copied!");
|
||||
let tooltip;
|
||||
if (window.bootstrap) {
|
||||
button.setAttribute("data-bs-toggle", "tooltip");
|
||||
button.setAttribute("data-bs-placement", "left");
|
||||
button.setAttribute("data-bs-title", "Copied!");
|
||||
tooltip = new bootstrap.Tooltip(button,
|
||||
{ trigger: "manual",
|
||||
customClass: "code-copy-button-tooltip",
|
||||
offset: [0, -8]});
|
||||
tooltip.show();
|
||||
}
|
||||
setTimeout(function() {
|
||||
if (tooltip) {
|
||||
tooltip.hide();
|
||||
button.removeAttribute("data-bs-title");
|
||||
button.removeAttribute("data-bs-toggle");
|
||||
button.removeAttribute("data-bs-placement");
|
||||
}
|
||||
button.setAttribute("title", currentTitle);
|
||||
button.classList.remove('code-copy-button-checked');
|
||||
}, 1000);
|
||||
// clear code selection
|
||||
e.clearSelection();
|
||||
});
|
||||
function tippyHover(el, contentFn) {
|
||||
const config = {
|
||||
allowHTML: true,
|
||||
content: contentFn,
|
||||
maxWidth: 500,
|
||||
delay: 100,
|
||||
arrow: false,
|
||||
appendTo: function(el) {
|
||||
return el.parentElement;
|
||||
},
|
||||
interactive: true,
|
||||
interactiveBorder: 10,
|
||||
theme: 'quarto',
|
||||
placement: 'bottom-start'
|
||||
};
|
||||
window.tippy(el, config);
|
||||
}
|
||||
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
|
||||
for (var i=0; i<noterefs.length; i++) {
|
||||
const ref = noterefs[i];
|
||||
tippyHover(ref, function() {
|
||||
// use id or data attribute instead here
|
||||
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
|
||||
try { href = new URL(href).hash; } catch {}
|
||||
const id = href.replace(/^#\/?/, "");
|
||||
const note = window.document.getElementById(id);
|
||||
return note.innerHTML;
|
||||
});
|
||||
}
|
||||
let selectedAnnoteEl;
|
||||
const selectorForAnnotation = ( cell, annotation) => {
|
||||
let cellAttr = 'data-code-cell="' + cell + '"';
|
||||
let lineAttr = 'data-code-annotation="' + annotation + '"';
|
||||
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
|
||||
return selector;
|
||||
}
|
||||
const selectCodeLines = (annoteEl) => {
|
||||
const doc = window.document;
|
||||
const targetCell = annoteEl.getAttribute("data-target-cell");
|
||||
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
|
||||
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
|
||||
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
|
||||
const lineIds = lines.map((line) => {
|
||||
return targetCell + "-" + line;
|
||||
})
|
||||
let top = null;
|
||||
let height = null;
|
||||
let parent = null;
|
||||
if (lineIds.length > 0) {
|
||||
//compute the position of the single el (top and bottom and make a div)
|
||||
const el = window.document.getElementById(lineIds[0]);
|
||||
top = el.offsetTop;
|
||||
height = el.offsetHeight;
|
||||
parent = el.parentElement.parentElement;
|
||||
if (lineIds.length > 1) {
|
||||
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
|
||||
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
|
||||
height = bottom - top;
|
||||
}
|
||||
if (top !== null && height !== null && parent !== null) {
|
||||
// cook up a div (if necessary) and position it
|
||||
let div = window.document.getElementById("code-annotation-line-highlight");
|
||||
if (div === null) {
|
||||
div = window.document.createElement("div");
|
||||
div.setAttribute("id", "code-annotation-line-highlight");
|
||||
div.style.position = 'absolute';
|
||||
parent.appendChild(div);
|
||||
}
|
||||
div.style.top = top - 2 + "px";
|
||||
div.style.height = height + 4 + "px";
|
||||
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
|
||||
if (gutterDiv === null) {
|
||||
gutterDiv = window.document.createElement("div");
|
||||
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
|
||||
gutterDiv.style.position = 'absolute';
|
||||
const codeCell = window.document.getElementById(targetCell);
|
||||
const gutter = codeCell.querySelector('.code-annotation-gutter');
|
||||
gutter.appendChild(gutterDiv);
|
||||
}
|
||||
gutterDiv.style.top = top - 2 + "px";
|
||||
gutterDiv.style.height = height + 4 + "px";
|
||||
}
|
||||
selectedAnnoteEl = annoteEl;
|
||||
}
|
||||
};
|
||||
const unselectCodeLines = () => {
|
||||
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
|
||||
elementsIds.forEach((elId) => {
|
||||
const div = window.document.getElementById(elId);
|
||||
if (div) {
|
||||
div.remove();
|
||||
}
|
||||
});
|
||||
selectedAnnoteEl = undefined;
|
||||
};
|
||||
// Attach click handler to the DT
|
||||
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
|
||||
for (const annoteDlNode of annoteDls) {
|
||||
annoteDlNode.addEventListener('click', (event) => {
|
||||
const clickedEl = event.target;
|
||||
if (clickedEl !== selectedAnnoteEl) {
|
||||
unselectCodeLines();
|
||||
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
|
||||
if (activeEl) {
|
||||
activeEl.classList.remove('code-annotation-active');
|
||||
}
|
||||
selectCodeLines(clickedEl);
|
||||
clickedEl.classList.add('code-annotation-active');
|
||||
} else {
|
||||
// Unselect the line
|
||||
unselectCodeLines();
|
||||
clickedEl.classList.remove('code-annotation-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
const findCites = (el) => {
|
||||
const parentEl = el.parentElement;
|
||||
if (parentEl) {
|
||||
const cites = parentEl.dataset.cites;
|
||||
if (cites) {
|
||||
return {
|
||||
el,
|
||||
cites: cites.split(' ')
|
||||
};
|
||||
} else {
|
||||
return findCites(el.parentElement)
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
|
||||
for (var i=0; i<bibliorefs.length; i++) {
|
||||
const ref = bibliorefs[i];
|
||||
const citeInfo = findCites(ref);
|
||||
if (citeInfo) {
|
||||
tippyHover(citeInfo.el, function() {
|
||||
var popup = window.document.createElement('div');
|
||||
citeInfo.cites.forEach(function(cite) {
|
||||
var citeDiv = window.document.createElement('div');
|
||||
citeDiv.classList.add('hanging-indent');
|
||||
citeDiv.classList.add('csl-entry');
|
||||
var biblioDiv = window.document.getElementById('ref-' + cite);
|
||||
if (biblioDiv) {
|
||||
citeDiv.innerHTML = biblioDiv.innerHTML;
|
||||
}
|
||||
popup.appendChild(citeDiv);
|
||||
});
|
||||
return popup.innerHTML;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div> <!-- /content -->
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2018
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+10
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-kw-color: #859900;
|
||||
--quarto-hl-fu-color: #268bd2;
|
||||
--quarto-hl-va-color: #268bd2;
|
||||
--quarto-hl-cf-color: #859900;
|
||||
--quarto-hl-op-color: #859900;
|
||||
--quarto-hl-bu-color: #cb4b16;
|
||||
--quarto-hl-ex-color: #268bd2;
|
||||
--quarto-hl-pp-color: #cb4b16;
|
||||
--quarto-hl-at-color: #268bd2;
|
||||
--quarto-hl-ch-color: #2aa198;
|
||||
--quarto-hl-sc-color: #dc322f;
|
||||
--quarto-hl-st-color: #2aa198;
|
||||
--quarto-hl-vs-color: #2aa198;
|
||||
--quarto-hl-ss-color: #dc322f;
|
||||
--quarto-hl-im-color: #2aa198;
|
||||
--quarto-hl-dt-color: #b58900;
|
||||
--quarto-hl-dv-color: #2aa198;
|
||||
--quarto-hl-bn-color: #2aa198;
|
||||
--quarto-hl-fl-color: #2aa198;
|
||||
--quarto-hl-cn-color: #2aa198;
|
||||
--quarto-hl-co-color: #93a1a1;
|
||||
--quarto-hl-do-color: #dc322f;
|
||||
--quarto-hl-an-color: #268bd2;
|
||||
--quarto-hl-cv-color: #2aa198;
|
||||
--quarto-hl-re-color: #268bd2;
|
||||
--quarto-hl-in-color: #b58900;
|
||||
--quarto-hl-wa-color: #cb4b16;
|
||||
--quarto-hl-al-color: #d33682;
|
||||
--quarto-hl-er-color: #dc322f;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #657b83;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #859900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
code span.bu {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.ex {
|
||||
color: #268bd2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #b58900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #2aa198;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #93a1a1;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
code span.re {
|
||||
color: #268bd2;
|
||||
background-color: #eee8d5;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #d33682;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #dc322f;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
|
||||
@@ -0,0 +1,902 @@
|
||||
const sectionChanged = new CustomEvent("quarto-sectionChanged", {
|
||||
detail: {},
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
});
|
||||
|
||||
const layoutMarginEls = () => {
|
||||
// Find any conflicting margin elements and add margins to the
|
||||
// top to prevent overlap
|
||||
const marginChildren = window.document.querySelectorAll(
|
||||
".column-margin.column-container > * "
|
||||
);
|
||||
|
||||
let lastBottom = 0;
|
||||
for (const marginChild of marginChildren) {
|
||||
if (marginChild.offsetParent !== null) {
|
||||
// clear the top margin so we recompute it
|
||||
marginChild.style.marginTop = null;
|
||||
const top = marginChild.getBoundingClientRect().top + window.scrollY;
|
||||
console.log({
|
||||
childtop: marginChild.getBoundingClientRect().top,
|
||||
scroll: window.scrollY,
|
||||
top,
|
||||
lastBottom,
|
||||
});
|
||||
if (top < lastBottom) {
|
||||
const margin = lastBottom - top;
|
||||
marginChild.style.marginTop = `${margin}px`;
|
||||
}
|
||||
const styles = window.getComputedStyle(marginChild);
|
||||
const marginTop = parseFloat(styles["marginTop"]);
|
||||
|
||||
console.log({
|
||||
top,
|
||||
height: marginChild.getBoundingClientRect().height,
|
||||
marginTop,
|
||||
total: top + marginChild.getBoundingClientRect().height + marginTop,
|
||||
});
|
||||
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.document.addEventListener("DOMContentLoaded", function (_event) {
|
||||
// Recompute the position of margin elements anytime the body size changes
|
||||
if (window.ResizeObserver) {
|
||||
const resizeObserver = new window.ResizeObserver(
|
||||
throttle(layoutMarginEls, 50)
|
||||
);
|
||||
resizeObserver.observe(window.document.body);
|
||||
}
|
||||
|
||||
const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
|
||||
const sidebarEl = window.document.getElementById("quarto-sidebar");
|
||||
const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
|
||||
const marginSidebarEl = window.document.getElementById(
|
||||
"quarto-margin-sidebar"
|
||||
);
|
||||
// function to determine whether the element has a previous sibling that is active
|
||||
const prevSiblingIsActiveLink = (el) => {
|
||||
const sibling = el.previousElementSibling;
|
||||
if (sibling && sibling.tagName === "A") {
|
||||
return sibling.classList.contains("active");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
|
||||
function fireSlideEnter(e) {
|
||||
const event = window.document.createEvent("Event");
|
||||
event.initEvent("slideenter", true, true);
|
||||
window.document.dispatchEvent(event);
|
||||
}
|
||||
const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("shown.bs.tab", fireSlideEnter);
|
||||
});
|
||||
|
||||
// fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
|
||||
document.addEventListener("tabby", fireSlideEnter, false);
|
||||
|
||||
// Track scrolling and mark TOC links as active
|
||||
// get table of contents and sidebar (bail if we don't have at least one)
|
||||
const tocLinks = tocEl
|
||||
? [...tocEl.querySelectorAll("a[data-scroll-target]")]
|
||||
: [];
|
||||
const makeActive = (link) => tocLinks[link].classList.add("active");
|
||||
const removeActive = (link) => tocLinks[link].classList.remove("active");
|
||||
const removeAllActive = () =>
|
||||
[...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
|
||||
|
||||
// activate the anchor for a section associated with this TOC entry
|
||||
tocLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
if (link.href.indexOf("#") !== -1) {
|
||||
const anchor = link.href.split("#")[1];
|
||||
const heading = window.document.querySelector(
|
||||
`[data-anchor-id=${anchor}]`
|
||||
);
|
||||
if (heading) {
|
||||
// Add the class
|
||||
heading.classList.add("reveal-anchorjs-link");
|
||||
|
||||
// function to show the anchor
|
||||
const handleMouseout = () => {
|
||||
heading.classList.remove("reveal-anchorjs-link");
|
||||
heading.removeEventListener("mouseout", handleMouseout);
|
||||
};
|
||||
|
||||
// add a function to clear the anchor when the user mouses out of it
|
||||
heading.addEventListener("mouseout", handleMouseout);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sections = tocLinks.map((link) => {
|
||||
const target = link.getAttribute("data-scroll-target");
|
||||
if (target.startsWith("#")) {
|
||||
return window.document.getElementById(decodeURI(`${target.slice(1)}`));
|
||||
} else {
|
||||
return window.document.querySelector(decodeURI(`${target}`));
|
||||
}
|
||||
});
|
||||
|
||||
const sectionMargin = 200;
|
||||
let currentActive = 0;
|
||||
// track whether we've initialized state the first time
|
||||
let init = false;
|
||||
|
||||
const updateActiveLink = () => {
|
||||
// The index from bottom to top (e.g. reversed list)
|
||||
let sectionIndex = -1;
|
||||
if (
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
window.document.body.offsetHeight
|
||||
) {
|
||||
sectionIndex = 0;
|
||||
} else {
|
||||
sectionIndex = [...sections].reverse().findIndex((section) => {
|
||||
if (section) {
|
||||
return window.pageYOffset >= section.offsetTop - sectionMargin;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sectionIndex > -1) {
|
||||
const current = sections.length - sectionIndex - 1;
|
||||
if (current !== currentActive) {
|
||||
removeAllActive();
|
||||
currentActive = current;
|
||||
makeActive(current);
|
||||
if (init) {
|
||||
window.dispatchEvent(sectionChanged);
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const inHiddenRegion = (top, bottom, hiddenRegions) => {
|
||||
for (const region of hiddenRegions) {
|
||||
if (top <= region.bottom && bottom >= region.top) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const categorySelector = "header.quarto-title-block .quarto-category";
|
||||
const activateCategories = (href) => {
|
||||
// Find any categories
|
||||
// Surround them with a link pointing back to:
|
||||
// #category=Authoring
|
||||
try {
|
||||
const categoryEls = window.document.querySelectorAll(categorySelector);
|
||||
for (const categoryEl of categoryEls) {
|
||||
const categoryText = categoryEl.textContent;
|
||||
if (categoryText) {
|
||||
const link = `${href}#category=${encodeURIComponent(categoryText)}`;
|
||||
const linkEl = window.document.createElement("a");
|
||||
linkEl.setAttribute("href", link);
|
||||
for (const child of categoryEl.childNodes) {
|
||||
linkEl.append(child);
|
||||
}
|
||||
categoryEl.appendChild(linkEl);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
};
|
||||
function hasTitleCategories() {
|
||||
return window.document.querySelector(categorySelector) !== null;
|
||||
}
|
||||
|
||||
function offsetRelativeUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
return offset ? offset + url : url;
|
||||
}
|
||||
|
||||
function offsetAbsoluteUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
const baseUrl = new URL(offset, window.location);
|
||||
|
||||
const projRelativeUrl = url.replace(baseUrl, "");
|
||||
if (projRelativeUrl.startsWith("/")) {
|
||||
return projRelativeUrl;
|
||||
} else {
|
||||
return "/" + projRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// read a meta tag value
|
||||
function getMeta(metaName) {
|
||||
const metas = window.document.getElementsByTagName("meta");
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
if (metas[i].getAttribute("name") === metaName) {
|
||||
return metas[i].getAttribute("content");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
async function findAndActivateCategories() {
|
||||
const currentPagePath = offsetAbsoluteUrl(window.location.href);
|
||||
const response = await fetch(offsetRelativeUrl("listings.json"));
|
||||
if (response.status == 200) {
|
||||
return response.json().then(function (listingPaths) {
|
||||
const listingHrefs = [];
|
||||
for (const listingPath of listingPaths) {
|
||||
const pathWithoutLeadingSlash = listingPath.listing.substring(1);
|
||||
for (const item of listingPath.items) {
|
||||
if (
|
||||
item === currentPagePath ||
|
||||
item === currentPagePath + "index.html"
|
||||
) {
|
||||
// Resolve this path against the offset to be sure
|
||||
// we already are using the correct path to the listing
|
||||
// (this adjusts the listing urls to be rooted against
|
||||
// whatever root the page is actually running against)
|
||||
const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
|
||||
const baseUrl = window.location;
|
||||
const resolvedPath = new URL(relative, baseUrl);
|
||||
listingHrefs.push(resolvedPath.pathname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const nearestListing = findNearestParentListing(
|
||||
offsetAbsoluteUrl(window.location.pathname),
|
||||
listingHrefs
|
||||
);
|
||||
if (nearestListing) {
|
||||
activateCategories(nearestListing);
|
||||
} else {
|
||||
// See if the referrer is a listing page for this item
|
||||
const referredRelativePath = offsetAbsoluteUrl(document.referrer);
|
||||
const referrerListing = listingHrefs.find((listingHref) => {
|
||||
const isListingReferrer =
|
||||
listingHref === referredRelativePath ||
|
||||
listingHref === referredRelativePath + "index.html";
|
||||
return isListingReferrer;
|
||||
});
|
||||
|
||||
if (referrerListing) {
|
||||
// Try to use the referrer if possible
|
||||
activateCategories(referrerListing);
|
||||
} else if (listingHrefs.length > 0) {
|
||||
// Otherwise, just fall back to the first listing
|
||||
activateCategories(listingHrefs[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (hasTitleCategories()) {
|
||||
findAndActivateCategories();
|
||||
}
|
||||
|
||||
const findNearestParentListing = (href, listingHrefs) => {
|
||||
if (!href || !listingHrefs) {
|
||||
return undefined;
|
||||
}
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const relativeParts = href.substring(1).split("/");
|
||||
while (relativeParts.length > 0) {
|
||||
const path = relativeParts.join("/");
|
||||
for (const listingHref of listingHrefs) {
|
||||
if (listingHref.startsWith(path)) {
|
||||
return listingHref;
|
||||
}
|
||||
}
|
||||
relativeParts.pop();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const manageSidebarVisiblity = (el, placeholderDescriptor) => {
|
||||
let isVisible = true;
|
||||
let elRect;
|
||||
|
||||
return (hiddenRegions) => {
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the last element of the TOC
|
||||
const lastChildEl = el.lastElementChild;
|
||||
|
||||
if (lastChildEl) {
|
||||
// Converts the sidebar to a menu
|
||||
const convertToMenu = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 0;
|
||||
child.style.overflow = "hidden";
|
||||
}
|
||||
|
||||
nexttick(() => {
|
||||
const toggleContainer = window.document.createElement("div");
|
||||
toggleContainer.style.width = "100%";
|
||||
toggleContainer.classList.add("zindex-over-content");
|
||||
toggleContainer.classList.add("quarto-sidebar-toggle");
|
||||
toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
|
||||
toggleContainer.id = placeholderDescriptor.id;
|
||||
toggleContainer.style.position = "fixed";
|
||||
|
||||
const toggleIcon = window.document.createElement("i");
|
||||
toggleIcon.classList.add("quarto-sidebar-toggle-icon");
|
||||
toggleIcon.classList.add("bi");
|
||||
toggleIcon.classList.add("bi-caret-down-fill");
|
||||
|
||||
const toggleTitle = window.document.createElement("div");
|
||||
const titleEl = window.document.body.querySelector(
|
||||
placeholderDescriptor.titleSelector
|
||||
);
|
||||
if (titleEl) {
|
||||
toggleTitle.append(
|
||||
titleEl.textContent || titleEl.innerText,
|
||||
toggleIcon
|
||||
);
|
||||
}
|
||||
toggleTitle.classList.add("zindex-over-content");
|
||||
toggleTitle.classList.add("quarto-sidebar-toggle-title");
|
||||
toggleContainer.append(toggleTitle);
|
||||
|
||||
const toggleContents = window.document.createElement("div");
|
||||
toggleContents.classList = el.classList;
|
||||
toggleContents.classList.add("zindex-over-content");
|
||||
toggleContents.classList.add("quarto-sidebar-toggle-contents");
|
||||
for (const child of el.children) {
|
||||
if (child.id === "toc-title") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clone = child.cloneNode(true);
|
||||
clone.style.opacity = 1;
|
||||
clone.style.display = null;
|
||||
toggleContents.append(clone);
|
||||
}
|
||||
toggleContents.style.height = "0px";
|
||||
const positionToggle = () => {
|
||||
// position the element (top left of parent, same width as parent)
|
||||
if (!elRect) {
|
||||
elRect = el.getBoundingClientRect();
|
||||
}
|
||||
toggleContainer.style.left = `${elRect.left}px`;
|
||||
toggleContainer.style.top = `${elRect.top}px`;
|
||||
toggleContainer.style.width = `${elRect.width}px`;
|
||||
};
|
||||
positionToggle();
|
||||
|
||||
toggleContainer.append(toggleContents);
|
||||
el.parentElement.prepend(toggleContainer);
|
||||
|
||||
// Process clicks
|
||||
let tocShowing = false;
|
||||
// Allow the caller to control whether this is dismissed
|
||||
// when it is clicked (e.g. sidebar navigation supports
|
||||
// opening and closing the nav tree, so don't dismiss on click)
|
||||
const clickEl = placeholderDescriptor.dismissOnClick
|
||||
? toggleContainer
|
||||
: toggleTitle;
|
||||
|
||||
const closeToggle = () => {
|
||||
if (tocShowing) {
|
||||
toggleContainer.classList.remove("expanded");
|
||||
toggleContents.style.height = "0px";
|
||||
tocShowing = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get rid of any expanded toggle if the user scrolls
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
closeToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
// Handle positioning of the toggle
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
elRect = undefined;
|
||||
positionToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
window.addEventListener("quarto-hrChanged", () => {
|
||||
elRect = undefined;
|
||||
});
|
||||
|
||||
// Process the click
|
||||
clickEl.onclick = () => {
|
||||
if (!tocShowing) {
|
||||
toggleContainer.classList.add("expanded");
|
||||
toggleContents.style.height = null;
|
||||
tocShowing = true;
|
||||
} else {
|
||||
closeToggle();
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Converts a sidebar from a menu back to a sidebar
|
||||
const convertToSidebar = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 1;
|
||||
child.style.overflow = null;
|
||||
}
|
||||
|
||||
const placeholderEl = window.document.getElementById(
|
||||
placeholderDescriptor.id
|
||||
);
|
||||
if (placeholderEl) {
|
||||
placeholderEl.remove();
|
||||
}
|
||||
|
||||
el.classList.remove("rollup");
|
||||
};
|
||||
|
||||
if (isReaderMode()) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
} else {
|
||||
// Find the top and bottom o the element that is being managed
|
||||
const elTop = el.offsetTop;
|
||||
const elBottom =
|
||||
elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
|
||||
|
||||
if (!isVisible) {
|
||||
// If the element is current not visible reveal if there are
|
||||
// no conflicts with overlay regions
|
||||
if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToSidebar();
|
||||
isVisible = true;
|
||||
}
|
||||
} else {
|
||||
// If the element is visible, hide it if it conflicts with overlay regions
|
||||
// and insert a placeholder toggle (or if we're in reader mode)
|
||||
if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
for (const tabEl of tabEls) {
|
||||
const id = tabEl.getAttribute("data-bs-target");
|
||||
if (id) {
|
||||
const columnEl = document.querySelector(
|
||||
`${id} .column-margin, .tabset-margin-content`
|
||||
);
|
||||
if (columnEl)
|
||||
tabEl.addEventListener("shown.bs.tab", function (event) {
|
||||
const el = event.srcElement;
|
||||
if (el) {
|
||||
const visibleCls = `${el.id}-margin-content`;
|
||||
// walk up until we find a parent tabset
|
||||
let panelTabsetEl = el.parentElement;
|
||||
while (panelTabsetEl) {
|
||||
if (panelTabsetEl.classList.contains("panel-tabset")) {
|
||||
break;
|
||||
}
|
||||
panelTabsetEl = panelTabsetEl.parentElement;
|
||||
}
|
||||
|
||||
if (panelTabsetEl) {
|
||||
const prevSib = panelTabsetEl.previousElementSibling;
|
||||
if (
|
||||
prevSib &&
|
||||
prevSib.classList.contains("tabset-margin-container")
|
||||
) {
|
||||
const childNodes = prevSib.querySelectorAll(
|
||||
".tabset-margin-content"
|
||||
);
|
||||
for (const childEl of childNodes) {
|
||||
if (childEl.classList.contains(visibleCls)) {
|
||||
childEl.classList.remove("collapse");
|
||||
} else {
|
||||
childEl.classList.add("collapse");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutMarginEls();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Manage the visibility of the toc and the sidebar
|
||||
const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
|
||||
id: "quarto-toc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
|
||||
id: "quarto-sidebarnav-toggle",
|
||||
titleSelector: ".title",
|
||||
dismissOnClick: false,
|
||||
});
|
||||
let tocLeftScrollVisibility;
|
||||
if (leftTocEl) {
|
||||
tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
|
||||
id: "quarto-lefttoc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Find the first element that uses formatting in special columns
|
||||
const conflictingEls = window.document.body.querySelectorAll(
|
||||
'[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
|
||||
);
|
||||
|
||||
// Filter all the possibly conflicting elements into ones
|
||||
// the do conflict on the left or ride side
|
||||
const arrConflictingEls = Array.from(conflictingEls);
|
||||
const leftSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return false;
|
||||
}
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("right") &&
|
||||
!className.endsWith("container") &&
|
||||
className !== "column-margin"
|
||||
);
|
||||
});
|
||||
});
|
||||
const rightSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasMarginCaption = Array.from(el.classList).find((className) => {
|
||||
return className == "margin-caption";
|
||||
});
|
||||
if (hasMarginCaption) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
!className.endsWith("container") &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("left")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const kOverlapPaddingSize = 10;
|
||||
function toRegions(els) {
|
||||
return els.map((el) => {
|
||||
const boundRect = el.getBoundingClientRect();
|
||||
const top =
|
||||
boundRect.top +
|
||||
document.documentElement.scrollTop -
|
||||
kOverlapPaddingSize;
|
||||
return {
|
||||
top,
|
||||
bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let hasObserved = false;
|
||||
const visibleItemObserver = (els) => {
|
||||
let visibleElements = [...els];
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries, _observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (visibleElements.indexOf(entry.target) === -1) {
|
||||
visibleElements.push(entry.target);
|
||||
}
|
||||
} else {
|
||||
visibleElements = visibleElements.filter((visibleEntry) => {
|
||||
return visibleEntry !== entry;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasObserved) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
hasObserved = true;
|
||||
},
|
||||
{}
|
||||
);
|
||||
els.forEach((el) => {
|
||||
intersectionObserver.observe(el);
|
||||
});
|
||||
|
||||
return {
|
||||
getVisibleEntries: () => {
|
||||
return visibleElements;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const rightElementObserver = visibleItemObserver(rightSideConflictEls);
|
||||
const leftElementObserver = visibleItemObserver(leftSideConflictEls);
|
||||
|
||||
const hideOverlappedSidebars = () => {
|
||||
marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
|
||||
sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
|
||||
if (tocLeftScrollVisibility) {
|
||||
tocLeftScrollVisibility(
|
||||
toRegions(leftElementObserver.getVisibleEntries())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.quartoToggleReader = () => {
|
||||
// Applies a slow class (or removes it)
|
||||
// to update the transition speed
|
||||
const slowTransition = (slow) => {
|
||||
const manageTransition = (id, slow) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (slow) {
|
||||
el.classList.add("slow");
|
||||
} else {
|
||||
el.classList.remove("slow");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
manageTransition("TOC", slow);
|
||||
manageTransition("quarto-sidebar", slow);
|
||||
};
|
||||
const readerMode = !isReaderMode();
|
||||
setReaderModeValue(readerMode);
|
||||
|
||||
// If we're entering reader mode, slow the transition
|
||||
if (readerMode) {
|
||||
slowTransition(readerMode);
|
||||
}
|
||||
highlightReaderToggle(readerMode);
|
||||
hideOverlappedSidebars();
|
||||
|
||||
// If we're exiting reader mode, restore the non-slow transition
|
||||
if (!readerMode) {
|
||||
slowTransition(!readerMode);
|
||||
}
|
||||
};
|
||||
|
||||
const highlightReaderToggle = (readerMode) => {
|
||||
const els = document.querySelectorAll(".quarto-reader-toggle");
|
||||
if (els) {
|
||||
els.forEach((el) => {
|
||||
if (readerMode) {
|
||||
el.classList.add("reader");
|
||||
} else {
|
||||
el.classList.remove("reader");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setReaderModeValue = (val) => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
window.localStorage.setItem("quarto-reader-mode", val);
|
||||
} else {
|
||||
localReaderMode = val;
|
||||
}
|
||||
};
|
||||
|
||||
const isReaderMode = () => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
return window.localStorage.getItem("quarto-reader-mode") === "true";
|
||||
} else {
|
||||
return localReaderMode;
|
||||
}
|
||||
};
|
||||
let localReaderMode = null;
|
||||
|
||||
const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
|
||||
const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
|
||||
|
||||
// Walk the TOC and collapse/expand nodes
|
||||
// Nodes are expanded if:
|
||||
// - they are top level
|
||||
// - they have children that are 'active' links
|
||||
// - they are directly below an link that is 'active'
|
||||
const walk = (el, depth) => {
|
||||
// Tick depth when we enter a UL
|
||||
if (el.tagName === "UL") {
|
||||
depth = depth + 1;
|
||||
}
|
||||
|
||||
// It this is active link
|
||||
let isActiveNode = false;
|
||||
if (el.tagName === "A" && el.classList.contains("active")) {
|
||||
isActiveNode = true;
|
||||
}
|
||||
|
||||
// See if there is an active child to this element
|
||||
let hasActiveChild = false;
|
||||
for (child of el.children) {
|
||||
hasActiveChild = walk(child, depth) || hasActiveChild;
|
||||
}
|
||||
|
||||
// Process the collapse state if this is an UL
|
||||
if (el.tagName === "UL") {
|
||||
if (tocOpenDepth === -1 && depth > 1) {
|
||||
el.classList.add("collapse");
|
||||
} else if (
|
||||
depth <= tocOpenDepth ||
|
||||
hasActiveChild ||
|
||||
prevSiblingIsActiveLink(el)
|
||||
) {
|
||||
el.classList.remove("collapse");
|
||||
} else {
|
||||
el.classList.add("collapse");
|
||||
}
|
||||
|
||||
// untick depth when we leave a UL
|
||||
depth = depth - 1;
|
||||
}
|
||||
return hasActiveChild || isActiveNode;
|
||||
};
|
||||
|
||||
// walk the TOC and expand / collapse any items that should be shown
|
||||
|
||||
if (tocEl) {
|
||||
walk(tocEl, 0);
|
||||
updateActiveLink();
|
||||
}
|
||||
|
||||
// Throttle the scroll event and walk peridiocally
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 5)
|
||||
);
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 10)
|
||||
);
|
||||
hideOverlappedSidebars();
|
||||
highlightReaderToggle(isReaderMode());
|
||||
});
|
||||
|
||||
// grouped tabsets
|
||||
window.addEventListener("pageshow", (_event) => {
|
||||
function getTabSettings() {
|
||||
const data = localStorage.getItem("quarto-persistent-tabsets-data");
|
||||
if (!data) {
|
||||
localStorage.setItem("quarto-persistent-tabsets-data", "{}");
|
||||
return {};
|
||||
}
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
function setTabSettings(data) {
|
||||
localStorage.setItem(
|
||||
"quarto-persistent-tabsets-data",
|
||||
JSON.stringify(data)
|
||||
);
|
||||
}
|
||||
|
||||
function setTabState(groupName, groupValue) {
|
||||
const data = getTabSettings();
|
||||
data[groupName] = groupValue;
|
||||
setTabSettings(data);
|
||||
}
|
||||
|
||||
function toggleTab(tab, active) {
|
||||
const tabPanelId = tab.getAttribute("aria-controls");
|
||||
const tabPanel = document.getElementById(tabPanelId);
|
||||
if (active) {
|
||||
tab.classList.add("active");
|
||||
tabPanel.classList.add("active");
|
||||
} else {
|
||||
tab.classList.remove("active");
|
||||
tabPanel.classList.remove("active");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAll(selectedGroup, selectorsToSync) {
|
||||
for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
|
||||
const active = selectedGroup === thisGroup;
|
||||
for (const tab of tabs) {
|
||||
toggleTab(tab, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findSelectorsToSyncByLanguage() {
|
||||
const result = {};
|
||||
const tabs = Array.from(
|
||||
document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
|
||||
);
|
||||
for (const item of tabs) {
|
||||
const div = item.parentElement.parentElement.parentElement;
|
||||
const group = div.getAttribute("data-group");
|
||||
if (!result[group]) {
|
||||
result[group] = {};
|
||||
}
|
||||
const selectorsToSync = result[group];
|
||||
const value = item.innerHTML;
|
||||
if (!selectorsToSync[value]) {
|
||||
selectorsToSync[value] = [];
|
||||
}
|
||||
selectorsToSync[value].push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setupSelectorSync() {
|
||||
const selectorsToSync = findSelectorsToSyncByLanguage();
|
||||
Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
|
||||
Object.entries(tabSetsByValue).forEach(([value, items]) => {
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("click", (_event) => {
|
||||
setTabState(group, value);
|
||||
toggleAll(value, selectorsToSync[group]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return selectorsToSync;
|
||||
}
|
||||
|
||||
const selectorsToSync = setupSelectorSync();
|
||||
for (const [group, selectedName] of Object.entries(getTabSettings())) {
|
||||
const selectors = selectorsToSync[group];
|
||||
// it's possible that stale state gives us empty selections, so we explicitly check here.
|
||||
if (selectors) {
|
||||
toggleAll(selectedName, selectors);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function throttle(func, wait) {
|
||||
let waiting = false;
|
||||
return function () {
|
||||
if (!waiting) {
|
||||
func.apply(this, arguments);
|
||||
waiting = true;
|
||||
setTimeout(function () {
|
||||
waiting = false;
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function nexttick(func) {
|
||||
return setTimeout(func, 0);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
||||
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+238
@@ -0,0 +1,238 @@
|
||||
;; # Tablecloth Column Exploration
|
||||
|
||||
^{:kind/hidden true}
|
||||
(ns intro
|
||||
(:require [tablecloth.api :as tc]
|
||||
[scicloj.clay.v2.api :as clay]
|
||||
[scicloj.kindly.v3.api :as kindly]
|
||||
[scicloj.kindly.v3.kind :as kind]))
|
||||
|
||||
^{:kind/hidden true}
|
||||
(clay/start!)
|
||||
|
||||
^{:kind/hidden true}
|
||||
(comment
|
||||
(clay/show-doc! "docs/column_exploration.clj" {:hide-doc? true})
|
||||
(clay/write-html! "docs/column_exploration.html")
|
||||
,)
|
||||
|
||||
;; ## What is this exploration?
|
||||
;;
|
||||
;; We want to add a `column` entity to tablecloth that parallels `dataset`. It will make
|
||||
;; the column a first-class entity within tablecloth.
|
||||
|
||||
;; ## Usage
|
||||
|
||||
(require '[tablecloth.column.api :refer [column] :as col])
|
||||
|
||||
;; ### Column creation
|
||||
|
||||
;; We can create an empty column like this:
|
||||
|
||||
(column)
|
||||
|
||||
;; We can check if it it's a column.
|
||||
|
||||
(col/column? (column))
|
||||
|
||||
;; We can create a columns with data in a number of ways
|
||||
|
||||
(column [1 2 3 4])
|
||||
|
||||
(column (range 10))
|
||||
|
||||
;; When you do this the types of the resulting array is determined
|
||||
;; automatically from the items provided.
|
||||
|
||||
(let [int-column (column (range 10))]
|
||||
(col/typeof int-column))
|
||||
|
||||
(let [string-column (column ["foo" "bar"])]
|
||||
(col/typeof string-column))
|
||||
|
||||
|
||||
;; ### Basic Operations
|
||||
|
||||
;; Operations are right now in their own namespace
|
||||
(require '[tablecloth.column.api.operators :as ops])
|
||||
|
||||
;; With that imported we can perform a large number of operations:
|
||||
|
||||
(def a (column [20 30 40 50]))
|
||||
(def b (column (range 4)))
|
||||
|
||||
(ops/- a b)
|
||||
|
||||
(ops/pow a 2)
|
||||
|
||||
(ops/* 10 (ops/sin a))
|
||||
|
||||
(ops/< a 35)
|
||||
|
||||
;; All these operations take a column as their first argument and
|
||||
;; return a column, so they can be chained easily.
|
||||
|
||||
(-> a
|
||||
(ops/* b)
|
||||
(ops/< 70))
|
||||
|
||||
;; ### Subsetting and accesssing
|
||||
|
||||
;; You can access an element in a column in exactly the same ways you
|
||||
;; would in Clojure.
|
||||
|
||||
(def myclm (column (range 5)))
|
||||
|
||||
myclm
|
||||
|
||||
(myclm 2)
|
||||
|
||||
(nth myclm 2)
|
||||
|
||||
(get myclm 2)
|
||||
|
||||
;; #### Selecting multiple elements
|
||||
|
||||
;; There are two ways to select multiple elements from a column:
|
||||
;; * If you need to select a continuous subset, you can use `slice`;
|
||||
;; * if you may need to select diverse elements, use `select`.
|
||||
;;
|
||||
|
||||
;; **Slice**
|
||||
|
||||
;; The `slice` method allows you to use indexes to specify a portion
|
||||
;; of the column to extract.
|
||||
|
||||
(def myclm
|
||||
(column (repeatedly 10 #(rand-int 10))))
|
||||
|
||||
myclm
|
||||
|
||||
(col/slice myclm 3 5)
|
||||
|
||||
|
||||
;; It also supports negative indexing, making it possible to slice
|
||||
;; from the end of the column:
|
||||
|
||||
(col/slice myclm -7 -5)
|
||||
|
||||
;; It's also possible to slice from one direction to the beginning or
|
||||
;; end:
|
||||
|
||||
(col/slice myclm 7 :end)
|
||||
|
||||
(col/slice myclm -3 :end)
|
||||
|
||||
(col/slice myclm :start 7)
|
||||
|
||||
(col/slice myclm :start -3)
|
||||
|
||||
;; **Select**
|
||||
;;
|
||||
;; The `select` fn works by taking a list of index positions:
|
||||
|
||||
(col/select myclm [1 3 5 8])
|
||||
|
||||
;; We can combine this type of selection with the operations just
|
||||
;; demonstrated to select certain values.
|
||||
|
||||
|
||||
myclm
|
||||
|
||||
;; Let's see which positions are greter than 5.
|
||||
(ops/> myclm 5)
|
||||
|
||||
|
||||
;; We can use a column of boolean values like the one above with the `select` function as well. `select` will choose all the positions that are true. It's like supplying select a list of the index positions that hold true values.
|
||||
(col/select myclm (ops/> myclm 5))
|
||||
|
||||
|
||||
;; ### Iterating over a column
|
||||
|
||||
;; Many operations that you might want to perform on a column are
|
||||
;; available in the `tablecloth.column.api.operators` namespace.
|
||||
;; However, when there is a need to do something custom, you can also
|
||||
;; interate over the column.
|
||||
|
||||
(defn calc-percent [x]
|
||||
(/ x 100.0))
|
||||
|
||||
(col/column-map myclm calc-percent)
|
||||
|
||||
;; It's also possible to iterate over multiple columns by supplying a
|
||||
;; vector of columns:
|
||||
|
||||
(-> [(column [5 6 7 8 9])
|
||||
(column [1 2 3 4 5])]
|
||||
(col/column-map (partial *)))
|
||||
|
||||
|
||||
(comment
|
||||
(-> (column [1 nil 2 3 nil 0])
|
||||
(ops/* 10))
|
||||
|
||||
(-> (column [1 nil 2 3 nil 0])
|
||||
(ops/max [10 10 10 10 10 10]))
|
||||
|
||||
|
||||
(tech.v3.dataset.column/missing))
|
||||
|
||||
;; ### Sorting a column
|
||||
|
||||
;; You can use `sort-column` to sort a colum
|
||||
|
||||
(def myclm (column (repeatedly 10 #(rand-int 100))))
|
||||
|
||||
myclm
|
||||
|
||||
(col/sort-column myclm)
|
||||
|
||||
;; As you can see, sort-columns sorts in ascending order by default,
|
||||
;; but you can also specify a different order using ordering keywords
|
||||
;; `:asc` and `:desc`:
|
||||
|
||||
|
||||
(col/sort-column myclm :desc)
|
||||
|
||||
;; Finally, sort can also accept a `comparator-fn`:
|
||||
|
||||
(let [c (column ["1" "100" "4" "-10"])]
|
||||
(col/sort-column c (fn [a b]
|
||||
(let [a (parse-long a)
|
||||
b (parse-long b)]
|
||||
(< a b)))))
|
||||
|
||||
|
||||
;; ### Missing values
|
||||
|
||||
;; The column has built-in support for basic awareness and handling of
|
||||
;; missing values. Columns will be scanned for missing values when
|
||||
;; created.
|
||||
|
||||
(def myclm (column [10 nil -4 20 1000 nil -233]))
|
||||
|
||||
;; You can identify the set of index positions of missing values:
|
||||
|
||||
(col/missing myclm)
|
||||
|
||||
(col/count-missing myclm)
|
||||
|
||||
;; You can remove missing values:
|
||||
|
||||
(col/drop-missing myclm)
|
||||
|
||||
;; Or you can replace them:
|
||||
|
||||
(col/replace-missing myclm)
|
||||
|
||||
;; There are a range of built-in strategies:
|
||||
|
||||
(col/replace-missing myclm :midpoint)
|
||||
|
||||
|
||||
;; And you can provide your own value using a specific value or fn:
|
||||
|
||||
(col/replace-missing myclm :value 555)
|
||||
|
||||
(col/replace-missing myclm :value (fn [col-without-missing]
|
||||
(ops/mean col-without-missing)))
|
||||
+3813
File diff suppressed because one or more lines are too long
+40114
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user