init research

This commit is contained in:
2026-02-08 11:20:43 -10:00
commit bdf064f54d
3041 changed files with 1592200 additions and 0 deletions
+25
View File
@@ -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
View File
@@ -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"
]
}
}
}
+5
View File
@@ -0,0 +1,5 @@
((nil
.
((cider-clojure-cli-aliases
.
"dev:test"))))
+2
View File
@@ -0,0 +1,2 @@
docs/** linguist-documentation
pr-preview/** linguist-documentation
+27
View File
@@ -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
+35
View File
@@ -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
+27
View File
@@ -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
+25
View File
@@ -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*
+14
View File
@@ -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
+513
View File
@@ -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
+21
View File
@@ -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.
+162
View File
@@ -0,0 +1,162 @@
# Tablecloth
Dataset (data frame) manipulation API for the tech.ml.dataset library
[![](https://img.shields.io/clojars/v/scicloj/tablecloth)](https://clojars.org/scicloj/tablecloth)
[![](https://api.travis-ci.org/scicloj/tablecloth.svg?branch=master)](https://travis-ci.org/github/scicloj/tablecloth)
[![](https://img.shields.io/badge/zulip-discussion-yellowgreen)](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
## Versions
### tech.ml.dataset 7.x (master branch)
[![](https://img.shields.io/clojars/v/scicloj/tablecloth)](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
+180
View File
@@ -0,0 +1,180 @@
# Tablecloth
Dataset (data frame) manipulation API for the tech.ml.dataset library
[![](https://img.shields.io/clojars/v/scicloj/tablecloth)](https://clojars.org/scicloj/tablecloth)
[![](https://api.travis-ci.org/scicloj/tablecloth.svg?branch=master)](https://travis-ci.org/github/scicloj/tablecloth)
[![](https://img.shields.io/badge/zulip-discussion-yellowgreen)](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
## Versions
### tech.ml.dataset 7.x (master branch)
[![](https://img.shields.io/clojars/v/scicloj/tablecloth)](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
+10
View File
@@ -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"}}
+14
View File
@@ -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"]}}}
+123
View File
@@ -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"))
+12
View File
@@ -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"})
+7
View File
@@ -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/"))
+85
View File
@@ -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!))
View File
+9
View File
@@ -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
View File
@@ -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 &amp; 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 &amp; 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 &amp; 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&amp;lt<span class="co">;boolean&amp;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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;int64&amp;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 &amp; 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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;int64&amp;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>Tablecloths 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">-&gt;</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 &amp; 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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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 &amp; 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">-&gt;</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">-&gt;</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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;keyword&amp;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">-&gt;</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&amp;lt<span class="co">;keyword&amp;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">-&gt;</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">&lt;</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&amp;lt<span class="co">;persistent-map&amp;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>-&gt;</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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;float64&amp;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&amp;lt<span class="co">;float64&amp;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/&lt; 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&amp;lt<span class="co">;boolean&amp;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">-&gt;</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/&lt; <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&amp;lt<span class="co">;boolean&amp;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
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
@@ -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
View File
@@ -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)))
File diff suppressed because one or more lines are too long
+40366
View File
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
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 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
@@ -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 */
+823
View File
@@ -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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12
View File
@@ -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;
}
+179
View File
@@ -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://img.shields.io/clojars/v/scicloj/tablecloth)](https://clojars.org/scicloj/tablecloth)
[![](https://api.travis-ci.org/scicloj/tablecloth.svg?branch=master)](https://travis-ci.org/github/scicloj/tablecloth)
[![](https://img.shields.io/badge/zulip-discussion-yellowgreen)](https://clojurians.zulipchat.com/#narrow/stream/236259-tech.2Eml.2Edataset.2Edev/topic/api)
## Versions
### tech.ml.dataset 7.x (master branch)
[![](https://img.shields.io/clojars/v/scicloj/tablecloth)](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
+5743
View File
File diff suppressed because it is too large Load Diff
+37740
View File
File diff suppressed because one or more lines are too long
+12982
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+12
View File
@@ -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;
}
+7137
View File
File diff suppressed because it is too large Load Diff
+1711
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -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
View File
@@ -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 &amp; 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 &amp; 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 &amp; 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&amp;lt<span class="co">;boolean&amp;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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;int64&amp;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 &amp; 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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;int64&amp;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>Tablecloths 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">-&gt;</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 &amp; 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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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 &amp; 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">-&gt;</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">-&gt;</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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;int64&amp;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">-&gt;</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&amp;lt<span class="co">;keyword&amp;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">-&gt;</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&amp;lt<span class="co">;keyword&amp;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">-&gt;</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">&lt;</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&amp;lt<span class="co">;persistent-map&amp;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>-&gt;</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&amp;lt<span class="co">;int64&amp;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&amp;lt<span class="co">;float64&amp;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&amp;lt<span class="co">;float64&amp;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/&lt; 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&amp;lt<span class="co">;boolean&amp;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">-&gt;</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/&lt; <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&amp;lt<span class="co">;boolean&amp;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
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
@@ -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
View File
@@ -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)))
File diff suppressed because one or more lines are too long
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