diff --git a/.gitignore b/.gitignore index e108cd1..6e2b8d6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,6 @@ pom.xml.asc *.txt* .lsp/ .clj-kondo/ -.clay.html *.qmd .quarto +.clay* diff --git a/deps.edn b/deps.edn index 32c12c2..77dd4e5 100644 --- a/deps.edn +++ b/deps.edn @@ -1,9 +1,9 @@ {:extra-paths ["data"] :deps {org.clojure/clojure {:mvn/version "1.11.1"} techascent/tech.ml.dataset {:mvn/version "7.030"}} - :aliases {:dev {:extra-deps {org.scicloj/clay {:mvn/version "2-beta9"} + :aliases {:dev {:extra-deps {org.scicloj/clay {:mvn/version "2-beta11"} org.scicloj/note-to-test {:mvn/version "1-alpha7"}}} - :test {:extra-deps {org.scicloj/clay {:mvn/version "2-beta9"} + :test {:extra-deps {org.scicloj/clay {:mvn/version "2-beta11"} org.scicloj/note-to-test {:mvn/version "1-alpha7"} midje/midje {:mvn/version "1.10.10"}} :extra-paths ["test"]}}} diff --git a/docs/.clay.html b/docs/.clay.html index c0d599e..4d21cb6 100644 --- a/docs/.clay.html +++ b/docs/.clay.html @@ -45,9 +45,9 @@ */.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #373a3c;--quarto-text-muted: rgba(55, 58, 60, 0.75);--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.375rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #373a3c;--mermaid-node-fg-color: #373a3c;--mermaid-fg-color: #373a3c;--mermaid-fg-color--lighter: #4f5457;--mermaid-fg-color--lightest: #686d71;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: #373a3c}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}pre.sourceCode:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}pre.sourceCode:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 35px [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(850px - 3em)) [body-content-end] 3em [body-end] 50px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(50px, 100px) [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 175px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 175px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(25px, 50px) [page-start-inset] minmax(50px, 150px) [body-start-outset] minmax(25px, 50px) [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(1000px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(1000px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 50px [page-start-inset] minmax(50px, 150px) [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(50px, 100px) [page-start-inset] 50px [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(500px, calc(1000px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 50px [page-start-inset] minmax(50px, 150px) [body-start-outset] 50px [body-start] 1.5em [body-content-start] minmax(450px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(25px, 50px) [page-start-inset] minmax(50px, 150px) [body-start-outset] minmax(25px, 50px) [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc(1250px - 3em)) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 145px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 35px [page-start-inset] minmax(0px, 145px) [body-start-outset] 35px [body-start] 1.5em [body-content-start] minmax(450px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1.5em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(1000px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(800px - 3em)) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc(750px - 3em)) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left .page-columns.page-full>*,.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right .page-columns.page-full>*,.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;opacity:.999}.page-columns .column-body-outset table{background:#fff}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;opacity:.999}.page-columns .column-body-outset-left table{background:#fff}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;opacity:.999}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.page-columns .column-page-inset table{background:#fff}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;opacity:.999}.page-columns .column-page-inset-left table{background:#fff}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;opacity:.999}.page-columns .column-page-inset-right figcaption table{background:#fff}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.page-columns .column-page-right figcaption table{background:#fff}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;opacity:.999}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;opacity:.999}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}.zindex-content{z-index:998;opacity:.999}.zindex-modal{z-index:1055;opacity:.999}.zindex-over-content{z-index:999;opacity:.999}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside:not(.footnotes):not(.sidebar),.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside:not(.footnotes):not(.sidebar),.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside:not(.footnotes):not(.sidebar),.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside:not(.footnotes):not(.sidebar),.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;opacity:.999}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{color:inherit;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}main.content>section:first-of-type>h2:first-child,main.content>section:first-of-type>.h2:first-child{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#747a7f}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,.figure-caption,.subfigure-caption,.table-caption,figcaption,caption{font-size:.9rem;color:#747a7f}.quarto-layout-cell[data-ref-parent] caption{color:#747a7f}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#747a7f;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse):first-child{padding-bottom:.5em;display:block}.column-margin.column-container>*:not(.collapse):not(:first-child){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:0}.tab-pane>p:nth-child(1){padding-top:0}.tab-pane>p:last-child{margin-bottom:0}.tab-pane>pre:last-child{margin-bottom:0}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.375rem}pre.sourceCode{background-color:rgba(0,0,0,0)}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#747a7f}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p pre code:not(.sourceCode),li pre code:not(.sourceCode),pre code:not(.sourceCode){background-color:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#f8f9fa;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:rgba(55,58,60,.75);background-color:rgba(0,0,0,0);transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:rgba(55,58,60,.75);margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#2761e3}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.toc-actions i.bi,.quarto-code-links i.bi,.quarto-other-links i.bi,.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em;font-size:.8rem}.quarto-other-links-text-target .quarto-code-links i.bi,.quarto-other-links-text-target .quarto-other-links i.bi{margin-right:.2em}.quarto-other-formats-text-target .quarto-alternate-formats i.bi{margin-right:.1em}.toc-actions i.bi.empty,.quarto-code-links i.bi.empty,.quarto-other-links i.bi.empty,.quarto-alternate-notebooks i.bi.empty,.quarto-alternate-formats i.bi.empty{padding-left:1em}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook .cell-container.code-fold .cell-decorator{padding-top:3em}.quarto-notebook .cell-code code{white-space:pre-wrap}.quarto-notebook .cell .cell-output-stderr pre code,.quarto-notebook .cell .cell-output-stdout pre code{white-space:pre-wrap;overflow-wrap:anywhere}.toc-actions,.quarto-alternate-formats,.quarto-other-links,.quarto-code-links,.quarto-alternate-notebooks{padding-left:0em}.sidebar .toc-actions a,.sidebar .quarto-alternate-formats a,.sidebar .quarto-other-links a,.sidebar .quarto-code-links a,.sidebar .quarto-alternate-notebooks a,.sidebar nav[role=doc-toc] a{text-decoration:none}.sidebar .toc-actions a:hover,.sidebar .quarto-other-links a:hover,.sidebar .quarto-code-links a:hover,.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#2761e3}.sidebar .toc-actions h2,.sidebar .toc-actions .h2,.sidebar .quarto-code-links h2,.sidebar .quarto-code-links .h2,.sidebar .quarto-other-links h2,.sidebar .quarto-other-links .h2,.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-weight:500;margin-bottom:.2rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .toc-actions>h2,.sidebar .toc-actions>.h2,.sidebar .quarto-code-links>h2,.sidebar .quarto-code-links>.h2,.sidebar .quarto-other-links>h2,.sidebar .quarto-other-links>.h2,.sidebar .quarto-alternate-notebooks>h2,.sidebar .quarto-alternate-notebooks>.h2,.sidebar .quarto-alternate-formats>h2,.sidebar .quarto-alternate-formats>.h2{font-size:.8rem}.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .toc-actions h2>ul a,.sidebar .toc-actions .h2>ul a,.sidebar .quarto-code-links h2>ul a,.sidebar .quarto-code-links .h2>ul a,.sidebar .quarto-other-links h2>ul a,.sidebar .quarto-other-links .h2>ul a,.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .toc-actions ul a:empty,.sidebar .quarto-code-links ul a:empty,.sidebar .quarto-other-links ul a:empty,.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .toc-actions ul,.sidebar .quarto-code-links ul,.sidebar .quarto-other-links ul,.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul{padding-left:0;list-style:none}.sidebar nav[role=doc-toc] ul{list-style:none;padding-left:0;list-style:none}.sidebar nav[role=doc-toc]>ul{margin-left:.45em}.quarto-margin-sidebar nav[role=doc-toc]{padding-left:.5em}.sidebar .toc-actions>ul,.sidebar .quarto-code-links>ul,.sidebar .quarto-other-links>ul,.sidebar .quarto-alternate-notebooks>ul,.sidebar .quarto-alternate-formats>ul{font-size:.8rem}.sidebar nav[role=doc-toc]>ul{font-size:.875rem}.sidebar .toc-actions ul li a,.sidebar .quarto-code-links ul li a,.sidebar .quarto-other-links ul li a,.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #2761e3;color:#2761e3 !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#2761e3 !important}kbd,.kbd{color:#373a3c;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}.quarto-appendix-contents div.hanging-indent{margin-left:0em}.quarto-appendix-contents div.hanging-indent div.csl-entry{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.375rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default .callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default .callout-body>:first-child{padding-top:.5rem;margin-top:0}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){padding-bottom:.5rem;margin-bottom:0}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:rgba(55,58,60,.75)}div.callout.callout-style-default>.callout-header{background-color:rgba(55,58,60,.75)}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#e9f2fc}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e8}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#fff1e8}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#fef7ed}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#ffe6eb}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex;align-items:center}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.sidebar-navigation{padding-left:20px}.navbar{background-color:#f8f9fa;color:#545555}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.375rem;border-bottom-right-radius:.375rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#fafafa}#quarto-content .quarto-sidebar-toggle-title{color:#373a3c}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#fff;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}@media(max-width: 767.98px){.sidebar-menu-container{padding-bottom:5em}}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#fff;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .footnotes ol{margin-left:.5em}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{--bs-btn-color: #cbcccc;--bs-btn-bg: #373a3c;--bs-btn-border-color: #373a3c;--bs-btn-hover-color: #cbcccc;--bs-btn-hover-bg: #555859;--bs-btn-hover-border-color: #4b4e50;--bs-btn-focus-shadow-rgb: 77, 80, 82;--bs-btn-active-color: #fff;--bs-btn-active-bg: #5f6163;--bs-btn-active-border-color: #4b4e50;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #373a3c;--bs-btn-disabled-border-color: #373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#f8f9fa;color:#545555}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#545555}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:1em}body.nav-sidebar #title-block-header{margin-block-end:0}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#868e96}/*! light */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#fff}.code-annotation-gutter{background-color:rgba(233,236,239,.65)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:#4f5457;border:solid #4f5457 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#fff;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;opacity:.999}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;opacity:.999}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;opacity:.999}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;opacity:.999}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;opacity:.999}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:page-start-inset/page-end-inset;padding:1em;background:#f8f9fa;z-index:998;opacity:.999;margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table{border-top:1px solid #ebeced;border-bottom:1px solid #ebeced}.table>thead{border-top-width:0;border-bottom:1px solid #b6babc}.table a{word-break:break-word}.table>:not(caption)>*>*{background-color:unset;color:unset}#quarto-document-content .crosstalk-input .checkbox input[type=checkbox],#quarto-document-content .crosstalk-input .checkbox-inline input[type=checkbox]{position:unset;margin-top:unset;margin-left:unset}#quarto-document-content .row{margin-left:unset;margin-right:unset}.quarto-xref{white-space:nowrap}#quarto-draft-alert{margin-top:0px;margin-bottom:0px;padding:.3em;text-align:center;font-size:.9em}#quarto-draft-alert i{margin-right:.3em}a.external:after{content:"";background-image:url('data:image/svg+xml,');background-size:contain;background-repeat:no-repeat;background-position:center center;margin-left:.2em;padding-right:.75em}div.sourceCode code a.external:after{content:none}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#545555;background:#f8f9fa}.quarto-title-banner a{color:#545555}.quarto-title-banner h1,.quarto-title-banner .h1,.quarto-title-banner h2,.quarto-title-banner .h2{color:#545555}.quarto-title-banner .code-tools-button{color:#878888}.quarto-title-banner .code-tools-button:hover{color:#545555}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}@media(max-width: 767.98px){body.hypothesis-enabled #title-block-header>*{padding-right:20px}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;column-gap:.4em;padding-bottom:.5em;margin-top:.75em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.375rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}.quarto-title-meta-container{display:grid;grid-template-columns:1fr auto}.quarto-title-meta-column-end{display:flex;flex-direction:column;padding-left:1em}.quarto-title-meta-column-end a .bi{margin-right:.3em}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-0.2em;height:.8em;width:.8em}#title-block-header.quarto-title-block.default .quarto-title-author-email{opacity:.7}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.1em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .keywords,#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .keywords>p,#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .keywords>p:last-of-type,#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .keywords .block-title,#title-block-header.quarto-title-block.default .description .block-title,#title-block-header.quarto-title-block.default .abstract .block-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:minmax(max-content, 1fr) 1fr;grid-column-gap:1em}.quarto-title-tools-only{display:flex;justify-content:right}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}.table{width:auto}code{font-family:"Fira Code Medium",monospace}.clay-image img{max-width:100%} - + Clay - +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+

Tablecloth documentation

+
+ + + +
+ + + + +
+ + + +
+ + + + + + + +

Dataset (data frame) manipulation API for the tech.ml.dataset library

+

GenerateMe

+
+
(def tech-ml-version (get-in (read-string (slurp "deps.edn")) [:deps 'techascent/tech.ml.dataset :mvn/version]))
+
+
+
(def tablecloth-version (nth (read-string (slurp "project.clj")) 2))
+
+
+
tech-ml-version
+
+
+
"7.030"
+
+
+
tablecloth-version
+
+
+
"7.030"
+
+
+

Introduction

+

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, tidyr and 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.
  • +
+

If you want to know more about tech.ml.dataset and dtype-next please refer their documentation:

+ +

SOURCE CODE

+

Join the discussion on Zulip

+

First, let’s require main namespace and define dataset used in most examples:

+
+
(require '[tablecloth.api :as tc]
+         '[tech.v3.datatype.functional :as dfn])
+
+
+
(def DS (tc/dataset {:V1 (take 9 (cycle [1 2]))
+                     :V2 (range 1 10)
+                     :V3 (take 9 (cycle [0.5 1.0 1.5]))
+                     :V4 (take 9 (cycle ["A" "B" "C"]))}))
+
+
+
DS
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+
+

Dataset API

+

Dataset is a special type which can be considered as a map of columns implemented around tech.ml.dataset library. Each column can be considered as named sequence of typed data. Supported types include integers, floats, string, boolean, date/time, objects etc.

+
+

Dataset creation

+

Dataset can be created from various of types of Clojure structures and files:

+
    +
  • single values
  • +
  • sequence of maps
  • +
  • map of sequences or values
  • +
  • sequence of tech.v3.dataset.columns (taken from other dataset or created manually)
  • +
  • array of any arrays
  • +
  • file types: raw/gzipped csv/tsv, json, xls(x) taken from local file system or URL
  • +
  • input stream
  • +
+

tc/dataset accepts:

+
    +
  • data
  • +
  • options (see documentation of tech.ml.dataset/->dataset function for full list): +
      +
    • :dataset-name - name of the dataset
    • +
    • :num-rows - number of rows to read from file
    • +
    • :header-row? - indication if first row in file is a header
    • +
    • :key-fn - function applied to column names (eg. keyword, to convert column names to keywords)
    • +
    • :separator - column separator
    • +
    • :single-value-column-name - name of the column when single value is provided
    • +
    • :column-names - in case you want to name columns - only works for sequential input (arrays) or empty dataset
    • +
    • :layout - for numerical, native array of arrays - treat entries :as-rows or :as-columns (default)
    • +
  • +
+

tc/let-dataset accepts bindings symbol-column-data to simulate R’s tibble function. Each binding is converted into a column. You can refer previous columns to in further bindings (as in let).

+
+

Empty dataset.

+
+
(tc/dataset)
+
+
+

_unnamed [0 0]

+
+
+

Empty dataset with column names

+
+
(tc/dataset nil {:column-names [:a :b]})
+
+
+

_unnamed [0 2]:

+ + + + + + + + + +
:a:b
+
+
+

Dataset created from map (keys = column names, vals = value(s)).

+
+
(tc/dataset {:A 33})
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:A
33
+
+
+
(tc/dataset {:A [1 2 3]})
+
+
+

_unnamed [3 1]:

+ + + + + + + + + + + + + + + + + +
:A
1
2
3
+
+
+
(tc/dataset {:A [3 4 5] :B ["X" "Y" "Z"]})
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:A:B
3X
4Y
5Z
+
+
+

Non-sequential values are repeated row-count number of times.

+
+
(tc/dataset {:A [1 2 3 4 5 6] :B "X" :C :a})
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:A:B:C
1X:a
2X:a
3X:a
4X:a
5X:a
6X:a
+
+
+

You can put any value inside a column

+
+
(tc/dataset {:A [[3 4 5] [:a :b]] :B "X"})
+
+
+

_unnamed [2 2]:

+ + + + + + + + + + + + + + + + + +
:A:B
[3 4 5]X
[:a :b]X
+
+
+

Sequence of maps

+
+
(tc/dataset [{:a 1 :b 3} {:b 2 :a 99}])
+
+
+

_unnamed [2 2]:

+ + + + + + + + + + + + + + + + + +
:a:b
13
992
+
+
+
(tc/dataset [{:a 1 :b [1 2 3]} {:a 2 :b [3 4]}])
+
+
+

_unnamed [2 2]:

+ + + + + + + + + + + + + + + + + +
:a:b
1[1 2 3]
2[3 4]
+
+
+

Missing values are marked by nil

+
+
(tc/dataset [{:a nil :b 1} {:a 3 :b 4} {:a 11}])
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:a:b
1
34
11
+
+
+

Reading from arrays, by default :as-rows

+
+
(tc/dataset [[:a 1] [:b 2] [:c 3]])
+
+
+

:_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
01
:a1
:b2
:c3
+
+
+
(-> (map int-array [[1 2] [3 4] [5 6]])
+    (into-array)
+    (tc/dataset))
+
+
+

:_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
01
12
34
56
+
+

:as-columns

+
+
(-> (map int-array [[1 2] [3 4] [5 6]])
+    (into-array)
+    (tc/dataset {:layout :as-columns}))
+
+
+

:_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
012
135
246
+
+

:as-rows with names

+
+
(-> (map int-array [[1 2] [3 4] [5 6]])
+    (into-array)
+    (tc/dataset {:layout :as-rows
+                 :column-names [:a :b]}))
+
+
+

:_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:a:b
12
34
56
+
+

Any objects

+
+
(-> (map to-array [[:a :z] ["ee" "ww"] [9 10]])
+    (into-array)
+    (tc/dataset {:column-names [:a :b :c]
+                 :layout :as-columns}))
+
+
+

:_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:a:b:c
:aee9
:zww10
+
+
+

Create dataset using macro let-dataset to simulate R tibble function. Each binding is converted into a column.

+
+
(tc/let-dataset [x (range 1 6)
+                  y 1
+                  z (dfn/+ x y)])
+
+
+

:_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
01
:x(1 2 3 4 5)
:y1
:ztech.v3.datatype.unary_op\(eval29121\)fn$reify__29132@31056374
+
+
+

Import CSV file

+
+
(tc/dataset "data/family.csv")
+
+
+

data/family.csv [5 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
familydob_child1dob_child2gender_child1gender_child2
11998-11-262000-01-2912
21996-06-222
32002-07-112004-04-0522
42004-10-102009-08-2711
52000-12-052005-02-2821
+
+
+

Import from URL

+
+
(defonce ds (tc/dataset "https://vega.github.io/vega-lite/examples/data/seattle-weather.csv"))
+
+
+
ds
+
+
+

https://vega.github.io/vega-lite/examples/data/seattle-weather.csv [1461 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dateprecipitationtemp_maxtemp_minwindweather
2012-01-010.012.85.04.7drizzle
2012-01-0210.910.62.84.5rain
2012-01-030.811.77.22.3rain
2012-01-0420.312.25.64.7rain
2012-01-051.38.92.86.1rain
2012-01-062.54.42.22.2rain
2012-01-070.07.22.82.3rain
2012-01-080.010.02.82.0sun
2012-01-094.39.45.03.4rain
2012-01-101.06.10.63.4rain
2015-12-2127.45.62.84.3rain
2015-12-224.67.82.85.0rain
2015-12-236.15.02.87.6rain
2015-12-242.55.62.24.3rain
2015-12-255.85.02.21.5rain
2015-12-260.04.40.02.5sun
2015-12-278.64.41.72.9rain
2015-12-281.55.01.71.3rain
2015-12-290.07.20.62.6fog
2015-12-300.05.6-1.03.4sun
2015-12-310.05.6-2.13.5sun
+
+
+

When none of above works, singleton dataset is created. Along with the error message from the exception thrown by tech.ml.dataset

+
+
(tc/dataset 999)
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:\(value | :\)error
999Don’t know how to create ISeq from: java.lang.Long
+
+

To see the stack trace, turn it on by setting :stack-trace? to true.

+
+

Set column name for single value. Also set the dataset name and turn off creating error message column.

+
+
(tc/dataset 999 {:single-value-column-name "my-single-value"
+                 :error-column? false})
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
my-single-value
999
+
+
+
(tc/dataset 999 {:single-value-column-name ""
+                 :dataset-name "Single value"
+                 :error-column? false})
+
+
+

Single value [1 1]:

+ + + + + + + + + + + +
0
999
+
+
+
+

Saving

+

Export dataset to a file or output stream can be done by calling tc/write!. Function accepts:

+
    +
  • dataset
  • +
  • file name with one of the extensions: .csv, .tsv, .csv.gz and .tsv.gz or output stream
  • +
  • options:
  • +
  • :separator - string or separator char.
  • +
+
+
(tc/write! ds "output.tsv.gz")
+
+
+
1462
+
+
+
(.exists (clojure.java.io/file "output.tsv.gz"))
+
+
+
true
+
+
+

Nippy

+
+
(tc/write! DS "output.nippy.gz")
+
+
+
nil
+
+
+
(tc/dataset "output.nippy.gz")
+
+
+

output.nippy.gz [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+
+ +
+

Columns and rows

+

Get columns and rows as sequences. column, columns and rows treat grouped dataset as regular one. See Groups to read more about grouped datasets.

+

Possible result types:

+
    +
  • :as-seq or :as-seqs - sequence of seqences (default)
  • +
  • :as-maps - sequence of maps (rows)
  • +
  • :as-map - map of sequences (columns)
  • +
  • :as-double-arrays - array of double arrays
  • +
  • :as-vecs - sequence of vectors (rows)
  • +
+

For rows setting :nil-missing? option to false will elide keys for nil values.

+
+

Select column.

+
+
(ds "wind")
+
+
+
#tech.v3.dataset.column<float64>[1461]
+wind
+[4.700, 4.500, 2.300, 4.700, 6.100, 2.200, 2.300, 2.000, 3.400, 3.400, 5.100, 1.900, 1.300, 5.300, 3.200, 5.000, 5.600, 5.000, 1.600, 2.300...]
+
+
+
(tc/column ds "date")
+
+
+
#tech.v3.dataset.column<packed-local-date>[1461]
+date
+[2012-01-01, 2012-01-02, 2012-01-03, 2012-01-04, 2012-01-05, 2012-01-06, 2012-01-07, 2012-01-08, 2012-01-09, 2012-01-10, 2012-01-11, 2012-01-12, 2012-01-13, 2012-01-14, 2012-01-15, 2012-01-16, 2012-01-17, 2012-01-18, 2012-01-19, 2012-01-20...]
+
+
+

Columns as sequence

+
+
(take 2 (tc/columns ds))
+
+
+
(#tech.v3.dataset.column<packed-local-date>[1461]
+date
+[2012-01-01, 2012-01-02, 2012-01-03, 2012-01-04, 2012-01-05, 2012-01-06, 2012-01-07, 2012-01-08, 2012-01-09, 2012-01-10, 2012-01-11, 2012-01-12, 2012-01-13, 2012-01-14, 2012-01-15, 2012-01-16, 2012-01-17, 2012-01-18, 2012-01-19, 2012-01-20...]
+ #tech.v3.dataset.column<float64>[1461]
+precipitation
+[0.000, 10.90, 0.8000, 20.30, 1.300, 2.500, 0.000, 0.000, 4.300, 1.000, 0.000, 0.000, 0.000, 4.100, 5.300, 2.500, 8.100, 19.80, 15.20, 13.50...])
+
+
+

Columns as map

+
+
(keys (tc/columns ds :as-map))
+
+
+
("date" "precipitation" "temp_max" "temp_min" "wind" "weather")
+
+
+

Rows as sequence of sequences

+
+
(take 2 (tc/rows ds))
+
+
+
([#object[java.time.LocalDate 0x3f3237ad "2012-01-01"]
+  0.0
+  12.8
+  5.0
+  4.7
+  "drizzle"]
+ [#object[java.time.LocalDate 0x775ba254 "2012-01-02"]
+  10.9
+  10.6
+  2.8
+  4.5
+  "rain"])
+
+
+

Select rows/columns as double-double-array

+
+
(-> ds
+    (tc/select-columns :type/numerical)
+    (tc/head)
+    (tc/rows :as-double-arrays))
+
+
+
[[0.0, 12.8, 5.0, 4.7], [10.9, 10.6, 2.8, 4.5], [0.8, 11.7, 7.2, 2.3],
+ [20.3, 12.2, 5.6, 4.7], [1.3, 8.9, 2.8, 6.1]]
+
+
+
(-> ds
+    (tc/select-columns :type/numerical)
+    (tc/head)
+    (tc/columns :as-double-arrays))
+
+
+
[[0.0, 10.9, 0.8, 20.3, 1.3], [12.8, 10.6, 11.7, 12.2, 8.9],
+ [5.0, 2.8, 7.2, 5.6, 2.8], [4.7, 4.5, 2.3, 4.7, 6.1]]
+
+
+

Rows as sequence of maps

+
+
(clojure.pprint/pprint (take 2 (tc/rows ds :as-maps)))
+
+
+
nil
+
+
+

Rows with missing values

+
+
(-> {:a [1 nil 2]
+     :b [3 4 nil]}
+    (tc/dataset)
+    (tc/rows :as-maps))
+
+
+
[{:a 1, :b 3} {:a nil, :b 4} {:a 2, :b nil}]
+
+

Rows with elided missing values

+
+
(-> {:a [1 nil 2]
+     :b [3 4 nil]}
+    (tc/dataset)
+    (tc/rows :as-maps {:nil-missing? false}))
+
+
+
[{:a 1, :b 3} {:b 4} {:a 2}]
+
+
+
+

Single entry

+

Get single value from the table using get-in from Clojure API or get-entry. First argument is column name, second is row number.

+
+
(get-in ds ["wind" 2])
+
+
+
2.3
+
+
+
(tc/get-entry ds "wind" 2)
+
+
+
2.3
+
+
+
+

Printing

+

Dataset is printed using dataset->str or print-dataset functions. Options are the same as in tech.ml.dataset/dataset-data->str. Most important is :print-line-policy which can be one of the: :single, :repl or :markdown.

+
+
(tc/print-dataset (tc/group-by DS :V1) {:print-line-policy :markdown})
+
+
+
nil
+
+
+
(tc/print-dataset (tc/group-by DS :V1) {:print-line-policy :repl})
+
+
+
nil
+
+
+
(tc/print-dataset (tc/group-by DS :V1) {:print-line-policy :single})
+
+
+
nil
+
+
+
+

Group-by

+

Grouping by is an operation which splits dataset into subdatasets and pack it into new special type of… dataset. I distinguish two types of dataset: regular dataset and grouped dataset. The latter is the result of grouping.

+

Grouped dataset is annotated in by :grouped? meta tag and consist following columns:

+
    +
  • :name - group name or structure
  • +
  • :group-id - integer assigned to the group
  • +
  • :data - groups as datasets
  • +
+

Almost all functions recognize type of the dataset (grouped or not) and operate accordingly.

+

You can’t apply reshaping or join/concat functions on grouped datasets.

+
+

Grouping

+

Grouping is done by calling group-by function with arguments:

+
    +
  • ds - dataset
  • +
  • grouping-selector - what to use for grouping
  • +
  • options:
  • +
  • :result-type - what to return:
  • +
  • :as-dataset (default) - return grouped dataset
  • +
  • :as-indexes - return rows ids (row number from original dataset)
  • +
  • :as-map - return map with group names as keys and subdataset as values
  • +
  • :as-seq - return sequence of subdatasets
  • +
  • :select-keys - list of the columns passed to a grouping selector function
  • +
+

All subdatasets (groups) have set name as the group name, additionally group-id is in meta.

+

Grouping can be done by:

+
    +
  • single column name
  • +
  • seq of column names
  • +
  • map of keys (group names) and row indexes
  • +
  • value returned by function taking row as map (limited to :select-keys)
  • +
+

Note: currently dataset inside dataset is printed recursively so it renders poorly from markdown. So I will use :as-seq result type to show just group names and groups.

+
+

List of columns in grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/column-names))
+
+
+
(:V1 :V2 :V3 :V4)
+
+
+

List of columns in grouped dataset treated as regular dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/as-regular-dataset)
+    (tc/column-names))
+
+
+
(:name :group-id :data)
+
+
+

Content of the grouped dataset

+
+
(tc/columns (tc/group-by DS :V1) :as-map)
+
+
+

{

:name #tech.v3.dataset.column<int64>[2]
+:name
+[1, 2]
:group-id #tech.v3.dataset.column<int64>[2]
+:group-id
+[0, 1]
+ ++++ + + + + + + +
+
:data
+
+
+

(

+
+
+

Group: 1 [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
151.0B
170.5A
191.5C
+
+
+

Group: 2 [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
240.5A
261.5C
281.0B
+
+
+

)

+
+
+

}

+
+
+

Grouped dataset as map

+
+
(keys (tc/group-by DS :V1 {:result-type :as-map}))
+
+
+
(1 2)
+
+
+
(vals (tc/group-by DS :V1 {:result-type :as-map}))
+
+
+

(

Group: 1 [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
151.0B
170.5A
191.5C
+

Group: 2 [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
240.5A
261.5C
281.0B
+

)

+
+
+

Group dataset as map of indexes (row ids)

+
+
(tc/group-by DS :V1 {:result-type :as-indexes})
+
+
+
{1 [0 2 4 6 8], 2 [1 3 5 7]}
+
+
+

Grouped datasets are printed as follows by default.

+
+
(tc/group-by DS :V1)
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
10Group: 1 [5 4]:
21Group: 2 [4 4]:
+
+
+

To get groups as sequence or a map can be done from grouped dataset using groups->seq and groups->map functions.

+

Groups as seq can be obtained by just accessing :data column.

+

I will use temporary dataset here.

+
+
(let [ds (-> {"a" [1 1 2 2]
+              "b" ["a" "b" "c" "d"]}
+             (tc/dataset)
+             (tc/group-by "a"))]
+  (seq (ds :data)))
+
+
+

(

Group: 1 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
1a
1b
+

Group: 2 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
2c
2d
+

)

+
+

seq is not necessary but Markdown treats :data as command here

+
+
(-> {"a" [1 1 2 2]
+     "b" ["a" "b" "c" "d"]}
+    (tc/dataset)
+    (tc/group-by "a")
+    (tc/groups->seq))
+
+
+

(

Group: 1 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
1a
1b
+

Group: 2 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
2c
2d
+

)

+
+
+

Groups as map

+
+
(-> {"a" [1 1 2 2]
+     "b" ["a" "b" "c" "d"]}
+    (tc/dataset)
+    (tc/group-by "a")
+    (tc/groups->map))
+
+
+

{

+ ++++ + + + + + + +
+
1
+
+
+

Group: 1 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
1a
1b
+
+
+ ++++ + + + + + + +
+
2
+
+
+

Group: 2 [2 2]:

+ + + + + + + + + + + + + + + + + +
ab
2c
2d
+
+
+

}

+
+
+

Grouping by more than one column. You can see that group names are maps. When ungrouping is done these maps are used to restore column names.

+
+
(tc/group-by DS [:V1 :V3] {:result-type :as-seq})
+
+
+

(

Group: {:V1 1, :V3 0.5} [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
170.5A
+

Group: {:V1 2, :V3 1.0} [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
281.0B
+

Group: {:V1 1, :V3 1.5} [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
191.5C
+

Group: {:V1 2, :V3 0.5} [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
+

Group: {:V1 1, :V3 1.0} [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
151.0B
+

Group: {:V1 2, :V3 1.5} [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
+

)

+
+
+

Grouping can be done by providing just row indexes. This way you can assign the same row to more than one group.

+
+
(tc/group-by DS {"group-a" [1 2 1 2]
+                  "group-b" [5 5 5 1]} {:result-type :as-seq})
+
+
+

(

Group: group-a [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
131.5C
221.0B
131.5C
+

Group: group-b [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
261.5C
261.5C
221.0B
+

)

+
+
+

You can group by a result of grouping function which gets row as map and should return group name. When map is used as a group name, ungrouping restore original column names.

+
+
(tc/group-by DS (fn [row] (* (:V1 row)
+                             (:V3 row))) {:result-type :as-seq})
+
+
+

(

Group: 0.5 [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
170.5A
+

Group: 2.0 [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
281.0B
+

Group: 1.5 [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
191.5C
+

Group: 1.0 [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
151.0B
+

Group: 3.0 [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
+

)

+
+
+

You can use any predicate on column to split dataset into two groups.

+
+
(tc/group-by DS (comp #(< % 1.0) :V3) {:result-type :as-seq})
+
+
+

(

Group: true [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
+

Group: false [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
131.5C
151.0B
261.5C
281.0B
191.5C
+

)

+
+
+

juxt is also helpful

+
+
(tc/group-by DS (juxt :V1 :V3) {:result-type :as-seq})
+
+
+

(

Group: [1 0.5] [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
170.5A
+

Group: [2 1.0] [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
281.0B
+

Group: [1 1.5] [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
191.5C
+

Group: [2 0.5] [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
+

Group: [1 1.0] [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
151.0B
+

Group: [2 1.5] [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
+

)

+
+
+

tech.ml.dataset provides an option to limit columns which are passed to grouping functions. It’s done for performance purposes.

+
+
(tc/group-by DS identity {:result-type :as-seq
+                           :select-keys [:V1]})
+
+
+

(

Group: {:V1 1} [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
151.0B
170.5A
191.5C
+

Group: {:V1 2} [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
240.5A
261.5C
281.0B
+

)

+
+
+
+

Ungrouping

+

Ungrouping simply concats all the groups into the dataset. Following options are possible

+
    +
  • :order? - order groups according to the group name ascending order. Default: false
  • +
  • :add-group-as-column - should group name become a column? If yes column is created with provided name (or :$group-name if argument is true). Default: nil.
  • +
  • :add-group-id-as-column - should group id become a column? If yes column is created with provided name (or :$group-id if argument is true). Default: nil.
  • +
  • :dataset-name - to name resulting dataset. Default: nil (_unnamed)
  • +
+

If group name is a map, it will be splitted into separate columns. Be sure that groups (subdatasets) doesn’t contain the same columns already.

+

If group name is a vector, it will be splitted into separate columns. If you want to name them, set vector of target column names as :add-group-as-column argument.

+

After ungrouping, order of the rows is kept within the groups but groups are ordered according to the internal storage.

+
+

Grouping and ungrouping.

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/ungroup))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
221.0B
151.0B
281.0B
131.5C
261.5C
191.5C
+
+
+

Groups sorted by group name and named.

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/ungroup {:order? true
+                  :dataset-name "Ordered by V3"}))
+
+
+

Ordered by V3 [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
221.0B
151.0B
281.0B
131.5C
261.5C
191.5C
+
+
+

Groups sorted descending by group name and named.

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/ungroup {:order? :desc
+                  :dataset-name "Ordered by V3 descending"}))
+
+
+

Ordered by V3 descending [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
261.5C
191.5C
221.0B
151.0B
281.0B
110.5A
240.5A
170.5A
+
+
+

Let’s add group name and id as additional columns

+
+
(-> DS
+    (tc/group-by (comp #(< % 4) :V2))
+    (tc/ungroup {:add-group-as-column true
+                  :add-group-id-as-column true}))
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:\(group-name | :\)group-id:V1:V2:V3:V4
true0110.5A
true0221.0B
true0131.5C
false1240.5A
false1151.0B
false1261.5C
false1170.5A
false1281.0B
false1191.5C
+
+
+

Let’s assign different column names

+
+
(-> DS
+    (tc/group-by (comp #(< % 4) :V2))
+    (tc/ungroup {:add-group-as-column "Is V2 less than 4?"
+                  :add-group-id-as-column "group id"}))
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Is V2 less than 4?group id:V1:V2:V3:V4
true0110.5A
true0221.0B
true0131.5C
false1240.5A
false1151.0B
false1261.5C
false1170.5A
false1281.0B
false1191.5C
+
+
+

If we group by map, we can automatically create new columns out of group names.

+
+
(-> DS
+    (tc/group-by (fn [row] {"V1 and V3 multiplied" (* (:V1 row)
+                                                      (:V3 row))
+                            "V4 as lowercase" (clojure.string/lower-case (:V4 row))}))
+    (tc/ungroup {:add-group-as-column true}))
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
V1 and V3 multipliedV4 as lowercase:V1:V2:V3:V4
0.5a110.5A
0.5a170.5A
2.0b221.0B
2.0b281.0B
1.5c131.5C
1.5c191.5C
1.0a240.5A
1.0b151.0B
3.0c261.5C
+
+
+

We can add group names without separation

+
+
(-> DS
+    (tc/group-by (fn [row] {"V1 and V3 multiplied" (* (:V1 row)
+                                                      (:V3 row))
+                            "V4 as lowercase" (clojure.string/lower-case (:V4 row))}))
+    (tc/ungroup {:add-group-as-column "just map"
+                  :separate? false}))
+
+
+

_unnamed [9 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
just map:V1:V2:V3:V4
{V1 and V3 multiplied 0.5, V4 as lowercase a}110.5A
{V1 and V3 multiplied 0.5, V4 as lowercase a}170.5A
{V1 and V3 multiplied 2.0, V4 as lowercase b}221.0B
{V1 and V3 multiplied 2.0, V4 as lowercase b}281.0B
{V1 and V3 multiplied 1.5, V4 as lowercase c}131.5C
{V1 and V3 multiplied 1.5, V4 as lowercase c}191.5C
{V1 and V3 multiplied 1.0, V4 as lowercase a}240.5A
{V1 and V3 multiplied 1.0, V4 as lowercase b}151.0B
{V1 and V3 multiplied 3.0, V4 as lowercase c}261.5C
+
+
+

The same applies to group names as sequences

+
+
(-> DS
+    (tc/group-by (juxt :V1 :V3))
+    (tc/ungroup {:add-group-as-column "abc"}))
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:abc-0:abc-1:V1:V2:V3:V4
10.5110.5A
10.5170.5A
21.0221.0B
21.0281.0B
11.5131.5C
11.5191.5C
20.5240.5A
11.0151.0B
21.5261.5C
+
+
+

Let’s provide column names

+
+
(-> DS
+    (tc/group-by (juxt :V1 :V3))
+    (tc/ungroup {:add-group-as-column ["v1" "v3"]}))
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
v1v3:V1:V2:V3:V4
10.5110.5A
10.5170.5A
21.0221.0B
21.0281.0B
11.5131.5C
11.5191.5C
20.5240.5A
11.0151.0B
21.5261.5C
+
+
+

Also we can supress separation

+
+
(-> DS
+    (tc/group-by (juxt :V1 :V3))
+    (tc/ungroup {:separate? false
+                 :add-group-as-column true}))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:$group-name:V1:V2:V3:V4
[1 0.5]110.5A
[1 0.5]170.5A
[2 1.0]221.0B
[2 1.0]281.0B
[1 1.5]131.5C
[1 1.5]191.5C
[2 0.5]240.5A
[1 1.0]151.0B
[2 1.5]261.5C
+
+
+
+

Other functions

+

To check if dataset is grouped or not just use grouped? function.

+
+
(tc/grouped? DS)
+
+
+
nil
+
+
+
(tc/grouped? (tc/group-by DS :V1))
+
+
+
true
+
+
+

If you want to remove grouping annotation (to make all the functions work as with regular dataset) you can use unmark-group or as-regular-dataset (alias) functions.

+

It can be important when you want to remove some groups (rows) from grouped dataset using drop-rows or something like that.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/as-regular-dataset)
+    (tc/grouped?))
+
+
+
nil
+
+

You can also operate on grouped dataset as a regular one in case you want to access its columns using without-grouping-> threading macro.

+
+
(-> DS
+    (tc/group-by [:V4 :V1])
+    (tc/without-grouping->
+     (tc/order-by (comp (juxt :V4 :V1) :name))))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
{:V4 A, :V1 1}0Group: {:V4 “A”, :V1 1} [2 4]:
{:V4 A, :V1 2}3Group: {:V4 “A”, :V1 2} [1 4]:
{:V4 B, :V1 1}4Group: {:V4 “B”, :V1 1} [1 4]:
{:V4 B, :V1 2}1Group: {:V4 “B”, :V1 2} [2 4]:
{:V4 C, :V1 1}2Group: {:V4 “C”, :V1 1} [2 4]:
{:V4 C, :V1 2}5Group: {:V4 “C”, :V1 2} [1 4]:
+
+
+

This is considered internal.

+

If you want to implement your own mapping function on grouped dataset you can call process-group-data and pass function operating on datasets. Result should be a dataset to have ungrouping working.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/process-group-data #(str "Shape: " (vector (tc/row-count %) (tc/column-count %))))
+    (tc/as-regular-dataset))
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
10Shape: [5 4]
21Shape: [4 4]
+
+
+
+
+

Columns

+

Column is a special tech.ml.dataset structure. For our purposes we cat treat columns as typed and named sequence bound to particular dataset.

+

Type of the data is inferred from a sequence during column creation.

+
+

Names

+

To select dataset columns or column names columns-selector is used. columns-selector can be one of the following:

+
    +
  • :all keyword - selects all columns
  • +
  • column name - for single column
  • +
  • sequence of column names - for collection of columns
  • +
  • regex - to apply pattern on column names or datatype
  • +
  • filter predicate - to filter column names or datatype
  • +
  • type namespaced keyword for specific datatype or group of datatypes
  • +
+

Column name can be anything.

+

column-names function returns names according to columns-selector and optional meta-field. meta-field is one of the following:

+
    +
  • :name (default) - to operate on column names
  • +
  • :datatype - to operated on column types
  • +
  • :all - if you want to process all metadata
  • +
+

Datatype groups are:

+
    +
  • :type/numerical - any numerical type
  • +
  • :type/float - floating point number (:float32 and :float64)
  • +
  • :type/integer - any integer
  • +
  • :type/datetime - any datetime type
  • +
+

If qualified keyword starts with :!type, complement set is used.

+
+

To select all column names you can use column-names function.

+
+
(tc/column-names DS)
+
+
+
(:V1 :V2 :V3 :V4)
+
+

or

+
+
(tc/column-names DS :all)
+
+
+
(:V1 :V2 :V3 :V4)
+
+

In case you want to select column which has name :all (or is sequence or map), put it into a vector. Below code returns empty sequence since there is no such column in the dataset.

+
+
(tc/column-names DS [:all])
+
+
+
()
+
+
+

Obviously selecting single name returns it’s name if available

+
+
(tc/column-names DS :V1)
+
+
+
(:V1)
+
+
+
(tc/column-names DS "no such column")
+
+
+
()
+
+
+

Select sequence of column names.

+
+
(tc/column-names DS [:V1 "V2" :V3 :V4 :V5])
+
+
+
(:V1 :V3 :V4)
+
+
+

Select names based on regex, columns ends with 1 or 4

+
+
(tc/column-names DS #".*[14]")
+
+
+
(:V1 :V4)
+
+
+

Select names based on regex operating on type of the column (to check what are the column types, call (tc/info DS :columns). Here we want to get integer columns only.

+
+
(tc/column-names DS #"^:int.*" :datatype)
+
+
+
(:V1 :V2)
+
+

or

+
+
(tc/column-names DS :type/integer)
+
+
+
(:V1 :V2)
+
+
+

And finally we can use predicate to select names. Let’s select double precision columns.

+
+
(tc/column-names DS #{:float64} :datatype)
+
+
+
(:V3)
+
+

or

+
+
(tc/column-names DS :type/float64)
+
+
+
(:V3)
+
+
+

If you want to select all columns but given, use complement function. Works only on a predicate.

+
+
(tc/column-names DS (complement #{:V1}))
+
+
+
(:V2 :V3 :V4)
+
+
+
(tc/column-names DS (complement #{:float64}) :datatype)
+
+
+
(:V1 :V2 :V4)
+
+
+
(tc/column-names DS :!type/float64)
+
+
+
(:V1 :V2 :V4)
+
+
+

You can select column names based on all column metadata at once by using :all metadata selector. Below we want to select column names ending with 1 which have long datatype.

+
+
(tc/column-names DS (fn [meta]
+                       (and (= :int64 (:datatype meta))
+                            (clojure.string/ends-with? (:name meta) "1"))) :all)
+
+
+
(:V1)
+
+
+
+

Select

+

select-columns creates dataset with columns selected by columns-selector as described above. Function works on regular and grouped dataset.

+
+

Select only float64 columns

+
+
(tc/select-columns DS #(= :float64 %) :datatype)
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3
0.5
1.0
1.5
0.5
1.0
1.5
0.5
1.0
1.5
+
+

or

+
+
(tc/select-columns DS :type/float64)
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3
0.5
1.0
1.5
0.5
1.0
1.5
0.5
1.0
1.5
+
+
+

Select all but :V1 columns

+
+
(tc/select-columns DS (complement #{:V1}))
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2:V3:V4
10.5A
21.0B
31.5C
40.5A
51.0B
61.5C
70.5A
81.0B
91.5C
+
+
+

If we have grouped data set, column selection is applied to every group separately.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/select-columns [:V2 :V3])
+    (tc/groups->map))
+
+
+

{

+ ++++ + + + + + + +
+
1
+
+
+

Group: 1 [5 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2:V3
10.5
31.5
51.0
70.5
91.5
+
+
+ ++++ + + + + + + +
+
2
+
+
+

Group: 2 [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V2:V3
21.0
40.5
61.5
81.0
+
+
+

}

+
+
+
+

Drop

+

drop-columns creates dataset with removed columns.

+
+

Drop float64 columns

+
+
(tc/drop-columns DS #(= :float64 %) :datatype)
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
11A
22B
13C
24A
15B
26C
17A
28B
19C
+
+

or

+
+
(tc/drop-columns DS :type/float64)
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
11A
22B
13C
24A
15B
26C
17A
28B
19C
+
+
+

Drop all columns but :V1 and :V2

+
+
(tc/drop-columns DS (complement #{:V1 :V2}))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
11
22
13
24
15
26
17
28
19
+
+
+

If we have grouped data set, column selection is applied to every group separately. Selected columns are dropped.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/drop-columns [:V2 :V3])
+    (tc/groups->map))
+
+
+

{

+ ++++ + + + + + + +
+
1
+
+
+

Group: 1 [5 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V4
1A
1C
1B
1A
1C
+
+
+ ++++ + + + + + + +
+
2
+
+
+

Group: 2 [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V4
2B
2A
2C
2B
+
+
+

}

+
+
+
+

Rename

+

If you want to rename colums use rename-columns and pass map where keys are old names, values new ones.

+

You can also pass mapping function with optional columns-selector

+
+
(tc/rename-columns DS {:V1 "v1"
+                        :V2 "v2"
+                        :V3 [1 2 3]
+                        :V4 (Object.)})
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
v1v2[1 2 3]java.lang.Object@4c9d0e30
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+

Map all names with function

+
+
(tc/rename-columns DS (comp str second name))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1234
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+

Map selected names with function

+
+
(tc/rename-columns DS [:V1 :V3] (comp str second name))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1:V23:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+

Function works on grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/rename-columns {:V1 "v1"
+                         :V2 "v2"
+                         :V3 [1 2 3]
+                         :V4 (Object.)})
+    (tc/groups->map))
+
+
+

{

+ ++++ + + + + + + +
+
1
+
+
+

Group: 1 [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
v1v2[1 2 3]java.lang.Object@72cbadb0
110.5A
131.5C
151.0B
170.5A
191.5C
+
+
+ ++++ + + + + + + +
+
2
+
+
+

Group: 2 [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
v1v2[1 2 3]java.lang.Object@72cbadb0
221.0B
240.5A
261.5C
281.0B
+
+
+

}

+
+
+
+

Add or update

+

To add (or replace existing) column call add-column function. Function accepts:

+
    +
  • ds - a dataset
  • +
  • column-name - if it’s existing column name, column will be replaced
  • +
  • column - can be column (from other dataset), sequence, single value or function. Too big columns are always trimmed. Too small are cycled or extended with missing values (according to size-strategy argument)
  • +
  • size-strategy (optional) - when new column is shorter than dataset row count, following strategies are applied: +
      +
    • :cycle - repeat data
    • +
    • :na - append missing values
    • +
    • :strict - (default) throws an exception when sizes mismatch
    • +
  • +
+

Function works on grouped dataset.

+
+

Add single value as column

+
+
(tc/add-column DS :V5 "X")
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5AX
221.0BX
131.5CX
240.5AX
151.0BX
261.5CX
170.5AX
281.0BX
191.5CX
+
+
+

Replace one column (column is trimmed)

+
+
^:note-to-test/skip
+(tc/add-column DS :V1 (repeatedly rand))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
0.1271991410.5A
0.2595117921.0B
0.4220261431.5C
0.6434104440.5A
0.6635684051.0B
0.8823152461.5C
0.4080726970.5A
0.6688459481.0B
0.4334853791.5C
+
+
+

Copy column

+
+
(tc/add-column DS :V5 (DS :V1))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5A1
221.0B2
131.5C1
240.5A2
151.0B1
261.5C2
170.5A1
281.0B2
191.5C1
+
+
+

When function is used, argument is whole dataset and the result should be column, sequence or single value

+
+
(tc/add-column DS :row-count tc/row-count)
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:row-count
110.5A9
221.0B9
131.5C9
240.5A9
151.0B9
261.5C9
170.5A9
281.0B9
191.5C9
+
+
+

Above example run on grouped dataset, applies function on each group separately.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/add-column :row-count tc/row-count)
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:row-count
110.5A5
131.5C5
151.0B5
170.5A5
191.5C5
221.0B4
240.5A4
261.5C4
281.0B4
+
+
+

When column which is added is longer than row count in dataset, column is trimmed. When column is shorter, it’s cycled or missing values are appended.

+
+
(tc/add-column DS :V5 [:r :b] :cycle)
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5A:r
221.0B:b
131.5C:r
240.5A:b
151.0B:r
261.5C:b
170.5A:r
281.0B:b
191.5C:r
+
+
+
(tc/add-column DS :V5 [:r :b] :na)
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5A:r
221.0B:b
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+

Exception is thrown when :strict (default) strategy is used and column size is not equal row count

+
+
(try
+  (tc/add-column DS :V5 [:r :b])
+  (catch Exception e (str "Exception caught: "(ex-message e))))
+
+
+
"Exception caught: Column size (2) should be exactly the same as dataset row count (9). Consider `:cycle` or `:na` strategy."
+
+
+

Tha same applies for grouped dataset

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/add-column :V5 [:r :b] :na)
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5A:r
240.5A:b
170.5A
221.0B:r
151.0B:b
281.0B
131.5C:r
261.5C:b
191.5C
+
+
+

Let’s use other column to fill groups

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/add-column :V5 (DS :V2) :cycle)
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
110.5A1
240.5A2
170.5A3
221.0B1
151.0B2
281.0B3
131.5C1
261.5C2
191.5C3
+
+
+

In case you want to add or update several columns you can call add-columns and provide map where keys are column names, vals are columns.

+
+
(tc/add-columns DS {:V1 #(map inc (% :V1))
+                               :V5 #(map (comp keyword str) (% :V4))
+                               :V6 11})
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5:V6
210.5A:A11
321.0B:B11
231.5C:C11
340.5A:A11
251.0B:B11
361.5C:C11
270.5A:A11
381.0B:B11
291.5C:C11
+
+
+
+

Update

+

If you want to modify specific column(s) you can call update-columns. Arguments:

+
    +
  • dataset
  • +
  • one of: +
      +
    • columns-selector and function (or sequence of functions)
    • +
    • map where keys are column names and vals are function
    • +
  • +
+

Functions accept column and have to return column or sequence

+
+

Reverse of columns

+
+
(tc/update-columns DS :all reverse)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
281.0B
170.5A
261.5C
151.0B
240.5A
131.5C
221.0B
110.5A
+
+
+

Apply dec/inc on numerical columns

+
+
(tc/update-columns DS :type/numerical [(partial map dec)
+                                        (partial map inc)])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
02-0.5A
130.0B
040.5C
15-0.5A
060.0B
170.5C
08-0.5A
190.0B
0100.5C
+
+
+

You can also assign a function to a column by packing operations into the map.

+
+
^:note-to-test/skip
+(tc/update-columns DS {:V1 reverse
+                        :V2 (comp shuffle seq)})
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
150.5A
211.0B
121.5C
270.5A
191.0B
241.5C
130.5A
281.0B
161.5C
+
+
+
+

Map

+

The other way of creating or updating column is to map rows as regular map function. The arity of mapping function should be the same as number of selected columns.

+

Arguments:

+
    +
  • ds - dataset
  • +
  • column-name - target column name
  • +
  • columns-selector - columns selected
  • +
  • map-fn - mapping function
  • +
+
+

Let’s add numerical columns together

+
+
(tc/map-columns DS
+                 :sum-of-numbers
+                 (tc/column-names DS  #{:int64 :float64} :datatype)
+                 (fn [& rows]
+                   (reduce + rows)))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:sum-of-numbers
110.5A2.5
221.0B5.0
131.5C5.5
240.5A6.5
151.0B7.0
261.5C9.5
170.5A8.5
281.0B11.0
191.5C11.5
+
+

The same works on grouped dataset

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/map-columns :sum-of-numbers
+                     (tc/column-names DS  #{:int64 :float64} :datatype)
+                     (fn [& rows]
+                       (reduce + rows)))
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:sum-of-numbers
110.5A2.5
240.5A6.5
170.5A8.5
221.0B5.0
151.0B7.0
281.0B11.0
131.5C5.5
261.5C9.5
191.5C11.5
+
+
+
+

Reorder

+

To reorder columns use columns selectors to choose what columns go first. The unseleted columns are appended to the end.

+
+
(tc/reorder-columns DS :V4 [:V3 :V2])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V3:V2:V1
A0.511
B1.022
C1.531
A0.542
B1.051
C1.562
A0.571
B1.082
C1.591
+
+
+

This function doesn’t let you select meta field, so you have to call column-names in such case. Below we want to add integer columns at the end.

+
+
(tc/reorder-columns DS (tc/column-names DS (complement #{:int64}) :datatype))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:V4:V1:V2
0.5A11
1.0B22
1.5C13
0.5A24
1.0B15
1.5C26
0.5A17
1.0B28
1.5C19
+
+
+
+

Type conversion

+

To convert column into given datatype can be done using convert-types function. Not all the types can be converted automatically also some types require slow parsing (every conversion from string). In case where conversion is not possible you can pass conversion function.

+

Arguments:

+
    +
  • ds - dataset
  • +
  • Two options: +
      +
    • coltype-map in case when you want to convert several columns, keys are column names, vals are new types
    • +
    • column-selector and new-types - column name and new datatype (or datatypes as sequence)
    • +
  • +
+

new-types can be:

+
    +
  • a type like :int64 or :string or sequence of types
  • +
  • or sequence of pair of datetype and conversion function
  • +
+

After conversion additional infomation is given on problematic values.

+

The other conversion is casting column into java array (->array) of the type column or provided as argument. Grouped dataset returns sequence of arrays.

+
+

Basic conversion

+
+
(-> DS
+    (tc/convert-types :V1 :float64)
+    (tc/info :columns))
+
+
+

_unnamed :column info [4 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:datatype:n-elems:unparsed-indexes:unparsed-data:categorical?
:V1:float649{}[]
:V2:int649
:V3:float649
:V4:string9true
+
+
+

Using custom converter. Let’s treat :V4 as haxadecimal values. See that this way we can map column to any value.

+
+
(-> DS
+    (tc/convert-types :V4 [[:int16 #(Integer/parseInt % 16)]]))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.510
221.011
131.512
240.510
151.011
261.512
170.510
281.011
191.512
+
+
+

You can process several columns at once

+
+
(-> DS
+    (tc/convert-types {:V1 :float64
+                        :V2 :object
+                        :V3 [:boolean #(< % 1.0)]
+                        :V4 :object})
+    (tc/info :columns))
+
+
+

_unnamed :column info [4 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:datatype:n-elems:unparsed-indexes:unparsed-data:categorical?
:V1:float649{}[]
:V2:object9{}[]true
:V3:boolean9{}[]
:V4:object9true
+
+
+

Convert one type into another

+
+
(-> DS
+    (tc/convert-types :type/numerical :int16)
+    (tc/info :columns))
+
+
+

_unnamed :column info [4 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:datatype:n-elems:unparsed-indexes:unparsed-data:categorical?
:V1:int169{}[]
:V2:int169{}[]
:V3:int169{}[]
:V4:string9true
+
+
+

Function works on the grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/convert-types :V1 :float32)
+    (tc/ungroup)
+    (tc/info :columns))
+
+
+

_unnamed :column info [4 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:datatype:n-elems:unparsed-indexes:unparsed-data:categorical?
:V1:float329{}[]
:V2:int649
:V3:float649
:V4:string9true
+
+
+

Double array conversion.

+
+
(tc/->array DS :V1)
+
+
+
[1, 2, 1, 2, 1, 2, 1, 2, 1]
+
+
+

Function also works on grouped dataset

+
+
(-> DS
+    (tc/group-by :V3)
+    (tc/->array :V2))
+
+
+
([1, 4, 7] [2, 5, 8] [3, 6, 9])
+
+
+

You can also cast the type to the other one (if casting is possible):

+
+
(tc/->array DS :V4 :string)
+
+
+
["A", "B", "C", "A", "B", "C", "A", "B", "C"]
+
+
+
(tc/->array DS :V1 :float32)
+
+
+
[1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0]
+
+
+
+
+

Column Operations

+

There are a large number of column operations that can be performed on the columns in your dataset. These operations are a similar set as the Column API operations, but instead of operating directly on columns, they take a Dataset and a columns-selector.

+

The behavior of the operations differ based on their return value:

+
    +
  • If an operation would return a scalar value, then the function behaves like an aggregator and can be used in group-by expresesions. Such functions will have the general signature:

    +
    (dataset columns-selector) => dataset
  • +
  • If an operation would return another column, then the function will not behave like an aggregator. Instead, it asks you to specify a target column, which it will add to the dataset that it returns. The signature of these functions is:

    +
    (ds target-col columns-selector) => dataset
  • +
+

As there are a large number of operations, we will illustrate their usage. To begin with, here are some examples of operations whose result is a new column:

+
+
DS
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+
(-> DS
+    (tc/+ :SUM [:V1 :V2]))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:SUM
110.5A2
221.0B4
131.5C4
240.5A6
151.0B6
261.5C8
170.5A8
281.0B10
191.5C10
+
+
+
(-> DS
+    (tc/* :MULTIPY [:V1 :V2]))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:MULTIPY
110.5A1
221.0B4
131.5C3
240.5A8
151.0B5
261.5C12
170.5A7
281.0B16
191.5C9
+
+

Now let’s look at operations that would return a scalar:

+
+
(-> DS
+    (tc/mean [:V1]))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
summary
1.44444444
+
+

Notice that we did not supply a target column to the mean function. Since mean does not return a column, we do not provide this argument. Instead, we simply provide the dataset and a columns-selector. We then get back a dataset with the result.

+

Now let’s use this function within a grouping expression:

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/mean [:V2]))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4summary
A4.0
B5.0
C6.0
+
+

In this example, we grouped DS and then passed the resulting grouped Dataset directly to the mean operation. Since mean returns a scalar, it operates as an aggregator, summarizing the results of each group.

+
+
+

Rows

+

Rows can be selected or dropped using various selectors:

+
    +
  • row id(s) - row index as number or seqence of numbers (first row has index 0, second 1 and so on)
  • +
  • sequence of true/false values
  • +
  • filter by predicate (argument is row as a map)
  • +
+

When predicate is used you may want to limit columns passed to the function (select-keys option).

+

Additionally you may want to precalculate some values which will be visible for predicate as additional columns. It’s done internally by calling add-columns on a dataset. :pre is used as a column definitions.

+
+

Select

+

Select fifth row

+
+
(tc/select-rows DS 4)
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
151.0B
+
+
+

Select 3 rows

+
+
(tc/select-rows DS [1 4 5])
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
151.0B
261.5C
+
+
+

Select rows using sequence of true/false values

+
+
(tc/select-rows DS [true nil nil true])
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
+
+
+

Select rows using predicate

+
+
(tc/select-rows DS (comp #(< % 1) :V3))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
+
+
+

The same works on grouped dataset, let’s select first row from every group.

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/select-rows 0)
+    (tc/ungroup))
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
+
+
+

If you want to select :V2 values which are lower than or equal mean in grouped dataset you have to precalculate it using :pre.

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/select-rows (fn [row] (<= (:V2 row) (:mean row)))
+                     {:pre {:mean #(tech.v3.datatype.functional/mean (% :V2))}})
+    (tc/ungroup))
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
221.0B
151.0B
131.5C
261.5C
+
+
+
+

Drop

+

drop-rows removes rows, and accepts exactly the same parameters as select-rows

+
+

Drop values lower than or equal :V2 column mean in grouped dataset.

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/drop-rows (fn [row] (<= (:V2 row) (:mean row)))
+                   {:pre {:mean #(tech.v3.datatype.functional/mean (% :V2))}})
+    (tc/ungroup))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
170.5A
281.0B
191.5C
+
+
+
+

Map rows

+

Call a mapping function for every row. Mapping function should return a map, where keys are column names (new or old) and values are column values.

+

Works on grouped dataset too.

+
+
(tc/map-rows DS (fn [{:keys [V1 V2]}] {:V1 0
+                                       :V5 (/ (+ V1 V2) (double V2))}))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:V5
010.5A2.00000000
021.0B2.00000000
031.5C1.33333333
040.5A1.50000000
051.0B1.20000000
061.5C1.33333333
070.5A1.14285714
081.0B1.25000000
091.5C1.11111111
+
+
+
+

Other

+

There are several function to select first, last, random rows, or display head, tail of the dataset. All functions work on grouped dataset.

+

All random functions accept :seed as an option if you want to fix returned result.

+
+

First row

+
+
(tc/first DS)
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
+
+
+

Last row

+
+
(tc/last DS)
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
+
+
+

Random row (single)

+
+
^:note-to-test/skip
+(tc/rand-nth DS)
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
+
+
+

Random row (single) with seed

+
+
(tc/rand-nth DS {:seed 42})
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
+
+
+

Random n (default: row count) rows with repetition.

+
+
^:note-to-test/skip
+(tc/random DS)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
281.0B
131.5C
110.5A
240.5A
131.5C
240.5A
281.0B
281.0B
+
+
+

Five random rows with repetition

+
+
^:note-to-test/skip
+(tc/random DS 5)
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
281.0B
240.5A
240.5A
281.0B
170.5A
+
+
+

Five random, non-repeating rows

+
+
^:note-to-test/skip
+(tc/random DS 5 {:repeat? false})
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
131.5C
170.5A
261.5C
110.5A
+
+
+

Five random, with seed

+
+
(tc/random DS 5 {:seed 42})
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
151.0B
131.5C
110.5A
191.5C
+
+
+

Shuffle dataset

+
+
^:note-to-test/skip
+(tc/shuffle DS)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
240.5A
151.0B
110.5A
170.5A
131.5C
191.5C
221.0B
281.0B
+
+
+

Shuffle with seed

+
+
(tc/shuffle DS {:seed 42})
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
151.0B
221.0B
261.5C
240.5A
281.0B
131.5C
170.5A
110.5A
191.5C
+
+
+

First n rows (default 5)

+
+
(tc/head DS)
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
+
+
+

Last n rows (default 5)

+
+
(tc/tail DS)
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+

by-rank calculates rank on column(s). It’s base on R rank() with addition of :dense (default) tie strategy which give consecutive rank numbering.

+

:desc? options (default: true) sorts input with descending order, giving top values under 0 value.

+

rank is zero based and is defined at tablecloth.api.utils namespace.

+
+
+
(tc/by-rank DS :V3 zero?)
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
261.5C
191.5C
+
+

most V3 values

+
+
(tc/by-rank DS :V3 zero? {:desc? false})
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
+
+

least V3 values

+
+

Rank also works on multiple columns

+
+
(tc/by-rank DS [:V1 :V3] zero? {:desc? false})
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
170.5A
+
+
+

Select 5 random rows from each group

+
+
^:note-to-test/skip
+(-> DS
+    (tc/group-by :V4)
+    (tc/random 5)
+    (tc/ungroup))
+
+
+

_unnamed [15 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
110.5A
240.5A
110.5A
110.5A
221.0B
281.0B
151.0B
281.0B
281.0B
131.5C
191.5C
131.5C
191.5C
131.5C
+
+
+
+
+

Aggregate

+

Aggregating is a function which produces single row out of dataset.

+

Aggregator is a function or sequence or map of functions which accept dataset as an argument and result single value, sequence of values or map.

+

Where map is given as an input or result, keys are treated as column names.

+

Grouped dataset is ungrouped after aggreation. This can be turned off by setting :ungroup to false. In case you want to pass additional ungrouping parameters add them to the options.

+

By default resulting column names are prefixed with summary prefix (set it with :default-column-name-prefix option).

+
+

Let’s calculate mean of some columns

+
+
(tc/aggregate DS #(reduce + (% :V2)))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
summary
45
+
+
+

Let’s give resulting column a name.

+
+
(tc/aggregate DS {:sum-of-V2 #(reduce + (% :V2))})
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:sum-of-V2
45
+
+
+

Sequential result is spread into separate columns

+
+
(tc/aggregate DS #(take 5(% :V2)))
+
+
+

_unnamed [1 5]:

+ + + + + + + + + + + + + + + + + + + +
:summary-0:summary-1:summary-2:summary-3:summary-4
12345
+
+
+

You can combine all variants and rename default prefix

+
+
(tc/aggregate DS [#(take 3 (% :V2))
+                   (fn [ds] {:sum-v1 (reduce + (ds :V1))
+                            :prod-v3 (reduce * (ds :V3))})] {:default-column-name-prefix "V2-value"})
+
+
+

_unnamed [1 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + +
:V2-value-0-0:V2-value-0-1:V2-value-0-2:V2-value-1-sum-v1:V2-value-1-prod-v3
123130.421875
+
+
+

Processing grouped dataset

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate [#(take 3 (% :V2))
+                    (fn [ds] {:sum-v1 (reduce + (ds :V1))
+                             :prod-v3 (reduce * (ds :V3))})] {:default-column-name-prefix "V2-value"}))
+
+
+

_unnamed [3 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V2-value-0-0:V2-value-0-1:V2-value-0-2:V2-value-1-sum-v1:V2-value-1-prod-v3
A14740.125
B25851.000
C36943.375
+
+

Result of aggregating is automatically ungrouped, you can skip this step by stetting :ungroup option to false.

+
+
(-> DS
+    (tc/group-by [:V3])
+    (tc/aggregate [#(take 3 (% :V2))
+                    (fn [ds] {:sum-v1 (reduce + (ds :V1))
+                             :prod-v3 (reduce * (ds :V3))})] {:default-column-name-prefix "V2-value"
+                                                              :ungroup? false}))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
{:V3 0.5}0_unnamed [1 5]:
{:V3 1.0}1_unnamed [1 5]:
{:V3 1.5}2_unnamed [1 5]:
+
+
+

Column

+

You can perform columnar aggreagation also. aggregate-columns selects columns and apply aggregating function (or sequence of functions) for each column separately.

+
+
(tc/aggregate-columns DS [:V1 :V2 :V3] #(reduce + %))
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V3
13459.0
+
+
+
+
(tc/aggregate-columns DS [:V1 :V2 :V3] [#(reduce + %)
+                                         #(reduce max %)
+                                         #(reduce * %)])
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V3
1390.421875
+
+
+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns [:V1 :V2 :V3] #(reduce + %)))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A4121.5
B5153.0
C4184.5
+
+

You can also aggregate whole dataset

+
+
(-> DS
+    (tc/drop-columns :V4)
+    (tc/aggregate-columns #(reduce + %)))
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V3
13459.0
+
+
+
+

Crosstab

+

Cross tabulation built from two sets of columns. First rows and cols are used to construct grouped dataset, then aggregation function is applied for each pair. By default it counts rows from each group.

+

Options are:

+
    +
  • :aggregator - function which aggregates values of grouped dataset, default it’s row-count
  • +
  • :marginal-rows and :marginal-cols - if true, sum of rows and cols are added as an additional columns and row. May be custom function which accepts pure row and col as a seq.
  • +
  • :replace-missing? - should missing values be replaced (default: true) with :missing-value (default: 0)
  • +
  • :pivot? - if false, flat aggregation result is returned (default: false)
  • +
+
+
(def ctds (tc/dataset {:a [:foo :foo :bar :bar :foo :foo]
+                       :b [:one :one :two :one :two :one]
+                       :c [:dull :dull :shiny :dull :dull :shiny]}))
+
+
+
ctds
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
:foo:one:dull
:foo:one:dull
:bar:two:shiny
:bar:one:dull
:foo:two:dull
:foo:one:shiny
+
+
+
+
(tc/crosstab ctds :a [:b :c])
+
+
+

_unnamed [2 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
rows/cols[:one :dull][:two :shiny][:two :dull][:one :shiny]
:foo2011
:bar1100
+
+
+

With marginals

+
+
(tc/crosstab ctds :a [:b :c] {:marginal-rows true :marginal-cols true})
+
+
+

_unnamed [3 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
rows/cols[:one :dull][:two :shiny][:two :dull][:one :shiny]:summary
:foo20114
:bar11002
:summary31116
+
+
+

Set missing value to -1

+
+
(tc/crosstab ctds :a [:b :c] {:missing-value -1})
+
+
+

_unnamed [2 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
rows/cols[:one :dull][:two :shiny][:two :dull][:one :shiny]
:foo2-111
:bar11-1-1
+
+
+

Turn off pivoting

+
+
(tc/crosstab ctds :a [:b :c] {:pivot? false})
+
+
+

_unnamed [5 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:rows:colssummary
:foo[:one :dull]2
:bar[:two :shiny]1
:bar[:one :dull]1
:foo[:two :dull]1
:foo[:one :shiny]1
+
+
+
+
+

Order

+

Ordering can be done by column(s) or any function operating on row. Possible order can be:

+
    +
  • :asc for ascending order (default)
  • +
  • :desc for descending order
  • +
  • custom comparator
  • +
+

:select-keys limits row map provided to ordering functions.

+
+

Order by single column, ascending

+
+
(tc/order-by DS :V1)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
151.0B
170.5A
191.5C
261.5C
240.5A
281.0B
221.0B
+
+
+

Descending order

+
+
(tc/order-by DS :V1 :desc)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
240.5A
261.5C
281.0B
151.0B
131.5C
170.5A
110.5A
191.5C
+
+
+

Order by two columns

+
+
(tc/order-by DS [:V1 :V2])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
151.0B
170.5A
191.5C
221.0B
240.5A
261.5C
281.0B
+
+
+

Use different orders for columns

+
+
(tc/order-by DS [:V1 :V2] [:asc :desc])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
170.5A
151.0B
131.5C
110.5A
281.0B
261.5C
240.5A
221.0B
+
+
+
(tc/order-by DS [:V1 :V2] [:desc :desc])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
281.0B
261.5C
240.5A
221.0B
191.5C
170.5A
151.0B
131.5C
110.5A
+
+
+
(tc/order-by DS [:V1 :V3] [:desc :asc])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
221.0B
281.0B
261.5C
110.5A
170.5A
151.0B
131.5C
191.5C
+
+
+

Custom function can be used to provided ordering key. Here order by :V4 descending, then by product of other columns ascending.

+
+
(tc/order-by DS [:V4 (fn [row] (* (:V1 row)
+                                  (:V2 row)
+                                  (:V3 row)))] [:desc :asc])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
191.5C
261.5C
221.0B
151.0B
281.0B
110.5A
170.5A
240.5A
+
+
+

Custom comparator also can be used in case objects are not comparable by default. Let’s define artificial one: if Euclidean distance is lower than 2, compare along z else along x and y. We use first three columns for that.

+
+
(defn dist
+  [v1 v2]
+  (->> v2
+       (map - v1)
+       (map #(* % %))
+       (reduce +)
+       (Math/sqrt)))
+
+
+
(tc/order-by DS [:V1 :V2 :V3] (fn [[x1 y1 z1 :as v1] [x2 y2 z2 :as v2]]
+                                (let [d (dist v1 v2)]
+                                  (if (< d 2.0)
+                                    (compare z1 z2)
+                                    (compare [x1 y1] [x2 y2])))))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
151.0B
170.5A
191.5C
221.0B
240.5A
131.5C
261.5C
281.0B
+
+
+
+

Unique

+

Remove rows which contains the same data. By default unique-by removes duplicates from whole dataset. You can also pass list of columns or functions (similar as in group-by) to remove duplicates limited by them. Default strategy is to keep the first row. More strategies below.

+

unique-by works on groups

+
+

Remove duplicates from whole dataset

+
+
(tc/unique-by DS)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+

Remove duplicates from each group selected by column.

+
+
(tc/unique-by DS :V1)
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
+
+
+

Pair of columns

+
+
(tc/unique-by DS [:V1 :V3])
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
+
+
+

Also function can be used, split dataset by modulo 3 on columns :V2

+
+
(tc/unique-by DS (fn [m] (mod (:V2 m) 3)))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
+
+
+

The same can be achived with group-by

+
+
(-> DS
+    (tc/group-by (fn [m] (mod (:V2 m) 3)))
+    (tc/first)
+    (tc/ungroup))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
+
+
+

Grouped dataset

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/unique-by :V1)
+    (tc/ungroup))
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
221.0B
151.0B
131.5C
261.5C
+
+
+

Strategies

+

There are 4 strategies defined:

+
    +
  • :first - select first row (default)
  • +
  • :last - select last row
  • +
  • :random - select random row
  • +
  • any function - apply function to a columns which are subject of uniqueness
  • +
+
+

Last

+
+
(tc/unique-by DS :V1 {:strategy :last})
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
281.0B
191.5C
+
+
+

Random

+
+
^:note-to-test/skip
+(tc/unique-by DS :V1 {:strategy :random})
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
170.5A
+
+
+

Pack columns into vector

+
+
(tc/unique-by DS :V4 {:strategy vec})
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3
[1 2 1][1 4 7][0.5 0.5 0.5]
[2 1 2][2 5 8][1.0 1.0 1.0]
[1 2 1][3 6 9][1.5 1.5 1.5]
+
+
+

Sum columns

+
+
(tc/unique-by DS :V4 {:strategy (partial reduce +)})
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3
4121.5
5153.0
4184.5
+
+
+

Group by function and apply functions

+
+
(tc/unique-by DS (fn [m] (mod (:V2 m) 3)) {:strategy vec})
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
[1 2 1][1 4 7][0.5 0.5 0.5][A A A]
[2 1 2][2 5 8][1.0 1.0 1.0][B B B]
[1 2 1][3 6 9][1.5 1.5 1.5][C C C]
+
+
+

Grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/unique-by (fn [m] (mod (:V2 m) 3)) {:strategy vec})
+    (tc/ungroup {:add-group-as-column :from-V1}))
+
+
+

_unnamed [6 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:from-V1:V1:V2:V3:V4
1[1 1][1 7][0.5 0.5][A A]
1[1 1][3 9][1.5 1.5][C C]
1[1][5][1.0][B]
2[2 2][2 8][1.0 1.0][B B]
2[2][4][0.5][A]
2[2][6][1.5][C]
+
+
+
+
+

Missing

+

When dataset contains missing values you can select or drop rows with missing values or replace them using some strategy.

+

column-selector can be used to limit considered columns

+

Let’s define dataset which contains missing values

+
+
(def DSm (tc/dataset {:V1 (take 9 (cycle [1 2 nil]))
+                      :V2 (range 1 10)
+                      :V3 (take 9 (cycle [0.5 1.0 nil 1.5]))
+                      :V4 (take 9 (cycle ["A" "B" "C"]))}))
+
+
+
DSm
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
3C
141.5A
250.5B
61.0C
17A
281.5B
90.5C
+
+
+

Select

+

Select rows with missing values

+
+
(tc/select-missing DSm)
+
+
+

_unnamed [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
3C
61.0C
17A
90.5C
+
+
+

Select rows with missing values in :V1

+
+
(tc/select-missing DSm :V1)
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
3C
61.0C
90.5C
+
+
+

The same with grouped dataset

+
+
(-> DSm
+    (tc/group-by :V4)
+    (tc/select-missing :V3)
+    (tc/ungroup))
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
17A
3C
+
+
+
+

Drop

+

Drop rows with missing values

+
+
(tc/drop-missing DSm)
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
141.5A
250.5B
281.5B
+
+
+

Drop rows with missing values in :V1

+
+
(tc/drop-missing DSm :V1)
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
141.5A
250.5B
17A
281.5B
+
+
+

The same with grouped dataset

+
+
(-> DSm
+    (tc/group-by :V4)
+    (tc/drop-missing :V1)
+    (tc/ungroup))
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
141.5A
17A
221.0B
250.5B
281.5B
+
+
+
+

Replace

+

Missing values can be replaced using several strategies. replace-missing accepts:

+
    +
  • dataset
  • +
  • column selector, default: :all
  • +
  • strategy, default: :nearest
  • +
  • value (optional) +
      +
    • single value
    • +
    • sequence of values (cycled)
    • +
    • function, applied on column(s) with stripped missings
    • +
    • map with [index,value] pairs
    • +
  • +
+

Strategies are:

+
    +
  • :value - replace with given value
  • +
  • :up - copy values up
  • +
  • :down - copy values down
  • +
  • :updown - copy values up and then down for missing values at the end
  • +
  • :downup - copy values down and then up for missing values at the beginning
  • +
  • :mid or :nearest - copy values around known values
  • +
  • :midpoint - use average value from previous and next non-missing
  • +
  • :lerp - trying to lineary approximate values, works for numbers and datetime, otherwise applies :nearest. For numbers always results in float datatype.
  • +
+

Let’s define special dataset here:

+
+
(def DSm2 (tc/dataset {:a [nil nil nil 1.0 2  nil nil nil nil  nil 4   nil  11 nil nil]
+                       :b [2   2   2 nil nil nil nil nil nil 13   nil   3  4  5 5]}))
+
+
+
DSm2
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
2
2
2
1.0
2.0
13
4.0
3
11.04
5
5
+
+
+

Replace missing with default strategy for all columns

+
+
(tc/replace-missing DSm2)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.02
1.02
1.02
1.02
2.02
2.02
2.013
2.013
4.013
4.013
4.013
4.03
11.04
11.05
11.05
+
+
+

Replace missing with single value in whole dataset

+
+
(tc/replace-missing DSm2 :all :value 999)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
999.02
999.02
999.02
1.0999
2.0999
999.0999
999.0999
999.0999
999.0999
999.013
4.0999
999.03
11.04
999.05
999.05
+
+
+

Replace missing with single value in :a column

+
+
(tc/replace-missing DSm2 :a :value 999)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
999.02
999.02
999.02
1.0
2.0
999.0
999.0
999.0
999.0
999.013
4.0
999.03
11.04
999.05
999.05
+
+
+

Replace missing with sequence in :a column

+
+
(tc/replace-missing DSm2 :a :value [-999 -998 -997])
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
-999.02
-998.02
-997.02
1.0
2.0
-999.0
-998.0
-997.0
-999.0
-998.013
4.0
-997.03
11.04
-999.05
-998.05
+
+
+

Replace missing with a function (mean)

+
+
(tc/replace-missing DSm2 :a :value tech.v3.datatype.functional/mean)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
4.52
4.52
4.52
1.0
2.0
4.5
4.5
4.5
4.5
4.513
4.0
4.53
11.04
4.55
4.55
+
+
+

Replace missing some missing values with a map

+
+
(tc/replace-missing DSm2 :a :value {0 100 1 -100 14 -1000})
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
100.02
-100.02
2
1.0
2.0
13
4.0
3
11.04
5
-1000.05
+
+
+

Using :down strategy, fills gaps with values from above. You can see that if missings are at the beginning, the are filled with first value

+
+
(tc/replace-missing DSm2 [:a :b] :downup)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.02
1.02
1.02
1.02
2.02
2.02
2.02
2.02
2.02
2.013
4.013
4.03
11.04
11.05
11.05
+
+
+

To fix above issue you can provide value

+
+
(tc/replace-missing DSm2 [:a :b] :down 999)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
999.02
999.02
999.02
1.02
2.02
2.02
2.02
2.02
2.02
2.013
4.013
4.03
11.04
11.05
11.05
+
+
+

The same applies for :up strategy which is opposite direction.

+
+
(tc/replace-missing DSm2 [:a :b] :up)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.02
1.02
1.02
1.013
2.013
4.013
4.013
4.013
4.013
4.013
4.03
11.03
11.04
5
5
+
+
+
+
(tc/replace-missing DSm2 [:a :b] :updown)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.02
1.02
1.02
1.013
2.013
4.013
4.013
4.013
4.013
4.013
4.03
11.03
11.04
11.05
11.05
+
+
+

The same applies for :up strategy which is opposite direction.

+
+
(tc/replace-missing DSm2 [:a :b] :midpoint)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.02.0
1.02.0
1.02.0
1.07.5
2.07.5
3.07.5
3.07.5
3.07.5
3.07.5
3.013.0
4.08.0
7.53.0
11.04.0
11.05.0
11.05.0
+
+
+

We can use a function which is applied after applying :up or :down

+
+
(tc/replace-missing DSm2 [:a :b] :down tech.v3.datatype.functional/mean)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
4.52
4.52
4.52
1.02
2.02
2.02
2.02
2.02
2.02
2.013
4.013
4.03
11.04
11.05
11.05
+
+
+

Lerp tries to apply linear interpolation of the values

+
+
(tc/replace-missing DSm2 [:a :b] :lerp)
+
+
+

_unnamed [15 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.000000002.00000000
1.000000002.00000000
1.000000002.00000000
1.000000003.57142857
2.000000005.14285714
2.333333336.71428571
2.666666678.28571429
3.000000009.85714286
3.3333333311.42857143
3.6666666713.00000000
4.000000008.00000000
7.500000003.00000000
11.000000004.00000000
11.000000005.00000000
11.000000005.00000000
+
+
+

Lerp works also on dates

+
+
(-> (tc/dataset {:dt [(java.time.LocalDateTime/of 2020 1 1 11 22 33)
+                      nil nil nil nil nil nil nil
+                      (java.time.LocalDateTime/of 2020 10 1 1 1 1)]})
+    (tc/replace-missing :lerp))
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:dt
2020-01-01T11:22:33
2020-02-04T16:04:51.500
2020-03-09T20:47:10
2020-04-13T01:29:28.500
2020-05-17T06:11:47
2020-06-20T10:54:05.500
2020-07-24T15:36:24
2020-08-27T20:18:42.500
2020-10-01T01:01:01
+
+
+
+

Inject

+

When your column contains not continuous data range you can fill up with lacking values. Arguments:

+
    +
  • dataset
  • +
  • column name
  • +
  • expected step (max-span, milliseconds in case of datetime column)
  • +
  • (optional) missing-strategy - how to replace missing, default :down (set to nil if none)
  • +
  • (optional) missing-value - optional value for replace missing
  • +
+
+
+
(-> (tc/dataset {:a [1 2 9]
+                 :b [:a :b :c]})
+    (tc/fill-range-replace :a 1))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b
1.0:a
2.0:b
3.0:b
4.0:b
5.0:b
6.0:b
7.0:b
8.0:b
9.0:c
+
+
+
+
+

Join/Separate Columns

+

Joining or separating columns are operations which can help to tidy messy dataset.

+
    +
  • join-columns joins content of the columns (as string concatenation or other structure) and stores it in new column
  • +
  • separate-column splits content of the columns into set of new columns
  • +
+
+

Join

+

join-columns accepts:

+
    +
  • dataset
  • +
  • column selector (as in select-columns)
  • +
  • options
  • +
  • :separator (default "-")
  • +
  • :drop-columns? - whether to drop source columns or not (default true)
  • +
  • :result-type
  • +
  • :map - packs data into map
  • +
  • :seq - packs data into sequence
  • +
  • :string - join strings with separator (default)
  • +
  • or custom function which gets row as a vector
  • +
  • :missing-subst - substitution for missing value
  • +
+
+

Default usage. Create :joined column out of other columns.

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4])
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.51-1-A
1.02-2-B
3-C
1.51-4-A
0.52-5-B
1.06-C
1-7-A
1.52-8-B
0.59-C
+
+
+

Without dropping source columns.

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:drop-columns? false})
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:joined
110.5A1-1-A
221.0B2-2-B
3C3-C
141.5A1-4-A
250.5B2-5-B
61.0C6-C
17A1-7-A
281.5B2-8-B
90.5C9-C
+
+
+

Let’s replace missing value with “NA” string.

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:missing-subst "NA"})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.51-1-A
1.02-2-B
NA-3-C
1.51-4-A
0.52-5-B
1.0NA-6-C
1-7-A
1.52-8-B
0.5NA-9-C
+
+
+

We can use custom separator.

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:separator "/"
+                                            :missing-subst "."})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.51/1/A
1.02/2/B
./3/C
1.51/4/A
0.52/5/B
1.0./6/C
1/7/A
1.52/8/B
0.5./9/C
+
+
+

Or even sequence of separators.

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:separator ["-" "/"]
+                                            :missing-subst "."})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.51-1/A
1.02-2/B
.-3/C
1.51-4/A
0.52-5/B
1.0.-6/C
1-7/A
1.52-8/B
0.5.-9/C
+
+
+

The other types of results, map:

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:result-type :map})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.5{:V1 1, :V2 1, :V4 A}
1.0{:V1 2, :V2 2, :V4 B}
{:V1 nil, :V2 3, :V4 C}
1.5{:V1 1, :V2 4, :V4 A}
0.5{:V1 2, :V2 5, :V4 B}
1.0{:V1 nil, :V2 6, :V4 C}
{:V1 1, :V2 7, :V4 A}
1.5{:V1 2, :V2 8, :V4 B}
0.5{:V1 nil, :V2 9, :V4 C}
+
+
+

Sequence

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:result-type :seq})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.5(1 1 A)
1.0(2 2 B)
(nil 3 C)
1.5(1 4 A)
0.5(2 5 B)
1.0(nil 6 C)
(1 7 A)
1.5(2 8 B)
0.5(nil 9 C)
+
+
+

Custom function, calculate hash

+
+
(tc/join-columns DSm :joined [:V1 :V2 :V4] {:result-type hash})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.5535226087
1.01128801549
-1842240303
1.52022347171
0.51884312041
1.0-1555412370
1640237355
1.5-967279152
0.51128367958
+
+
+

Grouped dataset

+
+
(-> DSm
+    (tc/group-by :V4)
+    (tc/join-columns :joined [:V1 :V2 :V4])
+    (tc/ungroup))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:joined
0.51-1-A
1.51-4-A
1-7-A
1.02-2-B
0.52-5-B
1.52-8-B
3-C
1.06-C
0.59-C
+
+
+
+
Tidyr examples
+

source

+
+
(def df (tc/dataset {:x ["a" "a" nil nil]
+                      :y ["b" nil "b" nil]}))
+
+
+
df
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:x:y
ab
a
b
+
+
+
+
(tc/join-columns df "z" [:x :y] {:drop-columns? false
+                                  :missing-subst "NA"
+                                  :separator "_"})
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:x:yz
aba_b
aa_NA
bNA_b
NA_NA
+
+
+
+
(tc/join-columns df "z" [:x :y] {:drop-columns? false
+                                  :separator "_"})
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:x:yz
aba_b
aa
bb
+
+
+
+
+

Separate

+

Column can be also separated into several other columns using string as separator, regex or custom function. Arguments:

+
    +
  • dataset
  • +
  • source column
  • +
  • target columns - can be nil or :infer to automatically create columns
  • +
  • separator as: +
      +
    • string - it’s converted to regular expression and passed to clojure.string/split function
    • +
    • regex
    • +
    • or custom function (default: identity)
    • +
  • +
  • options +
      +
    • :drop-columns? - whether drop source column(s) or not (default: true). Set to :all to keep only separation result.
    • +
    • :missing-subst - values which should be treated as missing, can be set, sequence, value or function (default: "")
    • +
  • +
+

Custom function (as separator) should return seqence of values for given value or a sequence of map.

+
+

Separate float into integer and factional values

+
+
(tc/separate-column DS :V3 [:int-part :frac-part] (fn [^double v]
+                                                     [(int (quot v 1.0))
+                                                      (mod v 1.0)]))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:int-part:frac-part:V4
1100.5A
2210.0B
1310.5C
2400.5A
1510.0B
2610.5C
1700.5A
2810.0B
1910.5C
+
+
+

Source column can be kept

+
+
(tc/separate-column DS :V3 [:int-part :frac-part] (fn [^double v]
+                                                     [(int (quot v 1.0))
+                                                      (mod v 1.0)]) {:drop-column? false})
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:int-part:frac-part:V4
110.500.5A
221.010.0B
131.510.5C
240.500.5A
151.010.0B
261.510.5C
170.500.5A
281.010.0B
191.510.5C
+
+
+

We can treat 0 or 0.0 as missing value

+
+
(tc/separate-column DS :V3 [:int-part :frac-part] (fn [^double v]
+                                                     [(int (quot v 1.0))
+                                                      (mod v 1.0)]) {:missing-subst [0 0.0]})
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:int-part:frac-part:V4
110.5A
221B
1310.5C
240.5A
151B
2610.5C
170.5A
281B
1910.5C
+
+
+

Works on grouped dataset

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/separate-column :V3 [:int-part :fract-part] (fn [^double v]
+                                                       [(int (quot v 1.0))
+                                                        (mod v 1.0)]))
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:int-part:fract-part:V4
1100.5A
2400.5A
1700.5A
2210.0B
1510.0B
2810.0B
1310.5C
2610.5C
1910.5C
+
+
+

Separate using separator returning sequence of maps.

+
+
(tc/separate-column DS :V3 (fn [^double v]
+                              {:int-part (int (quot v 1.0))
+                               :fract-part (mod v 1.0)}))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:int-part:fract-part:V4
1100.5A
2210.0B
1310.5C
2400.5A
1510.0B
2610.5C
1700.5A
2810.0B
1910.5C
+
+

Keeping all columns

+
+
(tc/separate-column DS :V3 nil (fn [^double v]
+                                  {:int-part (int (quot v 1.0))
+                                   :fract-part (mod v 1.0)}) {:drop-column? false})
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:int-part:fract-part:V4
110.500.5A
221.010.0B
131.510.5C
240.500.5A
151.010.0B
261.510.5C
170.500.5A
281.010.0B
191.510.5C
+
+

Droping all colums but separated

+
+
(tc/separate-column DS :V3 nil (fn [^double v]
+                                 {:int-part (int (quot v 1.0))
+                                  :fract-part (mod v 1.0)}) {:drop-column? :all})
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:int-part:fract-part
00.5
10.0
10.5
00.5
10.0
10.5
00.5
10.0
10.5
+
+

Infering column names

+
+
(tc/separate-column DS :V3 (fn [^double v]
+                             [(int (quot v 1.0)) (mod v 1.0)]))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3-0:V3-1:V4
1100.5A
2210.0B
1310.5C
2400.5A
1510.0B
2610.5C
1700.5A
2810.0B
1910.5C
+
+
+

Join and separate together.

+
+
(-> DSm
+    (tc/join-columns :joined [:V1 :V2 :V4] {:result-type :map})
+    (tc/separate-column :joined [:v1 :v2 :v4] (juxt :V1 :V2 :V4)))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:v1:v2:v4
0.511A
1.022B
3C
1.514A
0.525B
1.06C
17A
1.528B
0.59C
+
+
+
(-> DSm
+    (tc/join-columns :joined [:V1 :V2 :V4] {:result-type :seq})
+    (tc/separate-column :joined [:v1 :v2 :v4] identity))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:v1:v2:v4
0.511A
1.022B
3C
1.514A
0.525B
1.06C
17A
1.528B
0.59C
+
+
+
Tidyr examples
+

separate source extract source

+
+
(def df-separate (tc/dataset {:x [nil "a.b" "a.d" "b.c"]}))
+
+
+
(def df-separate2 (tc/dataset {:x ["a" "a b" nil "a b c"]}))
+
+
+
(def df-separate3 (tc/dataset {:x ["a?b" nil "a.b" "b:c"]}))
+
+
+
(def df-extract (tc/dataset {:x [nil "a-b" "a-d" "b-c" "d-e"]}))
+
+
+
df-separate
+
+
+

_unnamed [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:x
a.b
a.d
b.c
+
+
+
df-separate2
+
+
+

_unnamed [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:x
a
a b
a b c
+
+
+
df-separate3
+
+
+

_unnamed [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:x
a?b
a.b
b:c
+
+
+
df-extract
+
+
+

_unnamed [5 1]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:x
a-b
a-d
b-c
d-e
+
+
+
+
(tc/separate-column df-separate :x [:A :B] "\\.")
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:A:B
ab
ad
bc
+
+
+

You can drop columns after separation by setting nil as a name. We need second value here.

+
+
(tc/separate-column df-separate :x [nil :B] "\\.")
+
+
+

_unnamed [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:B
b
d
c
+
+
+

Extra data is dropped

+
+
(tc/separate-column df-separate2 :x ["a" "b"] " ")
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ab
a
ab
ab
+
+
+

Split with regular expression

+
+
(tc/separate-column df-separate3 :x ["a" "b"] "[?\\.:]")
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ab
ab
ab
bc
+
+
+

Or just regular expression to extract values

+
+
(tc/separate-column df-separate3 :x ["a" "b"] #"(.).(.)")
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ab
ab
ab
bc
+
+
+

Extract first value only

+
+
(tc/separate-column df-extract :x ["A"] "-")
+
+
+

_unnamed [5 1]:

+ + + + + + + + + + + + + + + + + + + + + + + +
A
a
a
b
d
+
+
+

Split with regex

+
+
(tc/separate-column df-extract :x ["A" "B"] #"(\p{Alnum})-(\p{Alnum})")
+
+
+

_unnamed [5 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AB
ab
ad
bc
de
+
+
+

Only a,b,c,d strings

+
+
(tc/separate-column df-extract :x ["A" "B"] #"([a-d]+)-([a-d]+)")
+
+
+

_unnamed [5 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AB
ab
ad
bc
+
+
+
+
+

Array column conversion

+

A dataset can have as well columns of type java array. We can convert from normal columns to a single array column and back like this:

+
+
(-> (tc/dataset {:x [(double-array [1 2 3])
+                     (double-array [4 5 6])]
+                 :y [:a :b]})
+    (tc/array-column->columns :x))
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:y012
:a1.02.03.0
:b4.05.06.0
+
+

and the other way around:

+
+
(-> (tc/dataset {0 [0.0 1 2]
+                 1 [3.0 4 5]
+                 :x [:a :b :c]})
+    (tc/columns->array-column [0 1] :y))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:x:y
:a[D@18a986c2
:b[D@1dec5284
:c[D@f1508b2
+
+
+
+
+

Fold/Unroll Rows

+

To pack or unpack the data into single value you can use fold-by and unroll functions.

+

fold-by groups dataset and packs columns data from each group separately into desired datastructure (like vector or sequence). unroll does the opposite.

+
+

Fold-by

+

Group-by and pack columns into vector

+
+
(tc/fold-by DS [:V3 :V4 :V1])
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3:V4:V1:V2
0.5A1[1 7]
1.0B2[2 8]
1.5C1[3 9]
0.5A2[4]
1.0B1[5]
1.5C2[6]
+
+
+

You can pack several columns at once.

+
+
(tc/fold-by DS [:V4])
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A[1 2 1][1 4 7][0.5 0.5 0.5]
B[2 1 2][2 5 8][1.0 1.0 1.0]
C[1 2 1][3 6 9][1.5 1.5 1.5]
+
+
+

You can use custom packing function

+
+
(tc/fold-by DS [:V4] seq)
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A(1 2 1)(1 4 7)(0.5 0.5 0.5)
B(2 1 2)(2 5 8)(1.0 1.0 1.0)
C(1 2 1)(3 6 9)(1.5 1.5 1.5)
+
+

or

+
+
(tc/fold-by DS [:V4] set)
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A#{1 2}#{7 1 4}#{0.5}
B#{1 2}#{2 5 8}#{1.0}
C#{1 2}#{6 3 9}#{1.5}
+
+
+

This works also on grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/fold-by :V4)
+    (tc/ungroup))
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A[1 1][1 7][0.5 0.5]
C[1 1][3 9][1.5 1.5]
B[1][5][1.0]
B[2 2][2 8][1.0 1.0]
A[2][4][0.5]
C[2][6][1.5]
+
+
+
+

Unroll

+

unroll unfolds sequences stored in data, multiplying other ones when necessary. You can unroll more than one column at once (folded data should have the same size!).

+

Options:

+
    +
  • :indexes? if true (or column name), information about index of unrolled sequence is added.
  • +
  • :datatypes list of datatypes which should be applied to restored columns, a map
  • +
+
+

Unroll one column

+
+
(tc/unroll (tc/fold-by DS [:V4]) [:V1])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V2:V3:V1
A[1 4 7][0.5 0.5 0.5]1
A[1 4 7][0.5 0.5 0.5]2
A[1 4 7][0.5 0.5 0.5]1
B[2 5 8][1.0 1.0 1.0]2
B[2 5 8][1.0 1.0 1.0]1
B[2 5 8][1.0 1.0 1.0]2
C[3 6 9][1.5 1.5 1.5]1
C[3 6 9][1.5 1.5 1.5]2
C[3 6 9][1.5 1.5 1.5]1
+
+
+

Unroll all folded columns

+
+
(tc/unroll (tc/fold-by DS [:V4]) [:V1 :V2 :V3])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A110.5
A240.5
A170.5
B221.0
B151.0
B281.0
C131.5
C261.5
C191.5
+
+
+

Unroll one by one leads to cartesian product

+
+
(-> DS
+    (tc/fold-by [:V4 :V1])
+    (tc/unroll [:V2])
+    (tc/unroll [:V3]))
+
+
+

_unnamed [15 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A110.5
A110.5
A170.5
A170.5
B221.0
B221.0
B281.0
B281.0
C131.5
C131.5
C191.5
C191.5
A240.5
B151.0
C261.5
+
+
+

You can add indexes

+
+
(tc/unroll (tc/fold-by DS [:V1]) [:V4 :V2 :V3] {:indexes? true})
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:indexes:V4:V2:V3
10A10.5
11C31.5
12B51.0
13A70.5
14C91.5
20B21.0
21A40.5
22C61.5
23B81.0
+
+
+
(tc/unroll (tc/fold-by DS [:V1]) [:V4 :V2 :V3] {:indexes? "vector idx"})
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1vector idx:V4:V2:V3
10A10.5
11C31.5
12B51.0
13A70.5
14C91.5
20B21.0
21A40.5
22C61.5
23B81.0
+
+
+

You can also force datatypes

+
+
(-> DS
+    (tc/fold-by [:V1])
+    (tc/unroll [:V4 :V2 :V3] {:datatypes {:V4 :string
+                                           :V2 :int16
+                                           :V3 :float32}})
+    (tc/info :columns))
+
+
+

_unnamed :column info [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:datatype:n-elems:categorical?
:V1:int649
:V4:string9true
:V2:int169
:V3:float329
+
+
+

This works also on grouped dataset

+
+
(-> DS
+    (tc/group-by :V1)
+    (tc/fold-by [:V1 :V4])
+    (tc/unroll :V3 {:indexes? true})
+    (tc/ungroup))
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V4:V2:indexes:V3
1A[1 7]00.5
1A[1 7]10.5
1C[3 9]01.5
1C[3 9]11.5
1B[5]01.0
2B[2 8]01.0
2B[2 8]11.0
2A[4]00.5
2C[6]01.5
+
+
+
+
+

Reshape

+

Reshaping data provides two types of operations:

+
    +
  • pivot->longer - converting columns to rows
  • +
  • pivot->wider - converting rows to columns
  • +
+

Both functions are inspired on tidyr R package and provide almost the same functionality.

+

All examples are taken from mentioned above documentation.

+

Both functions work only on regular dataset.

+
+

Longer

+

pivot->longer converts columns to rows. Column names are treated as data.

+

Arguments:

+
    +
  • dataset
  • +
  • columns selector
  • +
  • options: +
      +
    • :target-columns - names of the columns created or columns pattern (see below) (default: :$column)
    • +
    • :value-column-name - name of the column for values (default: :$value)
    • +
    • :splitter - string, regular expression or function which splits source column names into data
    • +
    • :drop-missing? - remove rows with missing? (default: true)
    • +
    • :datatypes - map of target columns data types
    • +
    • :coerce-to-number - try to convert extracted values to numbers if possible (default: true)
    • +
  • +
+

:target-columns - can be:

+
    +
  • column name - source columns names are put there as a data
  • +
  • column names as seqence - source columns names after split are put separately into :target-columns as data
  • +
  • pattern - is a sequence of names, where some of the names are nil. nil is replaced by a name taken from splitter and such column is used for values.
  • +
+
+

Create rows from all columns but "religion".

+
+
(def relig-income (tc/dataset "data/relig_income.csv"))
+
+
+
relig-income
+
+
+

data/relig_income.csv [18 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
religion<$10k$10-20k$20-30k$30-40k$40-50k$50-75k$75-100k$100-150k>150kDon’t know/refused
Agnostic27346081761371221098496
Atheist12273752357073597476
Buddhist27213034335862395354
Catholic41861773267063811169497926331489
Don’t know/refused151415111035211718116
Evangelical Prot575869106498288114869497234141529
Hindu1979113447485437
Historically Black Prot2282442362381972231318178339
Jehovah’s Witness2027242421301511637
Jewish1919252530956987151162
Mainline Prot28949561965565111079397536341328
Mormon294048515611285494269
Muslim67910923168622
Orthodox13172332324738424673
Other Christian971113131418141218
Other Faiths20334046496346404171
Other World Religions5234273448
Unaffiliated217299374365341528407321258597
+
+
+
(tc/pivot->longer relig-income (complement #{"religion"}))
+
+
+

data/relig_income.csv [180 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
religion:\(column | :\)value
Agnostic<$10k27
Atheist<$10k12
Buddhist<$10k27
Catholic<$10k418
Don’t know/refused<$10k15
Evangelical Prot<$10k575
Hindu<$10k1
Historically Black Prot<$10k228
Jehovah’s Witness<$10k20
Jewish<$10k19
Historically Black Prot>150k78
Jehovah’s Witness>150k6
Jewish>150k151
Mainline Prot>150k634
Mormon>150k42
Muslim>150k6
Orthodox>150k46
Other Christian>150k12
Other Faiths>150k41
Other World Religions>150k4
Unaffiliated>150k258
+
+
+

Convert only columns starting with "wk" and pack them into :week column, values go to :rank column

+
+
(def bilboard (-> (tc/dataset "data/billboard.csv.gz")
+                  (tc/drop-columns :type/boolean)))
+
+

drop some boolean columns, tidyr just skips them

+
+
(->> bilboard
+     (tc/column-names)
+     (take 13)
+     (tc/select-columns bilboard))
+
+
+

data/billboard.csv.gz [317 13]:

+ +++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
artisttrackdate.enteredwk1wk2wk3wk4wk5wk6wk7wk8wk9wk10
2 PacBaby Don’t Cry (Keep…2000-02-2687827277879499
2Ge+herThe Hardest Part Of …2000-09-02918792
3 Doors DownKryptonite2000-04-0881706867665754535151
3 Doors DownLoser2000-10-2176767269676555596261
504 BoyzWobble Wobble2000-04-1557342517173136495357
98^0Give Me Just One Nig…2000-08-195139342626192236
A*TeensDancing Queen2000-07-0897979695100
AaliyahI Don’t Wanna2000-01-2984625141383535383836
AaliyahTry Again2000-03-1859533828211816141210
Adams, YolandaOpen My Heart2000-08-2676767469686761585759
Wallflowers, TheSleepwalker2000-10-28737374809096
WestlifeSwear It Again2000-04-0196826655554644443735
Williams, RobbieAngels1999-11-2085776969625656645453
Wills, MarkBack At One2000-01-1589555143373736394246
Worley, DarrylWhen You Need My Lov…2000-06-1798889392858584808080
Wright, ChelyIt Was2000-03-0486787572716964758598
Yankee GreyAnother Nine Minutes2000-04-298683777483798895
Yearwood, TrishaReal Live Woman2000-04-01858383828191
Ying Yang TwinsWhistle While You Tw…2000-03-1895949185847874788589
Zombie NationKernkraft 4002000-09-029999
matchbox twentyBent2000-04-2960372924222118161312
+
+
+
(tc/pivot->longer bilboard #(clojure.string/starts-with? % "wk") {:target-columns :week
+                                                                   :value-column-name :rank})
+
+
+

data/billboard.csv.gz [5307 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
artisttrackdate.entered:week:rank
3 Doors DownKryptonite2000-04-08wk354
Braxton, ToniHe Wasn’t Man Enough2000-03-18wk3534
CreedHigher1999-09-11wk3522
CreedWith Arms Wide Open2000-05-13wk355
Hill, FaithBreathe1999-11-06wk358
JoeI Wanna Know2000-01-01wk355
LonestarAmazed1999-06-05wk3514
Vertical HorizonEverything You Want2000-01-22wk3527
matchbox twentyBent2000-04-29wk3533
CreedHigher1999-09-11wk5521
Savage GardenI Knew I Loved You1999-10-23wk2412
SisqoIncomplete2000-06-24wk2431
SisqoThong Song2000-01-29wk2417
Smash MouthThen The Morning Com…1999-10-30wk2435
Son By FourA Puro Dolor (Purest…2000-04-08wk2432
SoniqueIt Feels So Good2000-01-22wk2449
SoulDecisionFaded2000-07-08wk2450
StingDesert Rose2000-05-13wk2445
TrainMeet Virginia1999-10-09wk2442
Vertical HorizonEverything You Want2000-01-22wk246
matchbox twentyBent2000-04-29wk249
+
+
+

We can create numerical column out of column names

+
+
(tc/pivot->longer bilboard #(clojure.string/starts-with? % "wk") {:target-columns :week
+                                                                   :value-column-name :rank
+                                                                   :splitter #"wk(.*)"
+                                                                   :datatypes {:week :int16}})
+
+
+

data/billboard.csv.gz [5307 5]:

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
artisttrackdate.entered:week:rank
3 Doors DownKryptonite2000-04-084621
CreedHigher1999-09-11467
CreedWith Arms Wide Open2000-05-134637
Hill, FaithBreathe1999-11-064631
LonestarAmazed1999-06-05465
3 Doors DownKryptonite2000-04-085142
CreedHigher1999-09-115114
Hill, FaithBreathe1999-11-065149
LonestarAmazed1999-06-055112
2 PacBaby Don’t Cry (Keep…2000-02-26694
matchbox twentyBent2000-04-29522
3 Doors DownKryptonite2000-04-08343
Braxton, ToniHe Wasn’t Man Enough2000-03-183433
CreedHigher1999-09-113423
CreedWith Arms Wide Open2000-05-13345
Hill, FaithBreathe1999-11-06345
JoeI Wanna Know2000-01-01348
LonestarAmazed1999-06-053417
Nelly(Hot S**t) Country G…2000-04-293449
Vertical HorizonEverything You Want2000-01-223420
matchbox twentyBent2000-04-293430
+
+
+

When column names contain observation data, such column names can be splitted and data can be restored into separate columns.

+
+
(def who (tc/dataset "data/who.csv.gz"))
+
+
+
(->> who
+     (tc/column-names)
+     (take 10)
+     (tc/select-columns who))
+
+
+

data/who.csv.gz [7240 10]:

+ ++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryiso2iso3yearnew_sp_m014new_sp_m1524new_sp_m2534new_sp_m3544new_sp_m4554new_sp_m5564
AfghanistanAFAFG1980
AfghanistanAFAFG1981
AfghanistanAFAFG1982
AfghanistanAFAFG1983
AfghanistanAFAFG1984
AfghanistanAFAFG1985
AfghanistanAFAFG1986
AfghanistanAFAFG1987
AfghanistanAFAFG1988
AfghanistanAFAFG1989
ZimbabweZWZWE200313387430482228981367
ZimbabweZWZWE2004187833290822981056366
ZimbabweZWZWE200521083722641855762295
ZimbabweZWZWE200621573623911939896348
ZimbabweZWZWE200713850036930716292
ZimbabweZWZWE200812761403316704263
ZimbabweZWZWE20091255783471681293
ZimbabweZWZWE201015071022081682761350
ZimbabweZWZWE201115278424672071780377
ZimbabweZWZWE201212078324212086796360
ZimbabweZWZWE2013
+
+
+
(tc/pivot->longer who #(clojure.string/starts-with? % "new") {:target-columns [:diagnosis :gender :age]
+                                                               :splitter #"new_?(.*)_(.)(.*)"
+                                                               :value-column-name :count})
+
+
+

data/who.csv.gz [76046 8]:

+ ++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryiso2iso3year:diagnosis:gender:age:count
AlbaniaALALB2013relm152460
AlgeriaDZDZA2013relm15241021
AndorraADAND2013relm15240
AngolaAOAGO2013relm15242992
AnguillaAIAIA2013relm15240
Antigua and BarbudaAGATG2013relm15241
ArgentinaARARG2013relm15241124
ArmeniaAMARM2013relm1524116
AustraliaAUAUS2013relm1524105
AustriaATAUT2013relm152444
United Arab EmiratesAEARE2013relm25349
United Kingdom of Great Britain and Northern IrelandGBGBR2013relm25341158
United States of AmericaUSUSA2013relm2534829
UruguayUYURY2013relm2534142
UzbekistanUZUZB2013relm25342371
VanuatuVUVUT2013relm25349
Venezuela (Bolivarian Republic of)VEVEN2013relm2534739
Viet NamVNVNM2013relm25346302
YemenYEYEM2013relm25341113
ZambiaZMZMB2013relm25347808
ZimbabweZWZWE2013relm25345331
+
+
+

When data contains multiple observations per row, we can use splitter and pattern for target columns to create new columns and put values there. In following dataset we have two obseravations dob and gender for two childs. We want to put child infomation into the column and leave dob and gender for values.

+
+
(def family (tc/dataset "data/family.csv"))
+
+
+
family
+
+
+

data/family.csv [5 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
familydob_child1dob_child2gender_child1gender_child2
11998-11-262000-01-2912
21996-06-222
32002-07-112004-04-0522
42004-10-102009-08-2711
52000-12-052005-02-2821
+
+
+
(tc/pivot->longer family (complement #{"family"}) {:target-columns [nil :child]
+                                                    :splitter "_"
+                                                    :datatypes {"gender" :int16}})
+
+
+

data/family.csv [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
family:childdobgender
1child11998-11-261
2child11996-06-222
3child12002-07-112
4child12004-10-101
5child12000-12-052
1child22000-01-292
3child22004-04-052
4child22009-08-271
5child22005-02-281
+
+
+

Similar here, we have two observations: x and y in four groups.

+
+
(def anscombe (tc/dataset "data/anscombe.csv"))
+
+
+
anscombe
+
+
+

data/anscombe.csv [11 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
x1x2x3x4y1y2y3y4
10101088.049.147.466.58
88886.958.146.775.76
13131387.588.7412.747.71
99988.818.777.118.84
11111188.339.267.818.47
14141489.968.108.847.04
66687.246.136.085.25
444194.263.105.3912.50
121212810.849.138.155.56
77784.827.266.427.91
55585.684.745.736.89
+
+
+
(tc/pivot->longer anscombe :all {:splitter #"(.)(.)"
+                                  :target-columns [nil :set]})
+
+
+

data/anscombe.csv [44 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:setxy
1108.04
186.95
1137.58
198.81
1118.33
1149.96
167.24
144.26
11210.84
174.82
486.58
485.76
487.71
488.84
488.47
487.04
485.25
41912.50
485.56
487.91
486.89
+
+
+
+
^:note-to-test/skip
+(def pnl (tc/dataset {:x [1 2 3 4]
+                       :a [1 1 0 0]
+                       :b [0 1 1 1]
+                       :y1 (repeatedly 4 rand)
+                       :y2 (repeatedly 4 rand)
+                       :z1 [3 3 3 3]
+                       :z2 [-2 -2 -2 -2]}))
+
+
+
^:note-to-test/skip
+pnl
+
+
+

_unnamed [4 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:x:a:b:y1:y2:z1:z2
1100.948733690.702727993-2
2110.758453760.021185683-2
3010.346743180.133335613-2
4010.988435620.022732823-2
+
+
+
^:note-to-test/skip
+(tc/pivot->longer pnl [:y1 :y2 :z1 :z2] {:target-columns [nil :times]
+                                          :splitter #":(.)(.)"})
+
+
+

_unnamed [8 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:x:a:b:timesyz
11010.948733693
21110.758453763
30110.346743183
40110.988435623
11020.70272799-2
21120.02118568-2
30120.13333561-2
40120.02273282-2
+
+
+
+

Wider

+

pivot->wider converts rows to columns.

+

Arguments:

+
    +
  • dataset
  • +
  • columns-selector - values from selected columns are converted to new columns
  • +
  • value-columns - what are values
  • +
+

When multiple columns are used as columns selector, names are joined using :concat-columns-with option. :concat-columns-with can be a string or function (default: “_“). Function accepts sequence of names.

+

When columns-selector creates non unique set of values, they are folded using :fold-fn (default: vec) option.

+

When value-columns is a sequence, multiple observations as columns are created appending value column names into new columns. Column names are joined using :concat-value-with option. :concat-value-with can be a string or function (default: “-”). Function accepts current column name and value.

+
+

Use station as a name source for columns and seen for values

+
+
(def fish (tc/dataset "data/fish_encounters.csv"))
+
+
+
fish
+
+
+

data/fish_encounters.csv [114 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fishstationseen
4842Release1
4842I80_11
4842Lisbon1
4842Rstr1
4842Base_TD1
4842BCE1
4842BCW1
4842BCE21
4842BCW21
4842MAE1
4862BCE1
4862BCW1
4862BCE21
4862BCW21
4863Release1
4863I80_11
4864Release1
4864I80_11
4865Release1
4865I80_11
4865Lisbon1
+
+
+
(tc/pivot->wider fish "station" "seen" {:drop-missing? false})
+
+
+

data/fish_encounters.csv [19 12]:

+ ++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fishReleaseI80_1LisbonRstrBase_TDBCEBCWBCE2BCW2MAEMAW
484211111111111
484311111111111
484411111111111
485811111111111
486111111111111
4857111111111
4862111111111
4850111111
484511111
485511111
485911111
48481111
4847111
4865111
484911
485111
485411
486311
486411
+
+
+

If selected columns contain multiple values, such values should be folded.

+
+
(def warpbreaks (tc/dataset "data/warpbreaks.csv"))
+
+
+
warpbreaks
+
+
+

data/warpbreaks.csv [54 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
breakswooltension
26AL
30AL
54AL
25AL
70AL
52AL
51AL
26AL
67AL
18AM
39BM
29BM
20BH
21BH
24BH
17BH
13BH
15BH
15BH
16BH
28BH
+
+

Let’s see how many values are for each type of wool and tension groups

+
+
(-> warpbreaks
+    (tc/group-by ["wool" "tension"])
+    (tc/aggregate {:n tc/row-count}))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
wooltension:n
AL9
AM9
AH9
BL9
BM9
BH9
+
+
+
(-> warpbreaks
+    (tc/reorder-columns ["wool" "tension" "breaks"])
+    (tc/pivot->wider "wool" "breaks" {:fold-fn vec}))
+
+
+

data/warpbreaks.csv [3 3]:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
tensionAB
L[26 30 54 25 70 52 51 26 67][27 14 29 19 29 31 41 20 44]
M[18 21 29 17 12 18 35 30 36][42 26 19 16 39 28 21 39 29]
H[36 21 24 18 10 43 28 15 26][20 21 24 17 13 15 15 16 28]
+
+

We can also calculate mean (aggregate values)

+
+
(-> warpbreaks
+    (tc/reorder-columns ["wool" "tension" "breaks"])
+    (tc/pivot->wider "wool" "breaks" {:fold-fn tech.v3.datatype.functional/mean}))
+
+
+

data/warpbreaks.csv [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
tensionAB
L44.5555555628.22222222
M24.0000000028.77777778
H24.5555555618.77777778
+
+
+

Multiple source columns, joined with default separator.

+
+
(def production (tc/dataset "data/production.csv"))
+
+
+
production
+
+
+

data/production.csv [45 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
productcountryyearproduction
AAI20001.63727158
AAI20010.15870784
AAI2002-1.56797745
AAI2003-0.44455509
AAI2004-0.07133701
AAI20051.61183090
AAI2006-0.70434682
AAI2007-1.53550542
AAI20080.83907155
AAI2009-0.37424110
BEI20040.62564999
BEI2005-1.34530299
BEI2006-0.97184975
BEI2007-1.69715821
BEI20080.04556128
BEI20091.19315043
BEI2010-1.60557503
BEI2011-0.77235497
BEI2012-2.50262738
BEI2013-1.62753769
BEI20140.03329645
+
+
+
(tc/pivot->wider production ["product" "country"] "production")
+
+
+

data/production.csv [15 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearA_AIB_AIB_EI
20001.63727158-0.026176611.40470848
20010.15870784-0.68863576-0.59618369
2002-1.567977450.06248741-0.26568579
2003-0.44455509-0.723396860.65257808
2004-0.071337010.472489520.62564999
20051.61183090-0.94173861-1.34530299
2006-0.70434682-0.34782108-0.97184975
2007-1.535505420.52425284-1.69715821
20080.839071551.832309370.04556128
2009-0.374241100.107064911.19315043
2010-0.71158926-0.32903664-1.60557503
20111.12805634-1.78319121-0.77235497
20121.457182470.61125798-2.50262738
2013-1.55934101-0.78526092-1.62753769
2014-0.116958380.978436350.03329645
+
+

Joined with custom function

+
+
(tc/pivot->wider production ["product" "country"] "production" {:concat-columns-with vec})
+
+
+

data/production.csv [15 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
year[A AI][B AI][B EI]
20001.63727158-0.026176611.40470848
20010.15870784-0.68863576-0.59618369
2002-1.567977450.06248741-0.26568579
2003-0.44455509-0.723396860.65257808
2004-0.071337010.472489520.62564999
20051.61183090-0.94173861-1.34530299
2006-0.70434682-0.34782108-0.97184975
2007-1.535505420.52425284-1.69715821
20080.839071551.832309370.04556128
2009-0.374241100.107064911.19315043
2010-0.71158926-0.32903664-1.60557503
20111.12805634-1.78319121-0.77235497
20121.457182470.61125798-2.50262738
2013-1.55934101-0.78526092-1.62753769
2014-0.116958380.978436350.03329645
+
+
+

Multiple value columns

+
+
(def income (tc/dataset "data/us_rent_income.csv"))
+
+
+
income
+
+
+

data/us_rent_income.csv [104 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GEOIDNAMEvariableestimatemoe
1Alabamaincome24476136
1Alabamarent7473
2Alaskaincome32940508
2Alaskarent120013
4Arizonaincome27517148
4Arizonarent9724
5Arkansasincome23789165
5Arkansasrent7095
6Californiaincome29454109
6Californiarent13583
51Virginiarent11665
53Washingtonincome32318113
53Washingtonrent11204
54West Virginiaincome23707203
54West Virginiarent6816
55Wisconsinincome29868135
55Wisconsinrent8133
56Wyomingincome30854342
56Wyomingrent82811
72Puerto Ricoincome
72Puerto Ricorent4646
+
+
+
(tc/pivot->wider income "variable" ["estimate" "moe"] {:drop-missing? false})
+
+
+

data/us_rent_income.csv [52 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GEOIDNAMEincome-estimateincome-moerent-estimaterent-moe
1Alabama244761367473
2Alaska32940508120013
4Arizona275171489724
5Arkansas237891657095
6California2945410913583
8Colorado3240110911255
9Connecticut3532619511235
10Delaware31560247107610
11District of Columbia43198681142417
12Florida259527010773
46South Dakota288212766967
47Tennessee254531028084
48Texas280631109522
49Utah279282399486
50Vermont2935136194511
51Virginia3254520211665
53Washington3231811311204
54West Virginia237072036816
55Wisconsin298681358133
56Wyoming3085434282811
72Puerto Rico4646
+
+

Value concatenated by custom function

+
+
(tc/pivot->wider income "variable" ["estimate" "moe"] {:concat-columns-with vec
+                                                        :concat-value-with vector
+                                                        :drop-missing? false})
+
+
+

data/us_rent_income.csv [52 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GEOIDNAME[income estimate][income moe][rent estimate][rent moe]
1Alabama244761367473
2Alaska32940508120013
4Arizona275171489724
5Arkansas237891657095
6California2945410913583
8Colorado3240110911255
9Connecticut3532619511235
10Delaware31560247107610
11District of Columbia43198681142417
12Florida259527010773
46South Dakota288212766967
47Tennessee254531028084
48Texas280631109522
49Utah279282399486
50Vermont2935136194511
51Virginia3254520211665
53Washington3231811311204
54West Virginia237072036816
55Wisconsin298681358133
56Wyoming3085434282811
72Puerto Rico4646
+
+
+

Reshape contact data

+
+
(def contacts (tc/dataset "data/contacts.csv"))
+
+
+
contacts
+
+
+

data/contacts.csv [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fieldvalueperson_id
nameJiena McLellan1
companyToyota1
nameJohn Smith2
companygoogle2
emailjohn@google.com2
nameHuxley Ratcliffe3
+
+
+
(tc/pivot->wider contacts "field" "value" {:drop-missing? false})
+
+
+

data/contacts.csv [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
person_idnamecompanyemail
2John Smithgooglejohn@google.com
1Jiena McLellanToyota
3Huxley Ratcliffe
+
+
+
+

Reshaping

+

A couple of tidyr examples of more complex reshaping.

+
+

World bank

+
+
(def world-bank-pop (tc/dataset "data/world_bank_pop.csv.gz"))
+
+
+
(->> world-bank-pop
+     (tc/column-names)
+     (take 8)
+     (tc/select-columns world-bank-pop))
+
+
+

data/world_bank_pop.csv.gz [1056 8]:

+ ++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryindicator200020012002200320042005
ABWSP.URB.TOTL4.24440000E+044.30480000E+044.36700000E+044.42460000E+044.46690000E+044.48890000E+04
ABWSP.URB.GROW1.18263237E+001.41302122E+001.43455953E+001.31036044E+009.51477684E-014.91302715E-01
ABWSP.POP.TOTL9.08530000E+049.28980000E+049.49920000E+049.70170000E+049.87370000E+041.00031000E+05
ABWSP.POP.GROW2.05502678E+002.22593013E+002.22905605E+002.10935434E+001.75735287E+001.30203884E+00
AFGSP.URB.TOTL4.43629900E+064.64805500E+064.89295100E+065.15568600E+065.42677000E+065.69182300E+06
AFGSP.URB.GROW3.91222846E+004.66283822E+005.13467454E+005.23045853E+005.12439302E+004.76864700E+00
AFGSP.POP.TOTL2.00937560E+072.09664630E+072.19799230E+072.30648510E+072.41189790E+072.50707980E+07
AFGSP.POP.GROW3.49465874E+004.25150411E+004.72052846E+004.81804112E+004.46891840E+003.87047016E+00
AGOSP.URB.TOTL8.23476600E+068.70800000E+069.21878700E+069.76519700E+061.03435060E+071.09494240E+07
AGOSP.URB.GROW5.43749411E+005.58771954E+005.70013237E+005.75812711E+005.75341450E+005.69279690E+00
ZAFSP.URB.GROW2.32229180E+002.26080492E+002.29242659E+002.25719919E+002.18014731E+002.09725981E+00
ZAFSP.POP.TOTL4.57283150E+074.63850060E+074.70261730E+074.76487270E+074.82473950E+074.88205860E+07
ZAFSP.POP.GROW1.47499416E+001.42585702E+001.37280586E+001.31515951E+001.24859226E+001.18102315E+00
ZMBSP.URB.TOTL3.66507600E+063.78866000E+063.94496500E+064.10631700E+064.27387500E+064.44857100E+06
ZMBSP.URB.GROW1.50532147E+003.31633227E+004.04276877E+004.00864374E+003.99943902E+004.00620111E+00
ZMBSP.POP.TOTL1.05312210E+071.08241250E+071.11204090E+071.14219840E+071.17317460E+071.20521560E+07
ZMBSP.POP.GROW2.80705843E+002.74331654E+002.70046295E+002.67578507E+002.67585813E+002.69450644E+00
ZWESP.URB.TOTL4.12598700E+064.22551900E+064.32330700E+064.35604100E+064.38192000E+064.41384500E+06
ZWESP.URB.GROW2.52373518E+002.38368296E+002.28785252E+007.54299867E-015.92336717E-017.25920717E-01
ZWESP.POP.TOTL1.22222510E+071.23661650E+071.25005250E+071.26338970E+071.27775110E+071.29400320E+07
ZWESP.POP.GROW1.29878201E+001.17059711E+001.08065293E+001.06127964E+001.13032327E+001.26390895E+00
+
+

Step 1 - convert years column into values

+
+
(def pop2 (tc/pivot->longer world-bank-pop (map str (range 2000 2018)) {:drop-missing? false
+                                                                         :target-columns ["year"]
+                                                                         :value-column-name "value"}))
+
+
+
pop2
+
+
+

data/world_bank_pop.csv.gz [19008 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryindicatoryearvalue
ABWSP.URB.TOTL20134.43600000E+04
ABWSP.URB.GROW20136.69503994E-01
ABWSP.POP.TOTL20131.03187000E+05
ABWSP.POP.GROW20135.92914005E-01
AFGSP.URB.TOTL20137.73396400E+06
AFGSP.URB.GROW20134.19297967E+00
AFGSP.POP.TOTL20133.17316880E+07
AFGSP.POP.GROW20133.31522413E+00
AGOSP.URB.TOTL20131.61194910E+07
AGOSP.URB.GROW20134.72272270E+00
ZAFSP.URB.GROW20122.23077040E+00
ZAFSP.POP.TOTL20125.29982130E+07
ZAFSP.POP.GROW20121.39596592E+00
ZMBSP.URB.TOTL20125.93201300E+06
ZMBSP.URB.GROW20124.25944078E+00
ZMBSP.POP.TOTL20121.46999370E+07
ZMBSP.POP.GROW20123.00513283E+00
ZWESP.URB.TOTL20124.83015300E+06
ZWESP.URB.GROW20121.67857380E+00
ZWESP.POP.TOTL20121.47108260E+07
ZWESP.POP.GROW20122.22830616E+00
+
+

Step 2 - separate "indicate" column

+
+
(def pop3 (tc/separate-column pop2
+                               "indicator" ["area" "variable"]
+                               #(rest (clojure.string/split % #"\."))))
+
+
+
pop3
+
+
+

data/world_bank_pop.csv.gz [19008 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryareavariableyearvalue
ABWURBTOTL20134.43600000E+04
ABWURBGROW20136.69503994E-01
ABWPOPTOTL20131.03187000E+05
ABWPOPGROW20135.92914005E-01
AFGURBTOTL20137.73396400E+06
AFGURBGROW20134.19297967E+00
AFGPOPTOTL20133.17316880E+07
AFGPOPGROW20133.31522413E+00
AGOURBTOTL20131.61194910E+07
AGOURBGROW20134.72272270E+00
ZAFURBGROW20122.23077040E+00
ZAFPOPTOTL20125.29982130E+07
ZAFPOPGROW20121.39596592E+00
ZMBURBTOTL20125.93201300E+06
ZMBURBGROW20124.25944078E+00
ZMBPOPTOTL20121.46999370E+07
ZMBPOPGROW20123.00513283E+00
ZWEURBTOTL20124.83015300E+06
ZWEURBGROW20121.67857380E+00
ZWEPOPTOTL20121.47108260E+07
ZWEPOPGROW20122.22830616E+00
+
+

Step 3 - Make columns based on "variable" values.

+
+
(tc/pivot->wider pop3 "variable" "value" {:drop-missing? false})
+
+
+

data/world_bank_pop.csv.gz [9504 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
countryareayearTOTLGROW
ABWURB20134.43600000E+040.66950399
ABWPOP20131.03187000E+050.59291401
AFGURB20137.73396400E+064.19297967
AFGPOP20133.17316880E+073.31522413
AGOURB20131.61194910E+074.72272270
AGOPOP20132.59983400E+073.53182419
ALBURB20131.60350500E+061.74363937
ALBPOP20132.89509200E+06-0.18321138
ANDURB20137.15270000E+04-2.11923331
ANDPOP20138.07880000E+04-2.01331401
WSMPOP20121.89194000E+050.81144852
XKXURB2012
XKXPOP20121.80520000E+060.78972659
YEMURB20128.20982800E+064.49478765
YEMPOP20122.49099690E+072.67605025
ZAFURB20123.35330290E+072.23077040
ZAFPOP20125.29982130E+071.39596592
ZMBURB20125.93201300E+064.25944078
ZMBPOP20121.46999370E+073.00513283
ZWEURB20124.83015300E+061.67857380
ZWEPOP20121.47108260E+072.22830616
+
+
+
+

Multi-choice

+
+
(def multi (tc/dataset {:id [1 2 3 4]
+                         :choice1 ["A" "C" "D" "B"]
+                         :choice2 ["B" "B" nil "D"]
+                         :choice3 ["C" nil nil nil]}))
+
+
+
multi
+
+
+

_unnamed [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:choice1:choice2:choice3
1ABC
2CB
3D
4BD
+
+

Step 1 - convert all choices into rows and add artificial column to all values which are not missing.

+
+
(def multi2 (-> multi
+                (tc/pivot->longer (complement #{:id}))
+                (tc/add-column :checked true)))
+
+
+
multi2
+
+
+

_unnamed [8 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:\(column | :\)value:checked
1:choice1Atrue
2:choice1Ctrue
3:choice1Dtrue
4:choice1Btrue
1:choice2Btrue
2:choice2Btrue
4:choice2Dtrue
1:choice3Ctrue
+
+

Step 2 - Convert back to wide form with actual choices as columns

+
+
^:note-to-test/skip
+(-> multi2
+    (tc/drop-columns :$column)
+    (tc/pivot->wider :$value :checked {:drop-missing? false})
+    (tc/order-by :id))
+
+
+

_unnamed [4 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:idACDB
1truetruetrue
2truetrue
3true
4truetrue
+
+
+
+

Construction

+
+
(def construction (tc/dataset "data/construction.csv"))
+
+
+
(def construction-unit-map {"1 unit" "1"
+                            "2 to 4 units" "2-4"
+                            "5 units or more" "5+"})
+
+
+
construction
+
+
+

data/construction.csv [9 9]:

+ +++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
YearMonth1 unit2 to 4 units5 units or moreNortheastMidwestSouthWest
2018January859348114169596339
2018February882400138160655336
2018March862356150154595330
2018April797447144196613304
2018May87536490169673319
2018June86734276170610360
2018July829360108183594310
2018August93928690205649286
2018September835304117175560296
+
+

Conversion 1 - Group two column types

+
+
(-> construction
+    (tc/pivot->longer #"^[125NWS].*|Midwest" {:target-columns [:units :region]
+                                               :splitter (fn [col-name]
+                                                           (if (re-matches #"^[125].*" col-name)
+                                                             [(construction-unit-map col-name) nil]
+                                                             [nil col-name]))
+                                               :value-column-name :n
+                                               :drop-missing? false}))
+
+
+

data/construction.csv [63 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
YearMonth:units:region:n
2018January1859
2018February1882
2018March1862
2018April1797
2018May1875
2018June1867
2018July1829
2018August1939
2018September1835
2018January2-4
2018AugustSouth649
2018SeptemberSouth560
2018JanuaryWest339
2018FebruaryWest336
2018MarchWest330
2018AprilWest304
2018MayWest319
2018JuneWest360
2018JulyWest310
2018AugustWest286
2018SeptemberWest296
+
+

Conversion 2 - Convert to longer form and back and rename columns

+
+
(-> construction
+    (tc/pivot->longer #"^[125NWS].*|Midwest" {:target-columns [:units :region]
+                                               :splitter (fn [col-name]
+                                                           (if (re-matches #"^[125].*" col-name)
+                                                             [(construction-unit-map col-name) nil]
+                                                             [nil col-name]))
+                                               :value-column-name :n
+                                               :drop-missing? false})
+    (tc/pivot->wider [:units :region] :n {:drop-missing? false})
+    (tc/rename-columns (zipmap (vals construction-unit-map)
+                                (keys construction-unit-map))))
+
+
+

data/construction.csv [9 9]:

+ +++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
YearMonth12 to 4 units5 units or moreNortheastMidwestSouthWest
2018January859348114169596339
2018February882400138160655336
2018March862356150154595330
2018April797447144196613304
2018May87536490169673319
2018June86734276170610360
2018July829360108183594310
2018August93928690205649286
2018September835304117175560296
+
+
+

Various operations on stocks, examples taken from gather and spread manuals.

+
+
(def stocks-tidyr (tc/dataset "data/stockstidyr.csv"))
+
+
+
stocks-tidyr
+
+
+

data/stockstidyr.csv [10 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
timeXYZ
2009-01-011.30989806-1.89040193-1.77946880
2009-01-02-0.29993804-1.824730902.39892513
2009-01-030.53647501-1.03606860-3.98697977
2009-01-04-1.88390802-0.52178390-2.83065490
2009-01-05-0.96052361-2.216833491.43715171
2009-01-06-1.18528966-2.893509243.39784140
2009-01-07-0.85207056-2.16794818-1.20108258
2009-01-080.25234172-0.32854117-1.53160473
2009-01-090.402571361.96407898-6.80878830
2009-01-10-0.643835002.68618382-2.55909321
+
+

Convert to longer form

+
+
(def stocks-long (tc/pivot->longer stocks-tidyr ["X" "Y" "Z"] {:value-column-name :price
+                                                                :target-columns :stocks}))
+
+
+
stocks-long
+
+
+

data/stockstidyr.csv [30 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
time:stocks:price
2009-01-01X1.30989806
2009-01-02X-0.29993804
2009-01-03X0.53647501
2009-01-04X-1.88390802
2009-01-05X-0.96052361
2009-01-06X-1.18528966
2009-01-07X-0.85207056
2009-01-08X0.25234172
2009-01-09X0.40257136
2009-01-10X-0.64383500
2009-01-10Y2.68618382
2009-01-01Z-1.77946880
2009-01-02Z2.39892513
2009-01-03Z-3.98697977
2009-01-04Z-2.83065490
2009-01-05Z1.43715171
2009-01-06Z3.39784140
2009-01-07Z-1.20108258
2009-01-08Z-1.53160473
2009-01-09Z-6.80878830
2009-01-10Z-2.55909321
+
+

Convert back to wide form

+
+
(tc/pivot->wider stocks-long :stocks :price)
+
+
+

data/stockstidyr.csv [10 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
timeXYZ
2009-01-011.30989806-1.89040193-1.77946880
2009-01-02-0.29993804-1.824730902.39892513
2009-01-030.53647501-1.03606860-3.98697977
2009-01-04-1.88390802-0.52178390-2.83065490
2009-01-05-0.96052361-2.216833491.43715171
2009-01-06-1.18528966-2.893509243.39784140
2009-01-07-0.85207056-2.16794818-1.20108258
2009-01-080.25234172-0.32854117-1.53160473
2009-01-090.402571361.96407898-6.80878830
2009-01-10-0.643835002.68618382-2.55909321
+
+

Convert to wide form on time column (let’s limit values to a couple of rows)

+
+
^:note-to-test/skip
+(-> stocks-long
+    (tc/select-rows (range 0 30 4))
+    (tc/pivot->wider "time" :price {:drop-missing? false}))
+
+
+

data/stockstidyr.csv [3 6]:

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:stocks2009-01-012009-01-052009-01-092009-01-032009-01-07
Y-1.0360686-2.16794818
X1.30989806-0.960523610.40257136
Z-1.779468801.43715171-6.80878830
+
+
+
+
+

Join/Concat Datasets

+

Dataset join and concatenation functions.

+

Joins accept left-side and right-side datasets and columns selector. Options are the same as in tech.ml.dataset functions.

+

A column selector can be a map with :left and :right keys to specify column names separate for left and right dataset.

+

The difference between tech.ml.dataset join functions are: arguments order (first datasets) and possibility to join on multiple columns.

+

Multiple columns joins create temporary index column from column selection. The method for creating index is based on :hashing option and defaults to identity. Prior to 7.000-beta-50 hash function was used, which caused hash collision for certain cases.

+

Additionally set operations are defined: intersect and difference.

+

To concat two datasets rowwise you can choose:

+
    +
  • concat - concats rows for matching columns, the number of columns should be equal.
  • +
  • union - like concat but returns unique values
  • +
  • bind - concats rows add missing, empty columns
  • +
+

To add two datasets columnwise use bind. The number of rows should be equal.

+

Datasets used in examples:

+
+
(def ds1 (tc/dataset {:a [1 2 1 2 3 4 nil nil 4]
+                       :b (range 101 110)
+                       :c (map str "abs tract")}))
+
+
+
(def ds2 (tc/dataset {:a [nil 1 2 5 4 3 2 1 nil]
+                      :b (range 110 101 -1)
+                      :c (map str "datatable")
+                      :d (symbol "X")
+                      :e [3 4 5 6 7 nil 8 1 1]}))
+
+
+
ds1
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
+
+
+
ds2
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
110dX3
1109aX4
2108tX5
5107aX6
4106tX7
3105aX
2104bX8
1103lX1
102eX1
+
+
+

Left

+
+
(tc/left-join ds1 ds2 :b)
+
+
+

left-outer-join [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:right.b:right.a:right.c:d:e
1094t1091aX4
108c1082tX5
107a1075aX6
1064r1064tX7
1053t1053aX
10421042bX8
1031s1031lX1
1022b102eX1
1011a
+
+
+
+
(tc/left-join ds2 ds1 :b)
+
+
+

left-outer-join [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:d:e:right.b:right.a:right.c
102eX11022b
1031lX11031s
1042bX81042
1053aX1053t
1064tX71064r
1075aX6107a
1082tX5108c
1091aX41094t
110dX3
+
+
+
+
(tc/left-join ds1 ds2 [:a :b])
+
+
+

left-outer-join [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:right.a:right.b:right.c:d:e
4106r4106tX7
3105t3105aX
21042104bX8
1103s1103lX1
1101a
2102b
107a
108c
4109t
+
+
+
+
(tc/left-join ds2 ds1 [:a :b])
+
+
+

left-outer-join [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e:right.a:right.b:right.c
1103lX11103s
2104bX82104
3105aX3105t
4106tX74106r
110dX3
1109aX4
2108tX5
5107aX6
102eX1
+
+
+
+
(tc/left-join ds1 ds2 {:left :a :right :e})
+
+
+

left-outer-join [11 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:e:right.a:right.b:right.c:d
3105t3110dX
4106r41109aX
4109t41109aX
107a3105aX
108c3105aX
1101a11103lX
1103s11103lX
1101a1102eX
1103s1102eX
2102b
2104
+
+
+
+
(tc/left-join ds2 ds1 {:left :e :right :a})
+
+
+

left-outer-join [13 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:e:a:b:c:d:right.a:right.b:right.c
11103lX1101a
1102eX1101a
11103lX1103s
1102eX1103s
3110dX3105t
41109aX4106r
3105aX107a
3105aX108c
41109aX4109t
52108tX
65107aX
74106tX
82104bX
+
+
+
+
(tc/left-join ds2 ds1 {:left [:b :e] :right [:b :a]})
+
+
+

left-outer-join [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e:right.a:right.b:right.c
1103lX11103s
1109aX44109t
110dX3
2108tX5
5107aX6
4106tX7
3105aX
2104bX8
102eX1
+
+
+ +
+

Inner

+
+
(tc/inner-join ds1 ds2 :b)
+
+
+

inner-join [8 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:right.a:right.c:d:e
1094t1aX4
108c2tX5
107a5aX6
1064r4tX7
1053t3aX
10422bX8
1031s1lX1
1022beX1
+
+
+
+
(tc/inner-join ds2 ds1 :b)
+
+
+

inner-join [8 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:d:e:right.a:right.c
102eX12b
1031lX11s
1042bX82
1053aX3t
1064tX74r
1075aX6a
1082tX5c
1091aX44t
+
+
+
+
(tc/inner-join ds1 ds2 [:a :b])
+
+
+

inner-join [4 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:right.a:right.b:right.c:d:e
4106r4106tX7
3105t3105aX
21042104bX8
1103s1103lX1
+
+
+
+
(tc/inner-join ds2 ds1 [:a :b])
+
+
+

inner-join [4 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e:right.a:right.b:right.c
1103lX11103s
2104bX82104
3105aX3105t
4106tX74106r
+
+
+
+
(tc/inner-join ds1 ds2 {:left :a :right :e})
+
+
+

inner-join [9 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:right.a:right.b:right.c:d
3105t110dX
4106r1109aX
4109t1109aX
107a3105aX
108c3105aX
1101a1103lX
1103s1103lX
1101a102eX
1103s102eX
+
+
+
+
(tc/inner-join ds2 ds1 {:left :e :right :a})
+
+
+

inner-join [9 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:e:a:b:c:d:right.b:right.c
11103lX101a
1102eX101a
11103lX103s
1102eX103s
3110dX105t
41109aX106r
3105aX107a
3105aX108c
41109aX109t
+
+
+
+

Full

+

Join keeping all rows

+
+
(tc/full-join ds1 ds2 :b)
+
+
+

outer-join [10 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:right.a:right.c:d:e
1094t1aX4
108c2tX5
107a5aX6
1064r4tX7
1053t3aX
10422bX8
1031s1lX1
1022beX1
1011a
110dX3
+
+
+
+
(tc/full-join ds2 ds1 :b)
+
+
+

outer-join [10 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b:a:c:d:e:right.a:right.c
102eX12b
1031lX11s
1042bX82
1053aX3t
1064tX74r
1075aX6a
1082tX5c
1091aX44t
110dX3
1011a
+
+
+
+
(tc/full-join ds1 ds2 [:a :b])
+
+
+

outer-join [14 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:right.a:right.b:right.c:d:e
4106r4106tX7
3105t3105aX
21042104bX8
1103s1103lX1
1101a
2102b
107a
108c
4109t
110dX3
1109aX4
2108tX5
5107aX6
102eX1
+
+
+
+
(tc/full-join ds2 ds1 [:a :b])
+
+
+

outer-join [14 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e:right.a:right.b:right.c
1103lX11103s
2104bX82104
3105aX3105t
4106tX74106r
110dX3
1109aX4
2108tX5
5107aX6
102eX1
1101a
2102b
107a
108c
4109t
+
+
+
+
(tc/full-join ds1 ds2 {:left :a :right :e})
+
+
+

outer-join [15 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:e:right.a:right.b:right.c:d
3105t3110dX
4106r41109aX
4109t41109aX
107a3105aX
108c3105aX
1101a11103lX
1103s11103lX
1101a1102eX
1103s1102eX
2102b
2104
52108tX
65107aX
74106tX
82104bX
+
+
+
+
(tc/full-join ds2 ds1 {:left :e :right :a})
+
+
+

outer-join [15 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:e:a:b:c:d:right.a:right.b:right.c
11103lX1101a
1102eX1101a
11103lX1103s
1102eX1103s
3110dX3105t
41109aX4106r
3105aX107a
3105aX108c
41109aX4109t
52108tX
65107aX
74106tX
82104bX
2102b
2104
+
+
+
+

Semi

+

Return rows from ds1 matching ds2

+
+
(tc/semi-join ds1 ds2 :b)
+
+
+

_unnamed [8 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
4109t
108c
107a
4106r
3105t
2104
1103s
2102b
+
+
+
+
(tc/semi-join ds2 ds1 :b)
+
+
+

_unnamed [8 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
102eX1
1103lX1
2104bX8
3105aX
4106tX7
5107aX6
2108tX5
1109aX4
+
+
+
+
(tc/semi-join ds1 ds2 [:a :b])
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
4106r
3105t
2104
1103s
+
+
+
+
(tc/semi-join ds2 ds1 [:a :b])
+
+
+

_unnamed [4 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
1103lX1
2104bX8
3105aX
4106tX7
+
+
+
+
(tc/semi-join ds1 ds2 {:left :a :right :e})
+
+
+

_unnamed [7 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
3105t
4106r
4109t
107a
108c
1101a
1103s
+
+
+
+
(tc/semi-join ds2 ds1 {:left :e :right :a})
+
+
+

_unnamed [5 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
1103lX1
102eX1
110dX3
1109aX4
3105aX
+
+
+
+

Anti

+

Return rows from ds1 not matching ds2

+
+
(tc/anti-join ds1 ds2 :b)
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:a:b:c
1101a
+
+
+
+
(tc/anti-join ds2 ds1 :b)
+
+
+

_unnamed [1 5]:

+ + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
110dX3
+
+
+
+
(tc/anti-join ds1 ds2 [:a :b])
+
+
+

_unnamed [5 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
1101a
2102b
107a
108c
4109t
+
+
+
+
(tc/anti-join ds1 ds2 {:left :a :right :e})
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:a:b:c
2102b
2104
+
+
+
+
(tc/anti-join ds2 ds1 {:left :e :right :a})
+
+
+

_unnamed [4 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
2108tX5
5107aX6
4106tX7
2104bX8
+
+
+
+

Hashing

+

When :hashing option is used, data from join columns are preprocessed by applying join-columns funtion with :result-type set to the value of :hashing. This helps to create custom joining behaviour. Function used for hashing will get vector of row values from join columns.

+

In the following example we will join columns on value modulo 5.

+
+
(tc/left-join ds1 ds2 :b {:hashing (fn [[v]] (mod v 5))})
+
+
+

left-outer-join [16 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:right.a:right.b:right.c:d:e
3105t110dX3
21041109aX4
4109t1109aX4
1103s2108tX5
108c2108tX5
2102b5107aX6
107a5107aX6
1101a4106tX7
4106r4106tX7
3105t3105aX
21042104bX8
4109t2104bX8
1103s1103lX1
108c1103lX1
2102b102eX1
107a102eX1
+
+
+
+

Cross

+

Cross product from selected columns

+
+
(tc/cross-join ds1 ds2 [:a :b])
+
+
+

cross-join [81 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:right.a:right.b
1101110
11011109
11012108
11015107
11014106
11013105
11012104
11011103
1101102
2102110
1081103
108102
4109110
41091109
41092108
41095107
41094106
41093105
41092104
41091103
4109102
+
+
+
+
(tc/cross-join ds1 ds2 {:left [:a :b] :right :e})
+
+
+

cross-join [81 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:e
11013
11014
11015
11016
11017
1101
11018
11011
11011
21023
1081
1081
41093
41094
41095
41096
41097
4109
41098
41091
41091
+
+
+
+

Expand

+

Similar to cross product but works on a single dataset.

+
+
(tc/expand ds2 :a :c :d)
+
+
+

cross-join [36 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:c:d
dX
aX
tX
bX
lX
eX
1dX
1aX
1tX
1bX
4aX
4tX
4bX
4lX
4eX
3dX
3aX
3tX
3bX
3lX
3eX
+
+
+

Columns can be also bundled (nested) in tuples which are treated as a single entity during cross product.

+
+
(tc/expand ds2 [:a :c] [:e :b])
+
+
+

cross-join [81 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:c:e:b
d3110
d4109
d5108
d6107
d7106
d105
d8104
d1103
d1102
1a3110
1l1103
1l1102
e3110
e4109
e5108
e6107
e7106
e105
e8104
e1103
e1102
+
+
+
+

Complete

+

Same as expand with all other columns preserved (filled with missing values if necessary).

+
+
(tc/complete ds2 :a :c :d)
+
+
+

left-outer-join [36 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:c:d:b:e
dX1103
1aX1094
2tX1085
5aX1076
4tX1067
3aX105
2bX1048
1lX1031
eX1021
aX
5eX
4dX
4aX
4bX
4lX
4eX
3dX
3tX
3bX
3lX
3eX
+
+
+
+
(tc/complete ds2 [:a :c] [:e :b])
+
+
+

left-outer-join [81 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:c:e:b:d
d3110X
1a4109X
2t5108X
5a6107X
4t7106X
3a105X
2b8104X
1l1103X
e1102X
d4109
1l105
1l8104
1l1102
e3110
e4109
e5108
e6107
e7106
e105
e8104
e1103
+
+
+
+

asof

+
+
(def left-ds (tc/dataset {:a [1 5 10]
+                          :left-val ["a" "b" "c"]}))
+
+
+
(def right-ds (tc/dataset {:a [1 2 3 6 7]
+                           :right-val [:a :b :c :d :e]}))
+
+
+
left-ds
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:a:left-val
1a
5b
10c
+
+
+
right-ds
+
+
+

_unnamed [5 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:right-val
1:a
2:b
3:c
6:d
7:e
+
+
+
(tc/asof-join left-ds right-ds :a)
+
+
+

asof-<= [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:left-val:right.a:right-val
1a1:a
5b6:d
10c
+
+
+
(tc/asof-join left-ds right-ds :a {:asof-op :nearest})
+
+
+

asof-nearest [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:left-val:right.a:right-val
1a1:a
5b6:d
10c7:e
+
+
+
(tc/asof-join left-ds right-ds :a {:asof-op :>=})
+
+
+

asof->= [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:left-val:right.a:right-val
1a1:a
5b3:c
10c7:e
+
+
+
+

Concat

+

contact joins rows from other datasets

+
+
(tc/concat ds1)
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
+
+
+

concat-copying ensures all readers are evaluated.

+
+
(tc/concat-copying ds1)
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
+
+
+
+
(tc/concat ds1 (tc/drop-columns ds2 :d))
+
+
+

_unnamed [18 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:e
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
110d3
1109a4
2108t5
5107a6
4106t7
3105a
2104b8
1103l1
102e1
+
+
+
+
^:note-to-test/skip
+(apply tc/concat (repeatedly 3 #(tc/random DS)))
+
+
+

_unnamed [27 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
221.0B
170.5A
221.0B
240.5A
281.0B
170.5A
221.0B
281.0B
281.0B
110.5A
151.0B
240.5A
221.0B
221.0B
110.5A
131.5C
170.5A
151.0B
261.5C
221.0B
+
+
+
Concat grouped dataset
+

Concatenation of grouped datasets results also in grouped dataset.

+
+
(tc/concat (tc/group-by DS [:V3])
+           (tc/group-by DS [:V4]))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
{:V3 0.5}0Group: {:V3 0.5} [3 4]:
{:V3 1.0}1Group: {:V3 1.0} [3 4]:
{:V3 1.5}2Group: {:V3 1.5} [3 4]:
{:V4 A}3Group: {:V4 “A”} [3 4]:
{:V4 B}4Group: {:V4 “B”} [3 4]:
{:V4 C}5Group: {:V4 “C”} [3 4]:
+
+
+
+
+

Union

+

The same as concat but returns unique rows

+
+
(apply tc/union (tc/drop-columns ds2 :d) (repeat 10 ds1))
+
+
+

union [18 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:e
110d3
1109a4
2108t5
5107a6
4106t7
3105a
2104b8
1103l1
102e1
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
+
+
+
+
^:note-to-test/skip
+(apply tc/union (repeatedly 10 #(tc/random DS)))
+
+
+

union [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
170.5A
151.0B
261.5C
110.5A
240.5A
281.0B
191.5C
221.0B
+
+
+
+

Bind

+

bind adds empty columns during concat

+
+
(tc/bind ds1 ds2)
+
+
+

_unnamed [18 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:e:d
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
110d3X
1109a4X
2108t5X
5107a6X
4106t7X
3105aX
2104b8X
1103l1X
102e1X
+
+
+
+
(tc/bind ds2 ds1)
+
+
+

_unnamed [18 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:d:e
110dX3
1109aX4
2108tX5
5107aX6
4106tX7
3105aX
2104bX8
1103lX1
102eX1
1101a
2102b
1103s
2104
3105t
4106r
107a
108c
4109t
+
+
+
+

Append

+

append concats columns

+
+
(tc/append ds1 ds2)
+
+
+

_unnamed [9 8]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:a:b:c:a:b:c:d:e
1101a110dX3
2102b1109aX4
1103s2108tX5
21045107aX6
3105t4106tX7
4106r3105aX
107a2104bX8
108c1103lX1
4109t102eX1
+
+
+
+

Intersection

+
+
(tc/intersect (tc/select-columns ds1 :b)
+              (tc/select-columns ds2 :b))
+
+
+

intersection [8 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:b
109
108
107
106
105
104
103
102
+
+
+
+

Difference

+
+
(tc/difference (tc/select-columns ds1 :b)
+               (tc/select-columns ds2 :b))
+
+
+

difference [1 1]:

+ + + + + + + + + + + +
:b
101
+
+
+
+
(tc/difference (tc/select-columns ds2 :b)
+               (tc/select-columns ds1 :b))
+
+
+

difference [1 1]:

+ + + + + + + + + + + +
:b
110
+
+
+
+
+

Split into train/test

+

In ML world very often you need to test given model and prepare collection of train and test datasets. split creates new dataset with two additional columns:

+
    +
  • :$split-name - with :train, :test, :split-2, … values
  • +
  • :$split-id - id of splitted group (for k-fold and repeating)
  • +
+

split-type can be one of the following:

+
    +
  • :kfold (default) - k-fold strategy, :k defines number of folds (defaults to 5), produces k splits
  • +
  • :bootstrap - :ratio defines ratio of observations put into result (defaults to 1.0), produces 1 split
  • +
  • :holdout - split into two or more parts with given ratio(s) (defaults to 2/3), produces 1 split
  • +
  • :holdouts - splits into two parts for ascending ratio. Range of rations is given by steps option
  • +
  • :loo - leave one out, produces the same number of splits as number of observations
  • +
+

:holdout can accept also probabilites or ratios and can split to more than 2 subdatasets

+

Additionally you can provide:

+
    +
  • :seed - for random number generator
  • +
  • :shuffle? - turn on/off shuffle of the rows (default: true)
  • +
  • :repeats - repeat procedure :repeats times
  • +
  • :partition-selector - same as in group-by for stratified splitting to reflect dataset structure in splits.
  • +
  • :split-names names of subdatasets different than default, ie. [:train :test :split-2 ...]
  • +
  • :split-col-name - a column where name of split is stored, either :train or :test values (default: :$split-name)
  • +
  • :split-id-col-name - a column where id of the train/test pair is stored (default: :$split-id)
  • +
+

In case of grouped dataset each group is processed separately.

+

See more

+
+
^:note-to-test/skip
+(def for-splitting (tc/dataset (map-indexed (fn [id v] {:id id
+                                                        :partition v
+                                                        :group (rand-nth [:g1 :g2 :g3])})
+                                            (concat (repeat 20 :a) (repeat 5 :b)))))
+
+
+
^:note-to-test/skip
+for-splitting
+
+
+

_unnamed [25 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
0:a:g2
1:a:g1
2:a:g1
3:a:g2
4:a:g1
5:a:g1
6:a:g2
7:a:g3
8:a:g2
9:a:g1
14:a:g3
15:a:g1
16:a:g2
17:a:g3
18:a:g1
19:a:g2
20:b:g2
21:b:g1
22:b:g2
23:b:g3
24:b:g2
+
+
+

k-Fold

+

Returns k=5 maps

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split)
+    (tc/head 30))
+
+
+

_unnamed, (splitted) [30 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
24:b:g2:train0
12:a:g1:train0
2:a:g1:train0
19:a:g2:train0
11:a:g2:train0
9:a:g1:train0
16:a:g2:train0
21:b:g1:train0
14:a:g3:train0
10:a:g2:train0
0:a:g2:train0
20:b:g2:train0
4:a:g1:train0
3:a:g2:train0
5:a:g1:train0
22:b:g2:train0
18:a:g1:train0
7:a:g3:train0
23:b:g3:train0
1:a:g1:train0
17:a:g3:test0
15:a:g1:test0
8:a:g2:test0
13:a:g3:test0
6:a:g2:test0
17:a:g3:train1
15:a:g1:train1
8:a:g2:train1
13:a:g3:train1
6:a:g2:train1
+
+

Partition according to :k column to reflect it’s distribution

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split :kfold {:partition-selector :partition})
+    (tc/head 30))
+
+
+

_unnamed, (splitted) [30 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
8:a:g2:train0
7:a:g3:train0
12:a:g1:train0
3:a:g2:train0
2:a:g1:train0
4:a:g1:train0
10:a:g2:train0
14:a:g3:train0
13:a:g3:train0
9:a:g1:train0
16:a:g2:train0
17:a:g3:train0
0:a:g2:train0
6:a:g2:train0
1:a:g1:train0
11:a:g2:train0
18:a:g1:test0
15:a:g1:test0
19:a:g2:test0
5:a:g1:test0
18:a:g1:train1
15:a:g1:train1
19:a:g2:train1
5:a:g1:train1
2:a:g1:train1
4:a:g1:train1
10:a:g2:train1
14:a:g3:train1
13:a:g3:train1
9:a:g1:train1
+
+
+
+

Bootstrap

+
+
^:note-to-test/skip
+(tc/split for-splitting :bootstrap)
+
+
+

_unnamed, (splitted) [36 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
9:a:g1:train0
5:a:g1:train0
15:a:g1:train0
3:a:g2:train0
9:a:g1:train0
22:b:g2:train0
12:a:g1:train0
22:b:g2:train0
8:a:g2:train0
8:a:g2:train0
0:a:g2:test0
2:a:g1:test0
4:a:g1:test0
10:a:g2:test0
11:a:g2:test0
13:a:g3:test0
14:a:g3:test0
18:a:g1:test0
19:a:g2:test0
21:b:g1:test0
24:b:g2:test0
+
+

with repeats, to get 100 splits

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split :bootstrap {:repeats 100})
+    (:$split-id)
+    (distinct)
+    (count))
+
+
+
100
+
+
+
+

Holdout

+

with small ratio

+
+
^:note-to-test/skip
+(tc/split for-splitting :holdout {:ratio 0.2})
+
+
+

_unnamed, (splitted) [25 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
14:a:g3:train0
9:a:g1:train0
19:a:g2:train0
7:a:g3:train0
12:a:g1:train0
15:a:g1:test0
13:a:g3:test0
0:a:g2:test0
11:a:g2:test0
6:a:g2:test0
8:a:g2:test0
16:a:g2:test0
21:b:g1:test0
20:b:g2:test0
18:a:g1:test0
24:b:g2:test0
22:b:g2:test0
4:a:g1:test0
5:a:g1:test0
1:a:g1:test0
10:a:g2:test0
+
+

you can split to more than two subdatasets with holdout

+
+
^:note-to-test/skip
+(tc/split for-splitting :holdout {:ratio [0.1 0.2 0.3 0.15 0.25]})
+
+
+

_unnamed, (splitted) [25 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
24:b:g2:train0
14:a:g3:train0
12:a:g1:test0
16:a:g2:test0
3:a:g2:test0
19:a:g2:test0
2:a:g1:test0
20:b:g2:split-20
21:b:g1:split-20
10:a:g2:split-20
7:a:g3:split-30
17:a:g3:split-30
23:b:g3:split-30
4:a:g1:split-40
11:a:g2:split-40
8:a:g2:split-40
5:a:g1:split-40
6:a:g2:split-40
18:a:g1:split-40
15:a:g1:split-40
0:a:g2:split-40
+
+

you can use also proportions with custom names

+
+
^:note-to-test/skip
+(tc/split for-splitting :holdout {:ratio [5 3 11 2]
+                                  :split-names ["small" "smaller" "big" "the rest"]})
+
+
+

_unnamed, (splitted) [25 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
2:a:g1small0
6:a:g2small0
10:a:g2small0
18:a:g1small0
22:b:g2small0
23:b:g3smaller0
9:a:g1smaller0
20:b:g2smaller0
11:a:g2big0
12:a:g1big0
4:a:g1big0
3:a:g2big0
14:a:g3big0
8:a:g2big0
17:a:g3big0
19:a:g2big0
1:a:g1big0
5:a:g1the rest0
24:b:g2the rest0
13:a:g3the rest0
16:a:g2the rest0
+
+
+
+

Holdouts

+

With ratios from 5% to 95% of the dataset with step 1.5 generates 15 splits with ascending rows in train dataset.

+
+
^:note-to-test/skip
+(-> (tc/split for-splitting :holdouts {:steps [0.05 0.95 1.5]
+                                       :shuffle? false})
+    (tc/group-by [:$split-id :$split-name]))
+
+
+

_unnamed [30 3]:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
{:\(split-id 0, :\)split-name :train}0Group: {:\(split-id 0, :\)split-name :train} [1 5]:
{:\(split-id 0, :\)split-name :test}1Group: {:\(split-id 0, :\)split-name :test} [24 5]:
{:\(split-id 1, :\)split-name :train}2Group: {:\(split-id 1, :\)split-name :train} [2 5]:
{:\(split-id 1, :\)split-name :test}3Group: {:\(split-id 1, :\)split-name :test} [23 5]:
{:\(split-id 2, :\)split-name :train}4Group: {:\(split-id 2, :\)split-name :train} [4 5]:
{:\(split-id 2, :\)split-name :test}5Group: {:\(split-id 2, :\)split-name :test} [21 5]:
{:\(split-id 3, :\)split-name :train}6Group: {:\(split-id 3, :\)split-name :train} [5 5]:
{:\(split-id 3, :\)split-name :test}7Group: {:\(split-id 3, :\)split-name :test} [20 5]:
{:\(split-id 4, :\)split-name :train}8Group: {:\(split-id 4, :\)split-name :train} [7 5]:
{:\(split-id 4, :\)split-name :test}9Group: {:\(split-id 4, :\)split-name :test} [18 5]:
{:\(split-id 9, :\)split-name :test}19Group: {:\(split-id 9, :\)split-name :test} [11 5]:
{:\(split-id 10, :\)split-name :train}20Group: {:\(split-id 10, :\)split-name :train} [16 5]:
{:\(split-id 10, :\)split-name :test}21Group: {:\(split-id 10, :\)split-name :test} [9 5]:
{:\(split-id 11, :\)split-name :train}22Group: {:\(split-id 11, :\)split-name :train} [17 5]:
{:\(split-id 11, :\)split-name :test}23Group: {:\(split-id 11, :\)split-name :test} [8 5]:
{:\(split-id 12, :\)split-name :train}24Group: {:\(split-id 12, :\)split-name :train} [19 5]:
{:\(split-id 12, :\)split-name :test}25Group: {:\(split-id 12, :\)split-name :test} [6 5]:
{:\(split-id 13, :\)split-name :train}26Group: {:\(split-id 13, :\)split-name :train} [20 5]:
{:\(split-id 13, :\)split-name :test}27Group: {:\(split-id 13, :\)split-name :test} [5 5]:
{:\(split-id 14, :\)split-name :train}28Group: {:\(split-id 14, :\)split-name :train} [22 5]:
{:\(split-id 14, :\)split-name :test}29Group: {:\(split-id 14, :\)split-name :test} [3 5]:
+
+
+
+

Leave One Out

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split :loo)
+    (tc/head 30))
+
+
+

_unnamed, (splitted) [30 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group:\(split-name | :\)split-id
13:a:g3:train0
5:a:g1:train0
19:a:g2:train0
15:a:g1:train0
0:a:g2:train0
18:a:g1:train0
6:a:g2:train0
12:a:g1:train0
10:a:g2:train0
1:a:g1:train0
17:a:g3:train0
3:a:g2:train0
24:b:g2:train0
7:a:g3:train0
16:a:g2:train0
23:b:g3:train0
20:b:g2:train0
8:a:g2:train0
11:a:g2:train0
14:a:g3:train0
4:a:g1:train0
21:b:g1:train0
2:a:g1:train0
22:b:g2:train0
9:a:g1:test0
9:a:g1:train1
5:a:g1:train1
19:a:g2:train1
15:a:g1:train1
0:a:g2:train1
+
+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split :loo)
+    (tc/row-count))
+
+
+
625
+
+
+
+

Grouped dataset with partitioning

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/group-by :group)
+    (tc/split :bootstrap {:partition-selector :partition :seed 11 :ratio 0.8}))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
:g20Group: :g2, (splitted) [13 5]:
:g11Group: :g1, (splitted) [11 5]:
:g32Group: :g3, (splitted) [7 5]:
+
+
+
+

Split as a sequence

+

To get a sequence of pairs, use split->seq function

+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/split->seq :kfold {:partition-selector :partition})
+    (first))
+
+
+

{

+ ++++ + + + + + + +
+
:train
+
+
+

Group: 0 [20 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
17:a:g3
2:a:g1
10:a:g2
6:a:g2
9:a:g1
19:a:g2
0:a:g2
18:a:g1
1:a:g1
16:a:g2
15:a:g1
7:a:g3
14:a:g3
5:a:g1
13:a:g3
8:a:g2
24:b:g2
23:b:g3
22:b:g2
21:b:g1
+
+
+ ++++ + + + + + + +
+
:test
+
+
+

Group: 0 [5 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
12:a:g1
11:a:g2
4:a:g1
3:a:g2
20:b:g2
+
+
+

}

+
+
+
^:note-to-test/skip
+(-> for-splitting
+    (tc/group-by :group)
+    (tc/split->seq :bootstrap {:partition-selector :partition :seed 11 :ratio 0.8 :repeats 2})
+    (first))
+
+
+

[

:g2
+

(

{

+ ++++ + + + + + + +
+
:train
+
+
+

Group: 0 [10 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
11:a:g2
16:a:g2
10:a:g2
3:a:g2
0:a:g2
6:a:g2
6:a:g2
22:b:g2
22:b:g2
20:b:g2
+
+
+ ++++ + + + + + + +
+
:test
+
+
+

Group: 0 [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
8:a:g2
19:a:g2
24:b:g2
+
+
+

}

{

+ ++++ + + + + + + +
+
:train
+
+
+

Group: 1 [10 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:id:partition:group
3:a:g2
6:a:g2
11:a:g2
19:a:g2
0:a:g2
8:a:g2
10:a:g2
22:b:g2
20:b:g2
24:b:g2
+
+
+ ++++ + + + + + + +
+
:test
+
+
+

Group: 1 [1 3]:

+ + + + + + + + + + + + + + + +
:id:partition:group
16:a:g2
+
+
+

}

)

]

+
+
+
+
+
+

Column API

+

A column in tablecloth is a named sequence of typed data. It is the building block of the dataset since datasets are at base just a map of columns. Like dataset, the column is defined within tech.ml.dataset. In this section, we will show how you can interact with the column by itself.

+

Let’s begin by requiring the Column API, which we suggest you alias as tcc:

+
+
(require '[tablecloth.column.api :as tcc])
+
+
+

Creation & Identity

+

Creating an empty column is pretty straightforward:

+
+
(tcc/column)
+
+
+
#tech.v3.dataset.column<boolean>[0]
+null
+[]
+
+

You can also create a Column from a vector or a sequence

+
+
(tcc/column [1 2 3 4 5])
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[1, 2, 3, 4, 5]
+
+
+
(tcc/column `(1 2 3 4 5))
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[1, 2, 3, 4, 5]
+
+

You can also quickly create columns of ones or zeros:

+
+
(tcc/ones 10)
+
+
+
#tech.v3.dataset.column<int64>[10]
+null
+[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+
+
+
(tcc/zeros 10)
+
+
+
#tech.v3.dataset.column<int64>[10]
+null
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+

Finally, to identify a column, you can use the column? function to check if an item is a column:

+
+
(tcc/column? [1 2 3 4 5])
+
+
+
false
+
+
+
(tcc/column? (tcc/column))
+
+
+
true
+
+

Tablecloth’s datasets of course consists of columns:

+
+
(tcc/column? (-> (tc/dataset {:a [1 2 3 4 5]})
+                 :a))
+
+
+
true
+
+
+
+

Datatypes

+

The default set of types for a column are defined in the underlying “tech ml” system. We can see the set here:

+
+
(tech.v3.datatype.casting/all-datatypes)
+
+
+
[:array-buffer
+ :big-integer
+ :bitmap
+ :bool
+ :boolean
+ :byte
+ :char
+ :dataset
+ :days
+ :decimal
+ :double
+ :duration
+ :epoch-days
+ :epoch-hours
+ :epoch-microseconds
+ :epoch-milliseconds
+ :epoch-nanoseconds
+ :epoch-seconds
+ :float
+ :float32
+ :float64
+ :hours
+ :instant
+ :int
+ :int16
+ :int32
+ :int64
+ :int8
+ :keyword
+ :list
+ :local-date
+ :local-date-time
+ :local-time
+ :long
+ :microseconds
+ :milliseconds
+ :nanoseconds
+ :native-buffer
+ :object
+ :packed-duration
+ :packed-instant
+ :packed-local-date
+ :packed-local-time
+ :persistent-map
+ :persistent-set
+ :persistent-vector
+ :seconds
+ :short
+ :string
+ :tensor
+ :text
+ :time-microseconds
+ :time-milliseconds
+ :time-nanoseconds
+ :time-seconds
+ :uint16
+ :uint32
+ :uint64
+ :uint8
+ :uuid
+ :weeks
+ :years
+ :zoned-date-time]
+
+

When you create a column, the underlying system will try to autodetect its type. We can illustrate that behavior using the tcc/typeof function to check the type of a column:

+
+
(-> (tcc/column [1 2 3 4 5])
+    (tcc/typeof))
+
+
+
:int64
+
+
+
(-> (tcc/column [:a :b :c :d :e])
+    (tcc/typeof))
+
+
+
:keyword
+
+

Columns containing heterogenous data will receive type :object:

+
+
(-> (tcc/column [1 :b 3 :c 5])
+    (tcc/typeof))
+
+
+
:object
+
+

You can also use the tcc/typeof? function to check the value of a function as an asssertion:

+
+
(-> (tcc/column [1 2 3 4 6])
+    (tcc/typeof? :boolean))
+
+
+
false
+
+
+
(-> (tcc/column [1 2 3 4 6])
+    (tcc/typeof? :int64))
+
+
+
true
+
+

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 :int64, which is also of the general type :integer.

+

The typeof? function supports checking both.

+
+
(-> (tcc/column [1 2 3 4 6])
+    (tcc/typeof? :int64))
+
+
+
true
+
+
+
(-> (tcc/column [1 2 3 4 6])
+    (tcc/typeof? :integer))
+
+
+
true
+
+
+
+

Access & Manipulation

+
+

Column Access

+

The method for accessing a particular index position in a column is the same as for Clojure vectors:

+
+
(-> (tcc/column [1 2 3 4 5])
+    (get 3))
+
+
+
4
+
+
+
(-> (tcc/column [1 2 3 4 5])
+    (nth 3))
+
+
+
4
+
+
+
+

Slice

+

You can also slice a column

+
+
(-> (tcc/column (range 10))
+    (tcc/slice 5))
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[5, 6, 7, 8, 9]
+
+
+
(-> (tcc/column (range 10))
+    (tcc/slice 1 4))
+
+
+
#tech.v3.dataset.column<int64>[4]
+null
+[1, 2, 3, 4]
+
+
+
(-> (tcc/column (range 10))
+    (tcc/slice 0 9 2))
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[0, 2, 4, 6, 8]
+
+

The slice method supports the :end and :start keywords, which can add clarity to your code:

+
+
(-> (tcc/column (range 10))
+    (tcc/slice :start :end 2))
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[0, 2, 4, 6, 8]
+
+
+
+

Select

+

If you need to create a discontinuous subset of the column, you can use the select 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:

+

Select the values at index positions 1 and 9:

+
+
(-> (tcc/column (range 10))
+    (tcc/select [1 9]))
+
+
+
#tech.v3.dataset.column<int64>[2]
+null
+[1, 9]
+
+

Select the values at index positions 0 and 2 using booelan select:

+
+
(-> (tcc/column (range 10))
+    (tcc/select (tcc/column [true false true])))
+
+
+
#tech.v3.dataset.column<int64>[2]
+null
+[0, 2]
+
+

The boolean select feature is particularly useful when used with the large number of operations (see below) that the Column API includes:

+
+
(let [col (tcc/column (range 10))]
+  (-> col
+      (tcc/odd?)
+      (->> (tcc/select col))))
+
+
+
#tech.v3.dataset.column<int64>[5]
+null
+[1, 3, 5, 7, 9]
+
+

Here, we used the odd? operator to create a boolean array indicating all index positions in the Column that included an odd value. Then we pass that boolean Column to the select function using the ->> to send it to the second argument position.

+
+
+

Sort

+

Use sort-column to sort a column:

+

Default sort is in ascending order:

+
+
(-> (tcc/column [:c :z :a :f])
+    (tcc/sort-column))
+
+
+
#tech.v3.dataset.column<keyword>[4]
+null
+[:a, :c, :f, :z]
+
+

You can provide the :desc and :asc keywords to change the default behavior:

+
+
(-> (tcc/column [:c :z :a :f])
+    (tcc/sort-column :desc))
+
+
+
#tech.v3.dataset.column<keyword>[4]
+null
+[:z, :f, :c, :a]
+
+

You can also provide a comparator fn:

+
+
(-> (tcc/column [{:position 2
+                  :text "and then stopped"}
+                 {:position 1
+                  :text "I ran fast"}])
+    (tcc/sort-column (fn [a b] (< (:position a) (:position b)))))
+
+
+
#tech.v3.dataset.column<persistent-map>[2]
+null
+[{:position 1, :text "I ran fast"}, {:position 2, :text "and then stopped"}]
+
+
+
+
+

Operations

+

The Column API contains a large number of operations, which have been lifted from the underlying tech.ml.dataset library and adapted to Tablecloth. 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 operation. In other words, their signature is this general form:

+
(column1 column2 ...) => column | scalar
+

Because these functions all take a column as the first argument, they are easy to use with the pipe -> macro, as with all functions in Tablecloth.

+

Given the incredibly large number of available functions, we will illustrate instead how these operations can be used.

+

We’ll start with two columns a and b:

+
+
(def a (tcc/column [20 30 40 50]))
+
+
+
(def b (tcc/column (range 4)))
+
+

Here is a small sampling of operator functions:

+
+
(tcc/- a b)
+
+
+
#tech.v3.dataset.column<int64>[4]
+null
+[20, 29, 38, 47]
+
+
+
(tcc/* a b)
+
+
+
#tech.v3.dataset.column<int64>[4]
+null
+[0, 30, 80, 150]
+
+
+
(tcc// a 2)
+
+
+
#tech.v3.dataset.column<int64>[4]
+null
+[10, 15, 20, 25]
+
+
+
(tcc/pow a 2)
+
+
+
#tech.v3.dataset.column<float64>[4]
+null
+[400.0, 900.0, 1600, 2500]
+
+
+
(tcc/* 10 (tcc/sin a))
+
+
+
#tech.v3.dataset.column<float64>[4]
+null
+[9.129, -9.880, 7.451, -2.624]
+
+
+
(tcc/< a 35)
+
+
+
#tech.v3.dataset.column<boolean>[4]
+null
+[true, true, false, false]
+
+

All these operations take a column as their first argument and return a column, so they can be chained easily.

+
+
(-> a
+    (tcc/* b)
+    (tcc/< 70))
+
+
+
#tech.v3.dataset.column<boolean>[4]
+null
+[true, true, false, false]
+
+
+
+

Missing Values

+

The column API includes utilities to handle missing values. Let’s go through them.

+

We start with a column that has missing values:

+
+
(def missing (tcc/column [1 2 nil 8 10 nil 20]))
+
+
+
missing
+
+
+
#tech.v3.dataset.column<int64>[7]
+null
+[1, 2, , 8, 10, , 20]
+
+

In many cases, running an operation on this column will still work without any handling:

+
+
(tcc/* missing (tcc/column [1 2 3 4 5 6 7]))
+
+
+
#tech.v3.dataset.column<float64>[7]
+null
+[1.000, 4.000, , 32.00, 50.00, , 140.0]
+
+

You can count the number of missing values.

+
+
(tcc/count-missing missing)
+
+
+
2
+
+

You can drop them.

+
+
(tcc/drop-missing missing)
+
+
+
#tech.v3.dataset.column<int64>[5]
+:col
+[1, 2, 8, 10, 20]
+
+

You can replace them. This function includes different strategies, and the default is :nearest.

+
    +
  • :down - Take the previous value, or use provided value.
  • +
  • :up - Take the next value, or use provided value.
  • +
  • :downup - Take the previous value, otherwise take the next value.
  • +
  • :updown - Take the next value, otherwise take the previous value.
  • +
  • :nearest - Use the nearest of next or previous values. (Strategy :mid is an alias for :nearest).
  • +
  • :midpoint - Use the midpoint of averaged values between previous and next (non-missing) values.
  • +
  • :abb - Impute missing value with approximate Bayesian bootstrap. See r’s ABB.
  • +
  • :lerp - Linearly interpolate values between previous and next nonmissing rows.
  • +
  • :value - Provide a value explicitly. Value may be a function in which case it will be called on the column with missing values elided and the return will be used to as the filler.
  • +
+
+
(tcc/replace-missing missing)
+
+
+
#tech.v3.dataset.column<float64>[7]
+:col
+[1, 2, 2, 8, 10, 10, 20]
+
+
+
(tcc/replace-missing missing :value 100)
+
+
+
#tech.v3.dataset.column<float64>[7]
+:col
+[1, 2, 100.0, 8, 10, 100.0, 20]
+
+
+
+
+

Pipeline

+

tablecloth.pipeline exports special versions of API which create functions operating only on dataset. This creates the possibility to chain operations and compose them easily.

+

There are two ways to create pipelines:

+
    +
  • functional, as a composition of functions
  • +
  • declarative, separating task declarations and concrete parametrization.
  • +
+

Pipeline operations are prepared to work with metamorph library. That means that result of the pipeline is wrapped into a map and dataset is stored under :metamorph/data key.

+
+

Warning: Duplicated metamorph pipeline functions are removed from tablecloth.pipeline namespace.

+
+
+
+

Other examples

+
+

Stocks

+
+
(defonce stocks (tc/dataset "https://raw.githubusercontent.com/techascent/tech.ml.dataset/master/test/data/stocks.csv" {:key-fn keyword}))
+
+
+
stocks
+
+
+

https://raw.githubusercontent.com/techascent/tech.ml.dataset/master/test/data/stocks.csv [560 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:symbol:date:price
MSFT2000-01-0139.81
MSFT2000-02-0136.35
MSFT2000-03-0143.22
MSFT2000-04-0128.37
MSFT2000-05-0125.45
MSFT2000-06-0132.54
MSFT2000-07-0128.40
MSFT2000-08-0128.40
MSFT2000-09-0124.53
MSFT2000-10-0128.02
AAPL2009-05-01135.81
AAPL2009-06-01142.43
AAPL2009-07-01163.39
AAPL2009-08-01168.21
AAPL2009-09-01185.35
AAPL2009-10-01188.50
AAPL2009-11-01199.91
AAPL2009-12-01210.73
AAPL2010-01-01192.06
AAPL2010-02-01204.62
AAPL2010-03-01223.02
+
+
+
(-> stocks
+    (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]))
+
+
+

_unnamed [51 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:symbol:yearsummary
AAPL200021.74833333
AAPL200110.17583333
AAPL20029.40833333
AAPL20039.34750000
AAPL200418.72333333
AAPL200548.17166667
AAPL200672.04333333
AAPL2007133.35333333
AAPL2008138.48083333
AAPL2009150.39333333
MSFT200029.67333333
MSFT200125.34750000
MSFT200221.82666667
MSFT200320.93416667
MSFT200422.67416667
MSFT200523.84583333
MSFT200624.75833333
MSFT200729.28416667
MSFT200825.20833333
MSFT200922.87250000
MSFT201028.50666667
+
+
+
(-> stocks
+    (tc/group-by (juxt :symbol #(tech.v3.datatype.datetime/long-temporal-field :years (% :date))))
+    (tc/aggregate #(tech.v3.datatype.functional/mean (% :price)))
+    (tc/rename-columns {:$group-name-0 :symbol
+                        :$group-name-1 :year}))
+
+
+

_unnamed [51 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:symbol:yearsummary
MSFT200029.67333333
MSFT200125.34750000
MSFT200221.82666667
MSFT200320.93416667
MSFT200422.67416667
MSFT200523.84583333
MSFT200624.75833333
MSFT200729.28416667
MSFT200825.20833333
MSFT200922.87250000
AAPL200021.74833333
AAPL200110.17583333
AAPL20029.40833333
AAPL20039.34750000
AAPL200418.72333333
AAPL200548.17166667
AAPL200672.04333333
AAPL2007133.35333333
AAPL2008138.48083333
AAPL2009150.39333333
AAPL2010206.56666667
+
+
+
+

data.table

+

Below you can find comparizon between functionality of data.table and Clojure dataset API. I leave it without comments, please refer original document explaining details:

+

Introduction to data.table

+

R

+
+
library(data.table)
+library(knitr)
+
+flights <- fread("https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv")
+
+kable(head(flights))
+
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
2014111413AAJFKLAX35924759
201411-313AAJFKLAX363247511
20141129AAJFKLAX351247519
201411-8-26AALGAPBI15710357
20141121AAJFKLAX350247513
20141140AAEWRLAX339245418
+
+
+

Clojure

+
+
(require '[tech.v3.datatype.functional :as dfn]
+         '[tech.v3.datatype.argops :as aops]
+         '[tech.v3.datatype :as dtype])
+
+
+
(defonce flights (tc/dataset "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"))
+
+
+
(tc/head flights 6)
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
2014111413AAJFKLAX35924759
201411-313AAJFKLAX363247511
20141129AAJFKLAX351247519
201411-8-26AALGAPBI15710357
20141121AAJFKLAX350247513
20141140AAEWRLAX339245418
+
+
+

Basics

+
+
Shape of loaded data
+

R

+
+
dim(flights)
+
+
[1] 253316     11
+
+
+

Clojure

+
+
(tc/shape flights)
+
+
+
[253316 11]
+
+
+
+
What is data.table?
+

R

+
+
DT = data.table(
+ID = c("b","b","b","a","a","c"),
+a = 1:6,
+b = 7:12,
+c = 13:18
+)
+
+kable(DT)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDabc
b1713
b2814
b3915
a41016
a51117
c61218
+
+
class(DT$ID)
+
+
[1] "character"
+
+
+

Clojure

+
+
(def DT (tc/dataset {:ID ["b" "b" "b" "a" "a" "c"]
+                     :a (range 1 7)
+                     :b (range 7 13)
+                     :c (range 13 19)}))
+
+
+
DT
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:ID:a:b:c
b1713
b2814
b3915
a41016
a51117
c61218
+
+
+
(-> :ID DT meta :datatype)
+
+
+
:string
+
+
+
+
Get all the flights with “JFK” as the origin airport in the month of June.
+

R

+
+
ans <- flights[origin == "JFK" & month == 6L]
+kable(head(ans))
+
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
201461-9-5AAJFKLAX32424758
201461-10-13AAJFKLAX329247512
20146118-1AAJFKLAX32624757
201461-6-16AAJFKLAX320247510
201461-4-45AAJFKLAX326247518
201461-6-23AAJFKLAX329247514
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows (fn [row] (and (= (get row "origin") "JFK")
+                                   (= (get row "month") 6))))
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
201461-9-5AAJFKLAX32424758
201461-10-13AAJFKLAX329247512
20146118-1AAJFKLAX32624757
201461-6-16AAJFKLAX320247510
201461-4-45AAJFKLAX326247518
201461-6-23AAJFKLAX329247514
+
+
+
+
Get the first two rows from flights.
+

R

+
+
ans <- flights[1:2]
+kable(ans)
+
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
2014111413AAJFKLAX35924759
201411-313AAJFKLAX363247511
+
+
+

Clojure

+
+
(tc/select-rows flights (range 2))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [2 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
2014111413AAJFKLAX35924759
201411-313AAJFKLAX363247511
+
+
+
+
Sort flights first by column origin in ascending order, and then by dest in descending order
+

R

+
+
ans <- flights[order(origin, -dest)]
+kable(head(ans))
+
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
201415649EVEWRXNA19511318
201416713EVEWRXNA19011318
201417-6-13EVEWRXNA17911318
201418-7-12EVEWRXNA18411318
201419167EVEWRXNA18111318
20141136666EVEWRXNA18811319
+
+
+

Clojure

+
+
(-> flights
+    (tc/order-by ["origin" "dest"] [:asc :desc])
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
201463-6-38EVEWRXNA15411316
2014120-9-17EVEWRXNA17711318
2014319-610EVEWRXNA20111316
201423231268EVEWRXNA184113112
2014425-8-32EVEWRXNA15911316
20142192110EVEWRXNA17611318
+
+
+
+
Select arr_delay column, but return it as a vector
+

R

+
+
ans <- flights[, arr_delay]
+head(ans)
+
+
[1]  13  13   9 -26   1   0
+
+
+

Clojure

+
+
(take 6 (flights "arr_delay"))
+
+
+
(13 13 9 -26 1 0)
+
+
+
+
Select arr_delay column, but return as a data.table instead
+

R

+
+
ans <- flights[, list(arr_delay)]
+kable(head(ans))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
arr_delay
13
13
9
-26
1
0
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-columns "arr_delay")
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
arr_delay
13
13
9
-26
1
0
+
+
+
+
Select both arr_delay and dep_delay columns
+

R

+

``

+

Clojure

+
+
(-> flights
+    (tc/select-columns ["arr_delay" "dep_delay"])
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
arr_delaydep_delay
1314
13-3
92
-26-8
12
04
+
+
+
+
Select both arr_delay and dep_delay columns and rename them to delay_arr and delay_dep
+

R

+
+
ans <- flights[, .(delay_arr = arr_delay, delay_dep = dep_delay)]
+kable(head(ans))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
delay_arrdelay_dep
1314
13-3
92
-26-8
12
04
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-columns {"arr_delay" "delay_arr"
+                        "dep_delay" "delay_arr"})
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
delay_arrdelay_arr
1314
13-3
92
-26-8
12
04
+
+
+
+
How many trips have had total delay < 0?
+

R

+
+
ans <- flights[, sum( (arr_delay + dep_delay) < 0 )]
+ans
+
+
[1] 141814
+
+
+

Clojure

+
+
(->> (dfn/+ (flights "arr_delay") (flights "dep_delay"))
+     (aops/argfilter #(< % 0.0))
+     (dtype/ecount))
+
+
+
141814
+
+

or pure Clojure functions (much, much slower)

+
+
(->> (map + (flights "arr_delay") (flights "dep_delay"))
+     (filter neg?)
+     (count))
+
+
+
141814
+
+
+
+
Calculate the average arrival and departure delay for all flights with “JFK” as the origin airport in the month of June
+

R

+
+
ans <- flights[origin == "JFK" & month == 6L,
+.(m_arr = mean(arr_delay), m_dep = mean(dep_delay))]
+kable(ans)
+
+ + + + + + + + + + + + + +
m_arrm_dep
5.8393499.807884
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows (fn [row] (and (= (get row "origin") "JFK")
+                                   (= (get row "month") 6))))
+    (tc/aggregate {:m_arr #(dfn/mean (% "arr_delay"))
+                   :m_dep #(dfn/mean (% "dep_delay"))}))
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:m_arr:m_dep
5.839349329.80788411
+
+
+
+
How many trips have been made in 2014 from “JFK” airport in the month of June?
+

R

+
+
ans <- flights[origin == "JFK" & month == 6L, length(dest)]
+ans
+
+
[1] 8422
+
+
+

or

+
+
ans <- flights[origin == "JFK" & month == 6L, .N]
+ans
+
+
[1] 8422
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows (fn [row] (and (= (get row "origin") "JFK")
+                                   (= (get row "month") 6))))
+    (tc/row-count))
+
+
+
8422
+
+
+
+
deselect columns using - or !
+

R

+
+
ans <- flights[, !c("arr_delay", "dep_delay")]
+kable(head(ans))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaycarrierorigindestair_timedistancehour
201411AAJFKLAX35924759
201411AAJFKLAX363247511
201411AAJFKLAX351247519
201411AALGAPBI15710357
201411AAJFKLAX350247513
201411AAEWRLAX339245418
+
+
+

or

+
+
ans <- flights[, -c("arr_delay", "dep_delay")]
+kable(head(ans))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaycarrierorigindestair_timedistancehour
201411AAJFKLAX35924759
201411AAJFKLAX363247511
201411AAJFKLAX351247519
201411AALGAPBI15710357
201411AAJFKLAX350247513
201411AAEWRLAX339245418
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-columns (complement #{"arr_delay" "dep_delay"}))
+    (tc/head 6))
+
+
+

https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv [6 9]:

+ +++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaycarrierorigindestair_timedistancehour
201411AAJFKLAX35924759
201411AAJFKLAX363247511
201411AAJFKLAX351247519
201411AALGAPBI15710357
201411AAJFKLAX350247513
201411AAEWRLAX339245418
+
+
+
+
+

Aggregations

+
+
How can we get the number of trips corresponding to each origin airport?
+

R

+
+
ans <- flights[, .(.N), by = .(origin)]
+kable(ans)
+
+ + + + + + + + + + + + + + + + + + + + + +
originN
JFK81483
LGA84433
EWR87400
+
+
+

Clojure

+
+
(-> flights
+    (tc/group-by ["origin"])
+    (tc/aggregate {:N tc/row-count}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
origin:N
JFK81483
LGA84433
EWR87400
+
+
+
+
How can we calculate the number of trips for each origin airport for carrier code “AA”?
+

R

+
+
ans <- flights[carrier == "AA", .N, by = origin]
+kable(ans)
+
+ + + + + + + + + + + + + + + + + + + + + +
originN
JFK11923
LGA11730
EWR2649
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows #(= (get % "carrier") "AA"))
+    (tc/group-by ["origin"])
+    (tc/aggregate {:N tc/row-count}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
origin:N
JFK11923
LGA11730
EWR2649
+
+
+
+
How can we get the total number of trips for each origin, dest pair for carrier code “AA”?
+

R

+
+
ans <- flights[carrier == "AA", .N, by = .(origin, dest)]
+kable(head(ans))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestN
JFKLAX3387
LGAPBI245
EWRLAX62
JFKMIA1876
JFKSEA298
EWRMIA848
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows #(= (get % "carrier") "AA"))
+    (tc/group-by ["origin" "dest"])
+    (tc/aggregate {:N tc/row-count})
+    (tc/head 6))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindest:N
JFKLAX3387
LGAPBI245
EWRLAX62
JFKMIA1876
JFKSEA298
EWRMIA848
+
+
+
+
How can we get the average arrival and departure delay for each orig,dest pair for each month for carrier code “AA”?
+

R

+
+
ans <- flights[carrier == "AA",
+.(mean(arr_delay), mean(dep_delay)),
+by = .(origin, dest, month)]
+kable(head(ans,10))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmonthV1V2
JFKLAX16.59036114.2289157
LGAPBI1-7.7586210.3103448
EWRLAX11.3666677.5000000
JFKMIA115.72067018.7430168
JFKSEA114.35714330.7500000
EWRMIA111.01123612.1235955
JFKSFO119.25225228.6396396
JFKBOS112.91964315.2142857
JFKORD131.58620740.1724138
JFKIAH128.85714314.2857143
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows #(= (get % "carrier") "AA"))
+    (tc/group-by ["origin" "dest" "month"])
+    (tc/aggregate [#(dfn/mean (% "arr_delay"))
+                   #(dfn/mean (% "dep_delay"))])
+    (tc/head 10))
+
+
+

_unnamed [10 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmonth:summary-0:summary-1
JFKLAX16.5903614514.22891566
LGAPBI1-7.758620690.31034483
EWRLAX11.366666677.50000000
JFKMIA115.7206703918.74301676
JFKSEA114.3571428630.75000000
EWRMIA111.0112359612.12359551
JFKSFO119.2522522528.63963964
JFKBOS112.9196428615.21428571
JFKORD131.5862069040.17241379
JFKIAH128.8571428614.28571429
+
+
+
+
So how can we directly order by all the grouping variables?
+

R

+
+
ans <- flights[carrier == "AA",
+.(mean(arr_delay), mean(dep_delay)),
+keyby = .(origin, dest, month)]
+kable(head(ans,10))
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmonthV1V2
EWRDFW16.42767310.012579
EWRDFW210.53676511.345588
EWRDFW312.8650318.079755
EWRDFW417.79268312.920732
EWRDFW518.48780518.682927
EWRDFW637.00595238.744048
EWRDFW720.25000021.154762
EWRDFW816.93604622.069767
EWRDFW95.86503113.055215
EWRDFW1018.81366518.894410
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows #(= (get % "carrier") "AA"))
+    (tc/group-by ["origin" "dest" "month"])
+    (tc/aggregate [#(dfn/mean (% "arr_delay"))
+                   #(dfn/mean (% "dep_delay"))])
+    (tc/order-by ["origin" "dest" "month"])
+    (tc/head 10))
+
+
+

_unnamed [10 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmonth:summary-0:summary-1
EWRDFW16.4276729610.01257862
EWRDFW210.5367647111.34558824
EWRDFW312.865030678.07975460
EWRDFW417.7926829312.92073171
EWRDFW518.4878048818.68292683
EWRDFW637.0059523838.74404762
EWRDFW720.2500000021.15476190
EWRDFW816.9360465122.06976744
EWRDFW95.8650306713.05521472
EWRDFW1018.8136646018.89440994
+
+
+
+
Can by accept expressions as well or does it just take columns?
+

R

+
+
ans <- flights[, .N, .(dep_delay>0, arr_delay>0)]
+kable(ans)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dep_delayarr_delayN
TRUETRUE72836
FALSETRUE34583
FALSEFALSE119304
TRUEFALSE26593
+
+
+

Clojure

+
+
(-> flights
+    (tc/group-by (fn [row]
+                   {:dep_delay (pos? (get row "dep_delay"))
+                    :arr_delay (pos? (get row "arr_delay"))}))
+    (tc/aggregate {:N tc/row-count}))
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:dep_delay:arr_delay:N
truetrue72836
falsetrue34583
falsefalse119304
truefalse26593
+
+
+
+
Do we have to compute mean() for each column individually?
+

R

+
+
kable(DT)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDabc
b1713
b2814
b3915
a41016
a51117
c61218
+
+
DT[, print(.SD), by = ID]
+
+
   a b  c
+1: 1 7 13
+2: 2 8 14
+3: 3 9 15
+   a  b  c
+1: 4 10 16
+2: 5 11 17
+   a  b  c
+1: 6 12 18
+
+
+
Empty data.table (0 rows and 1 cols): ID
+
+
+
+
kable(DT[, lapply(.SD, mean), by = ID])
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDabc
b2.08.014.0
a4.510.516.5
c6.012.018.0
+
+
+

Clojure

+
+
DT
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:ID:a:b:c
b1713
b2814
b3915
a41016
a51117
c61218
+
+
+
(tc/group-by DT :ID {:result-type :as-map})
+
+
+

{

+ ++++ + + + + + + +
+
"b"
+
+
+

Group: b [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:ID:a:b:c
b1713
b2814
b3915
+
+
+ ++++ + + + + + + +
+
"a"
+
+
+

Group: a [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:ID:a:b:c
a41016
a51117
+
+
+ ++++ + + + + + + +
+
"c"
+
+
+

Group: c [1 4]:

+ + + + + + + + + + + + + + + + + +
:ID:a:b:c
c61218
+
+
+

}

+
+
+
(-> DT
+    (tc/group-by [:ID])
+    (tc/aggregate-columns (complement #{:ID}) dfn/mean))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:ID:a:b:c
b2.08.014.0
a4.510.516.5
c6.012.018.0
+
+
+
+
How can we specify just the columns we would like to compute the mean() on?
+

R

+
+
kable(head(flights[carrier == "AA",                         ## Only on trips with carrier "AA"
+lapply(.SD, mean),                       ## compute the mean
+by = .(origin, dest, month),             ## for every 'origin,dest,month'
+.SDcols = c("arr_delay", "dep_delay")])) ## for just those specified in .SDcols
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmontharr_delaydep_delay
JFKLAX16.59036114.2289157
LGAPBI1-7.7586210.3103448
EWRLAX11.3666677.5000000
JFKMIA115.72067018.7430168
JFKSEA114.35714330.7500000
EWRMIA111.01123612.1235955
+
+
+

Clojure

+
+
(-> flights
+    (tc/select-rows #(= (get % "carrier") "AA"))
+    (tc/group-by ["origin" "dest" "month"])
+    (tc/aggregate-columns ["arr_delay" "dep_delay"] dfn/mean)
+    (tc/head 6))
+
+
+

_unnamed [6 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
origindestmontharr_delaydep_delay
JFKLAX16.5903614514.22891566
LGAPBI1-7.758620690.31034483
EWRLAX11.366666677.50000000
JFKMIA115.7206703918.74301676
JFKSEA114.3571428630.75000000
EWRMIA111.0112359612.12359551
+
+
+
+
How can we return the first two rows for each month?
+

R

+
+
ans <- flights[, head(.SD, 2), by = month]
+kable(head(ans))
+
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
monthyeardaydep_delayarr_delaycarrierorigindestair_timedistancehour
1201411413AAJFKLAX35924759
120141-313AAJFKLAX363247511
220141-11AAJFKLAX35824758
220141-53AAJFKLAX358247511
320141-1136AAJFKLAX37524758
320141-314AAJFKLAX368247511
+
+
+

Clojure

+
+
(-> flights
+    (tc/group-by ["month"])
+    (tc/head 2) ;; head applied on each group
+    (tc/ungroup)
+    (tc/head 6))
+
+
+

_unnamed [6 11]:

+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearmonthdaydep_delayarr_delaycarrierorigindestair_timedistancehour
2014111413AAJFKLAX35924759
201411-313AAJFKLAX363247511
201421-11AAJFKLAX35824758
201421-53AAJFKLAX358247511
201431-1136AAJFKLAX37524758
201431-314AAJFKLAX368247511
+
+
+
+
How can we concatenate columns a and b for each group in ID?
+

R

+
+
kable(DT[, .(val = c(a,b)), by = ID])
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDval
b1
b2
b3
b7
b8
b9
a4
a5
a10
a11
c6
c12
+
+
+

Clojure

+
+
(-> DT
+    (tc/pivot->longer [:a :b] {:value-column-name :val})
+    (tc/drop-columns [:$column :c]))
+
+
+

_unnamed [12 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:ID:val
b1
b2
b3
a4
a5
c6
b7
b8
b9
a10
a11
c12
+
+
+
+
What if we would like to have all the values of column a and b concatenated, but returned as a list column?
+

R

+
+
kable(DT[, .(val = list(c(a,b))), by = ID])
+
+ + + + + + + + + + + + + + + + + + + + + +
IDval
b1, 2, 3, 7, 8, 9
a4, 5, 10, 11
c6, 12
+
+
+

Clojure

+
+
(-> DT
+    (tc/pivot->longer [:a :b] {:value-column-name :val})
+    (tc/drop-columns [:$column :c])
+    (tc/fold-by :ID))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:ID:val
b[1 2 3 7 8 9]
a[4 5 10 11]
c[6 12]
+
+
           ### API tour
+
+           Below snippets are taken from [A data.table and dplyr tour](https://atrebas.github.io/post/2019-03-03-datatable-dplyr/) written by Atrebas (permission granted).
+
+           I keep structure and subtitles but I skip `data.table` and `dplyr` examples.
+
+           Example data
+           
+
+
(def DS (tc/dataset {:V1 (take 9 (cycle [1 2]))
+                      :V2 (range 1 10)
+                      :V3 (take 9 (cycle [0.5 1.0 1.5]))
+                      :V4 (take 9 (cycle ["A" "B" "C"]))}))
+
+
+
(tc/dataset? DS)
+
+
+
true
+
+
+
(class DS)
+
+
+
tech.v3.dataset.impl.dataset.Dataset
+
+
+
DS
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
           #### Basic Operations
+
+           ##### Filter rows
+
+           Filter rows using indices
+           
+
+
(tc/select-rows DS [2 3])
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
240.5A
+
+
           ---
+
+           Discard rows using negative indices
+
+           In Clojure API we have separate function for that: `drop-rows`.
+           
+
+
(tc/drop-rows DS (range 2 7))
+
+
+

_unnamed [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
281.0B
191.5C
+
+
           ---
+
+           Filter rows using a logical expression
+           
+
+
(tc/select-rows DS (comp #(> % 5) :V2))
+
+
+

_unnamed [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
261.5C
170.5A
281.0B
191.5C
+
+
+
(tc/select-rows DS (comp #{"A" "C"} :V4))
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
131.5C
240.5A
261.5C
170.5A
191.5C
+
+
           ---
+
+           Filter rows using multiple conditions
+           
+
+
(tc/select-rows DS #(and (= (:V1 %) 1)
+                          (= (:V4 %) "A")))
+
+
+

_unnamed [2 4]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
170.5A
+
+
           ---
+
+           Filter unique rows
+           
+
+
(tc/unique-by DS)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+
(tc/unique-by DS [:V1 :V4])
+
+
+

_unnamed [6 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
+
+
           ---
+
+           Discard rows with missing values
+           
+
+
(tc/drop-missing DS)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
           ---
+
+           Other filters
+           
+
+
^:note-to-test/skip
+(tc/random DS 3)
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
151.0B
151.0B
+
+

3 random rows

+
+
^:note-to-test/skip
+(tc/random DS (/ (tc/row-count DS) 2))
+
+
+

_unnamed [5 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
170.5A
261.5C
110.5A
281.0B
+
+

fraction of random rows

+
+
(tc/by-rank DS :V1 zero?)
+
+
+

_unnamed [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
240.5A
261.5C
281.0B
+
+

take top n entries

+
           ---
+
+           Convenience functions
+           
+
+
(tc/select-rows DS (comp (partial re-matches #"^B") str :V4))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
221.0B
151.0B
281.0B
+
+
+
(tc/select-rows DS (comp #(<= 3 % 5) :V2))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
240.5A
151.0B
+
+
+
(tc/select-rows DS (comp #(< 3 % 5) :V2))
+
+
+

_unnamed [1 4]:

+ + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
240.5A
+
+
+
(tc/select-rows DS (comp #(<= 3 % 5) :V2))
+
+
+

_unnamed [3 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
240.5A
151.0B
+
+
           Last example skipped.
+
+           ##### Sort rows
+
+           Sort rows by column
+           
+
+
(tc/order-by DS :V3)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
240.5A
170.5A
221.0B
151.0B
281.0B
131.5C
261.5C
191.5C
+
+
           ---
+
+           Sort rows in decreasing order
+           
+
+
(tc/order-by DS :V3 :desc)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
131.5C
261.5C
191.5C
151.0B
221.0B
281.0B
170.5A
240.5A
110.5A
+
+
           ---
+
+           Sort rows based on several columns
+           
+
+
(tc/order-by DS [:V1 :V2] [:asc :desc])
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
191.5C
170.5A
151.0B
131.5C
110.5A
281.0B
261.5C
240.5A
221.0B
+
+
           ##### Select columns
+
+           Select one column using an index (not recommended)
+           
+
+
(nth (tc/columns DS :as-seq) 2)
+
+
+
#tech.v3.dataset.column<float64>[9]
+:V3
+[0.5000, 1.000, 1.500, 0.5000, 1.000, 1.500, 0.5000, 1.000, 1.500]
+
+

as column (iterable)

+
+
(tc/dataset [(nth (tc/columns DS :as-seq) 2)])
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3
0.5
1.0
1.5
0.5
1.0
1.5
0.5
1.0
1.5
+
+
           ---
+
+           Select one column using column name
+           
+
+
(tc/select-columns DS :V2)
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2
1
2
3
4
5
6
7
8
9
+
+

as dataset

+
+
(tc/select-columns DS [:V2])
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2
1
2
3
4
5
6
7
8
9
+
+

as dataset

+
+
(DS :V2)
+
+
+
#tech.v3.dataset.column<int64>[9]
+:V2
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+
+

as column (iterable)

+
           ---
+
+           Select several columns
+           
+
+
(tc/select-columns DS [:V2 :V3 :V4])
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2:V3:V4
10.5A
21.0B
31.5C
40.5A
51.0B
61.5C
70.5A
81.0B
91.5C
+
+
           ---
+
+           Exclude columns
+           
+
+
(tc/select-columns DS (complement #{:V2 :V3 :V4}))
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
1
2
1
2
1
2
1
+
+
+
(tc/drop-columns DS [:V2 :V3 :V4])
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
1
2
1
2
1
2
1
+
+
           ---
+
+           Other seletions
+           
+
+
(->> (range 1 3)
+     (map (comp keyword (partial format "V%d")))
+     (tc/select-columns DS))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
11
22
13
24
15
26
17
28
19
+
+
+
(tc/reorder-columns DS :V4)
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2:V3
A110.5
B221.0
C131.5
A240.5
B151.0
C261.5
A170.5
B281.0
C191.5
+
+
+
(tc/select-columns DS #(clojure.string/starts-with? (name %) "V"))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
110.5A
221.0B
131.5C
240.5A
151.0B
261.5C
170.5A
281.0B
191.5C
+
+
+
(tc/select-columns DS #(clojure.string/ends-with? (name %) "3"))
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V3
0.5
1.0
1.5
0.5
1.0
1.5
0.5
1.0
1.5
+
+
+
(tc/select-columns DS #"..2")
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V2
1
2
3
4
5
6
7
8
9
+
+

regex converts to string using str function

+
+
(tc/select-columns DS #{:V1 "X"})
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
1
2
1
2
1
2
1
+
+
+
(tc/select-columns DS #(not (clojure.string/starts-with? (name %) "V2")))
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V3:V4
10.5A
21.0B
11.5C
20.5A
11.0B
21.5C
10.5A
21.0B
11.5C
+
+
           ##### Summarise data
+
+           Summarise one column
+           
+
+
(reduce + (DS :V1))
+
+
+
13
+
+

using pure Clojure, as value

+
+
(tc/aggregate-columns DS :V1 dfn/sum)
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V1
13.0
+
+

as dataset

+
+
(tc/aggregate DS {:sumV1 #(dfn/sum (% :V1))})
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:sumV1
13.0
+
+
           ---
+
+           Summarize several columns
+           
+
+
(tc/aggregate DS [#(dfn/sum (% :V1))
+                   #(dfn/standard-deviation (% :V3))])
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:summary-0:summary-1
13.00.4330127
+
+
+
(tc/aggregate-columns DS [:V1 :V3] [dfn/sum
+                                     dfn/standard-deviation])
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:V1:V3
13.00.4330127
+
+
           ---
+
+           Summarise several columns and assign column names
+           
+
+
(tc/aggregate DS {:sumv1 #(dfn/sum (% :V1))
+                   :sdv3 #(dfn/standard-deviation (% :V3))})
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:sumv1:sdv3
13.00.4330127
+
+
           ---
+
+           Summarise a subset of rows
+           
+
+
(-> DS
+    (tc/select-rows (range 4))
+    (tc/aggregate-columns :V1 dfn/sum))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V1
6.0
+
+
           ##### Additional helpers
+
+
(-> DS
+    (tc/first)
+    (tc/select-columns :V3))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V3
0.5
+
+

select first row from :V3 column

+
+
(-> DS
+    (tc/last)
+    (tc/select-columns :V3))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V3
1.5
+
+

select last row from :V3 column

+
+
(-> DS
+    (tc/select-rows 4)
+    (tc/select-columns :V3))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V3
1.0
+
+

select forth row from :V3 column

+
+
(-> DS
+    (tc/select :V3 4))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V3
1.0
+
+

select forth row from :V3 column

+
+
(-> DS
+    (tc/unique-by :V4)
+    (tc/aggregate tc/row-count))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
summary
3
+
+

number of unique rows in :V4 column, as dataset

+
+
(-> DS
+    (tc/unique-by :V4)
+    (tc/row-count))
+
+
+
3
+
+

number of unique rows in :V4 column, as value

+
+
(-> DS
+    (tc/unique-by)
+    (tc/row-count))
+
+
+
9
+
+

number of unique rows in dataset, as value

+
           ##### Add/update/delete columns
+
+           Modify a column
+           
+
+
(tc/map-columns DS :V1 [:V1] #(dfn/pow % 2))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
1.010.5A
4.021.0B
1.031.5C
4.040.5A
1.051.0B
4.061.5C
1.070.5A
4.081.0B
1.091.5C
+
+
+
(def DS (tc/add-column DS :V1 (dfn/pow (DS :V1) 2)))
+
+
+
DS
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
1.010.5A
4.021.0B
1.031.5C
4.040.5A
1.051.0B
4.061.5C
1.070.5A
4.081.0B
1.091.5C
+
+
           ---
+
+           Add one column
+           
+
+
(tc/map-columns DS :v5 [:V1] dfn/log)
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:v5
1.010.5A0.00000000
4.021.0B1.38629436
1.031.5C0.00000000
4.040.5A1.38629436
1.051.0B0.00000000
4.061.5C1.38629436
1.070.5A0.00000000
4.081.0B1.38629436
1.091.5C0.00000000
+
+
+
(def DS (tc/add-column DS :v5 (dfn/log (DS :V1))))
+
+
+
DS
+
+
+

_unnamed [9 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:v5
1.010.5A0.00000000
4.021.0B1.38629436
1.031.5C0.00000000
4.040.5A1.38629436
1.051.0B0.00000000
4.061.5C1.38629436
1.070.5A0.00000000
4.081.0B1.38629436
1.091.5C0.00000000
+
+
           ---
+
+           Add several columns
+           
+
+
(def DS (tc/add-columns DS {:v6 (dfn/sqrt (DS :V1))
+                                       :v7 "X"}))
+
+
+
DS
+
+
+

_unnamed [9 7]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:v5:v6:v7
1.010.5A0.000000001.0X
4.021.0B1.386294362.0X
1.031.5C0.000000001.0X
4.040.5A1.386294362.0X
1.051.0B0.000000001.0X
4.061.5C1.386294362.0X
1.070.5A0.000000001.0X
4.081.0B1.386294362.0X
1.091.5C0.000000001.0X
+
+
           ---
+
+           Create one column and remove the others
+           
+
+
(tc/dataset {:v8 (dfn/+ (DS :V3) 1)})
+
+
+

_unnamed [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:v8
1.5
2.0
2.5
1.5
2.0
2.5
1.5
2.0
2.5
+
+
           ---
+
+           Remove one column
+           
+
+
(def DS (tc/drop-columns DS :v5))
+
+
+
DS
+
+
+

_unnamed [9 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4:v6:v7
1.010.5A1.0X
4.021.0B2.0X
1.031.5C1.0X
4.040.5A2.0X
1.051.0B1.0X
4.061.5C2.0X
1.070.5A1.0X
4.081.0B2.0X
1.091.5C1.0X
+
+
           ---
+
+           Remove several columns
+           
+
+
(def DS (tc/drop-columns DS [:v6 :v7]))
+
+
+
DS
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V3:V4
1.010.5A
4.021.0B
1.031.5C
4.040.5A
1.051.0B
4.061.5C
1.070.5A
4.081.0B
1.091.5C
+
+
           ---
+
+           Remove columns using a vector of colnames
+
+           We use set here.
+           
+
+
(def DS (tc/select-columns DS (complement #{:V3})))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1.01A
4.02B
1.03C
4.04A
1.05B
4.06C
1.07A
4.08B
1.09C
+
+
           ---
+
+           Replace values for rows matching a condition
+           
+
+
(def DS (tc/map-columns DS :V2 [:V2] #(if (< % 4.0) 0.0 %)))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1.00.0A
4.00.0B
1.00.0C
4.04.0A
1.05.0B
4.06.0C
1.07.0A
4.08.0B
1.09.0C
+
+
           ##### by
+
+           By group
+           
+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate {:sumV2 #(dfn/sum (% :V2))}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:sumV2
A11.0
B13.0
C15.0
+
+
           ---
+
+           By several groups
+           
+
+
(-> DS
+    (tc/group-by [:V4 :V1])
+    (tc/aggregate {:sumV2 #(dfn/sum (% :V2))}))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:sumV2
A1.07.0
B4.08.0
C1.09.0
A4.04.0
B1.05.0
C4.06.0
+
+
           ---
+
+           Calling function in by
+           
+
+
(-> DS
+    (tc/group-by (fn [row]
+                    (clojure.string/lower-case (:V4 row))))
+    (tc/aggregate {:sumV1 #(dfn/sum (% :V1))}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:$group-name:sumV1
a6.0
b9.0
c6.0
+
+
           ---
+
+           Assigning column name in by
+           
+
+
(-> DS
+    (tc/group-by (fn [row]
+                    {:abc (clojure.string/lower-case (:V4 row))}))
+    (tc/aggregate {:sumV1 #(dfn/sum (% :V1))}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:abc:sumV1
a6.0
b9.0
c6.0
+
+
+
(-> DS
+    (tc/group-by (fn [row]
+                    (clojure.string/lower-case (:V4 row))))
+    (tc/aggregate {:sumV1 #(dfn/sum (% :V1))} {:add-group-as-column :abc}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:$group-name:sumV1
a6.0
b9.0
c6.0
+
+
+

Using a condition in by

+
+
(-> DS
+    (tc/group-by #(= (:V4 %) "A"))
+    (tc/aggregate #(dfn/sum (% :V1))))
+
+
+

_unnamed [2 2]:

+ + + + + + + + + + + + + + + + + +
:$group-namesummary
true6.0
false15.0
+
+
+

By on a subset of rows

+
+
(-> DS
+    (tc/select-rows (range 5))
+    (tc/group-by :V4)
+    (tc/aggregate {:sumV1 #(dfn/sum (% :V1))}))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:$group-name:sumV1
A5.0
B5.0
C1.0
+
+
+

Count number of observations for each group

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/aggregate tc/row-count))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:$group-namesummary
A3
B3
C3
+
+
+

Add a column with number of observations for each group

+
+
(-> DS
+    (tc/group-by [:V1])
+    (tc/add-column :n tc/row-count)
+    (tc/ungroup))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4:n
1.00.0A5
1.00.0C5
1.05.0B5
1.07.0A5
1.09.0C5
4.00.0B4
4.04.0A4
4.06.0C4
4.08.0B4
+
+
+

Retrieve the first/last/nth observation for each group

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns :V2 first))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V2
A0.0
B0.0
C0.0
+
+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns :V2 last))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V2
A7.0
B8.0
C9.0
+
+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns :V2 #(nth % 1)))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V2
A4.0
B5.0
C6.0
+
+
+
+
+

Going further

+
+
Advanced columns manipulation
+

Summarise all the columns

+

custom max function which works on every type

+
+
(tc/aggregate-columns DS :all (fn [col] (first (sort #(compare %2 %1) col))))
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V4
4.09.0C
+
+
+

Summarise several columns

+
+
(tc/aggregate-columns DS [:V1 :V2] dfn/mean)
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:V1:V2
2.333333334.33333333
+
+
+

Summarise several columns by group

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns [:V1 :V2] dfn/mean))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A2.03.66666667
B3.04.33333333
C2.05.00000000
+
+
+

Summarise with more than one function by group

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate-columns [:V1 :V2] (fn [col]
+                                       {:sum (dfn/sum col)
+                                        :mean (dfn/mean col)})))
+
+
+

_unnamed [3 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1-sum:V1-mean:V2-sum:V2-mean
A6.02.011.03.66666667
B9.03.013.04.33333333
C6.02.015.05.00000000
+
+

Summarise using a condition

+
+
(-> DS
+    (tc/select-columns :type/numerical)
+    (tc/aggregate-columns :all dfn/mean))
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:V1:V2
2.333333334.33333333
+
+
+

Modify all the columns

+
+
(tc/update-columns DS :all reverse)
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1.09.0C
4.08.0B
1.07.0A
4.06.0C
1.05.0B
4.04.0A
1.00.0C
4.00.0B
1.00.0A
+
+
+

Modify several columns (dropping the others)

+
+
(-> DS
+    (tc/select-columns [:V1 :V2])
+    (tc/update-columns :all dfn/sqrt))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
1.00.00000000
2.00.00000000
1.00.00000000
2.02.00000000
1.02.23606798
2.02.44948974
1.02.64575131
2.02.82842712
1.03.00000000
+
+
+
(-> DS
+    (tc/select-columns (complement #{:V4}))
+    (tc/update-columns :all dfn/exp))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
2.718281831.00000000
54.598150031.00000000
2.718281831.00000000
54.5981500354.59815003
2.71828183148.41315910
54.59815003403.42879349
2.718281831096.63315843
54.598150032980.95798704
2.718281838103.08392758
+
+
+

Modify several columns (keeping the others)

+
+
(def DS (tc/update-columns DS [:V1 :V2] dfn/sqrt))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1.00.00000000A
2.00.00000000B
1.00.00000000C
2.02.00000000A
1.02.23606798B
2.02.44948974C
1.02.64575131A
2.02.82842712B
1.03.00000000C
+
+
+
(def DS (tc/update-columns DS (complement #{:V4}) #(dfn/pow % 2)))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1.00.0A
4.00.0B
1.00.0C
4.04.0A
1.05.0B
4.06.0C
1.07.0A
4.08.0B
1.09.0C
+
+
+

Modify columns using a condition (dropping the others)

+
+
(-> DS
+    (tc/select-columns :type/numerical)
+    (tc/update-columns :all #(dfn/- % 1)))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
0.0-1.0
3.0-1.0
0.0-1.0
3.03.0
0.04.0
3.05.0
0.06.0
3.07.0
0.08.0
+
+
+

Modify columns using a condition (keeping the others)

+
+
(def DS (tc/convert-types DS :type/numerical :int32))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10A
40B
10C
44A
15B
45C
17A
48B
19C
+
+
+

Use a complex expression

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/head 2)
+    (tc/add-column :V2 "X")
+    (tc/ungroup))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
1XA
4XA
4XB
1XB
1XC
4XC
+
+
+

Use multiple expressions

+
+
(tc/dataset (let [x (dfn/+ (DS :V1) (dfn/sum (DS :V2)))]
+              {:A (range 1 (inc (tc/row-count DS)))
+               :B x}))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:A:B
139.0
242.0
339.0
442.0
539.0
642.0
739.0
842.0
939.0
+
+
+
+
Chain expressions
+

Expression chaining using >

+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate {:V1sum #(dfn/sum (% :V1))})
+    (tc/select-rows #(>= (:V1sum %) 5)))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V1sum
A6.0
B9.0
C6.0
+
+
+
(-> DS
+    (tc/group-by [:V4])
+    (tc/aggregate {:V1sum #(dfn/sum (% :V1))})
+    (tc/order-by :V1sum :desc))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V1sum
B9.0
A6.0
C6.0
+
+
+
+
Indexing and Keys
+

Set the key/index (order)

+
+
(def DS (tc/order-by DS :V4))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10A
44A
17A
40B
15B
48B
10C
45C
19C
+
+

Select the matching rows

+
+
(tc/select-rows DS #(= (:V4 %) "A"))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10A
44A
17A
+
+
+
(tc/select-rows DS (comp #{"A" "C"} :V4))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10A
44A
17A
10C
45C
19C
+
+
+

Select the first matching row

+
+
(-> DS
+    (tc/select-rows #(= (:V4 %) "B"))
+    (tc/first))
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V4
40B
+
+
+
(-> DS
+    (tc/unique-by :V4)
+    (tc/select-rows (comp #{"B" "C"} :V4)))
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
40B
10C
+
+
+

Select the last matching row

+
+
(-> DS
+    (tc/select-rows #(= (:V4 %) "A"))
+    (tc/last))
+
+
+

_unnamed [1 3]:

+ + + + + + + + + + + + + + + +
:V1:V2:V4
17A
+
+
+

Nomatch argument

+
+
(tc/select-rows DS (comp #{"A" "D"} :V4))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10A
44A
17A
+
+
+

Apply a function on the matching rows

+
+
(-> DS
+    (tc/select-rows (comp #{"A" "C"} :V4))
+    (tc/aggregate-columns :V1 (fn [col]
+                                 {:sum (dfn/sum col)})))
+
+
+

_unnamed [1 1]:

+ + + + + + + + + + + +
:V1-sum
12.0
+
+
+

Modify values for matching rows

+
+
(def DS (-> DS
+            (tc/map-columns :V1 [:V1 :V4] #(if (= %2 "A") 0 %1))
+            (tc/order-by :V4)))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
00A
04A
07A
40B
15B
48B
10C
45C
19C
+
+
+

Use keys in by

+
+
(-> DS
+    (tc/select-rows (comp (complement #{"B"}) :V4))
+    (tc/group-by [:V4])
+    (tc/aggregate-columns :V1 dfn/sum))
+
+
+

_unnamed [2 2]:

+ + + + + + + + + + + + + + + + + +
:V4:V1
A0.0
C6.0
+
+
+

Set keys/indices for multiple columns (ordered)

+
+
(tc/order-by DS [:V4 :V1])
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
00A
04A
07A
15B
40B
48B
10C
19C
45C
+
+
+

Subset using multiple keys/indices

+
+
(-> DS
+    (tc/select-rows #(and (= (:V1 %) 1)
+                           (= (:V4 %) "C"))))
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
10C
19C
+
+
+
(-> DS
+    (tc/select-rows #(and (= (:V1 %) 1)
+                           (#{"B" "C"} (:V4 %)))))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
15B
10C
19C
+
+
+
(-> DS
+    (tc/select-rows #(and (= (:V1 %) 1)
+                           (#{"B" "C"} (:V4 %))) {:result-type :as-indexes}))
+
+
+
(4 6 8)
+
+
+
+
set*() modifications
+

Replace values

+

There is no mutating operations tech.ml.dataset or easy way to set value.

+
+
(def DS (tc/update-columns DS :V2 #(map-indexed (fn [idx v]
+                                                   (if (zero? idx) 3 v)) %)))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
03A
04A
07A
40B
15B
48B
10C
45C
19C
+
+
+

Reorder rows

+
+
(def DS (tc/order-by DS [:V4 :V1] [:asc :desc]))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2:V4
03A
04A
07A
40B
48B
15B
45C
10C
19C
+
+
+

Modify colnames

+
+
(def DS (tc/rename-columns DS {:V2 "v2"}))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1v2:V4
03A
04A
07A
40B
48B
15B
45C
10C
19C
+
+
+
(def DS (tc/rename-columns DS {"v2" :V2}))
+
+

revert back

+
+

Reorder columns

+
+
(def DS (tc/reorder-columns DS :V4 :V1 :V2))
+
+
+
DS
+
+
+

_unnamed [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
A04
A07
B40
B48
B15
C45
C10
C19
+
+
+
+
Advanced use of by
+

Select first/last/… row by group

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/first)
+    (tc/ungroup))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
B40
C45
+
+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/select-rows [0 2])
+    (tc/ungroup))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
A07
B40
B15
C45
C19
+
+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/tail 2)
+    (tc/ungroup))
+
+
+

_unnamed [6 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A04
A07
B48
B15
C10
C19
+
+
+

Select rows using a nested query

+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/order-by :V2)
+    (tc/first)
+    (tc/ungroup))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
B40
C10
+
+

Add a group counter column

+
+
(-> DS
+    (tc/group-by [:V4 :V1])
+    (tc/ungroup {:add-group-id-as-column :Grp}))
+
+
+

_unnamed [9 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:Grp:V4:V1:V2
0A03
0A04
0A07
1B40
1B48
2B15
3C45
4C10
4C19
+
+
+

Get row number of first (and last) observation by group

+
+
(-> DS
+    (tc/add-column :row-id (range))
+    (tc/select-columns [:V4 :row-id])
+    (tc/group-by :V4)
+    (tc/ungroup))
+
+
+

_unnamed [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:row-id
A0
A1
A2
B3
B4
B5
C6
C7
C8
+
+
+
(-> DS
+    (tc/add-column :row-id (range))
+    (tc/select-columns [:V4 :row-id])
+    (tc/group-by :V4)
+    (tc/first)
+    (tc/ungroup))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:row-id
A0
B3
C6
+
+
+
(-> DS
+    (tc/add-column :row-id (range))
+    (tc/select-columns [:V4 :row-id])
+    (tc/group-by :V4)
+    (tc/select-rows [0 2])
+    (tc/ungroup))
+
+
+

_unnamed [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:row-id
A0
A2
B3
B5
C6
C8
+
+
+

Handle list-columns by group

+
+
(-> DS
+    (tc/select-columns [:V1 :V4])
+    (tc/fold-by :V4))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V4:V1
A[0 0 0]
B[4 4 1]
C[4 1 1]
+
+
+
(-> DS
+    (tc/group-by :V4)
+    (tc/unmark-group))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:name:group-id:data
A0Group: A [3 3]:
B1Group: B [3 3]:
C2Group: C [3 3]:
+
+
+

Grouping sets (multiple by at once)

+

Not available.

+
+
+
+

Miscellaneous

+
+
Read / Write data
+

Write data to a csv file

+
+
(tc/write! DS "DF.csv")
+
+
+
10
+
+
+

Write data to a tab-delimited file

+
+
(tc/write! DS "DF.txt" {:separator \tab})
+
+
+
10
+
+

or

+
+
(tc/write! DS "DF.tsv")
+
+
+
10
+
+
+

Read a csv / tab-delimited file

+
+
(tc/dataset "DF.csv" {:key-fn keyword})
+
+
+

DF.csv [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
A04
A07
B40
B48
B15
C45
C10
C19
+
+
+
^:note-to-test/skip
+(tc/dataset "DF.txt" {:key-fn keyword})
+
+
+

DF.txt [9 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4 V1 V2
A 0 3
A 0 4
A 0 7
B 4 0
B 4 8
B 1 5
C 4 5
C 1 0
C 1 9
+
+
+
(tc/dataset "DF.tsv" {:key-fn keyword})
+
+
+

DF.tsv [9 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
A04
A07
B40
B48
B15
C45
C10
C19
+
+
+

Read a csv file selecting / droping columns

+
+
(tc/dataset "DF.csv" {:key-fn keyword
+                       :column-whitelist ["V1" "V4"]})
+
+
+

DF.csv [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1
A0
A0
A0
B4
B4
B1
C4
C1
C1
+
+
+
(tc/dataset "DF.csv" {:key-fn keyword
+                       :column-blacklist ["V4"]})
+
+
+

DF.csv [9 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
03
04
07
40
48
15
45
10
19
+
+
+

Read and rbind several files

+
+
(apply tc/concat (map tc/dataset ["DF.csv" "DF.csv"]))
+
+
+

DF.csv [18 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
V4V1V2
A03
A04
A07
B40
B48
B15
C45
C10
C19
A03
A04
A07
B40
B48
B15
C45
C10
C19
+
+
+
+
Reshape data
+

Melt data (from wide to long)

+
+
(def mDS (tc/pivot->longer DS [:V1 :V2] {:target-columns :variable
+                                          :value-column-name :value}))
+
+
+
mDS
+
+
+

_unnamed [18 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:variable:value
A:V10
A:V10
A:V10
B:V14
B:V14
B:V11
C:V14
C:V11
C:V11
A:V23
A:V24
A:V27
B:V20
B:V28
B:V25
C:V25
C:V20
C:V29
+
+
+

Cast data (from long to wide)

+
+
(-> mDS
+    (tc/pivot->wider :variable :value {:fold-fn vec})
+    (tc/update-columns ["V1" "V2"] (partial map count)))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A[0 0 0][3 4 7]
B[4 4 1][0 8 5]
C[4 1 1][5 0 9]
+
+
+
(-> mDS
+    (tc/pivot->wider :variable :value {:fold-fn vec})
+    (tc/update-columns ["V1" "V2"] (partial map dfn/sum)))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A[0 0 0][3 4 7]
B[4 4 1][0 8 5]
C[4 1 1][5 0 9]
+
+
+
^:note-to-test/skip
+(-> mDS
+    (tc/map-columns :value #(str (> % 5))) ;; coerce to strings
+    (tc/pivot->wider :value :variable {:fold-fn vec})
+    (tc/update-columns ["true" "false"] (partial map #(if (sequential? %) (count %) 1))))
+
+
+

_unnamed [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4falsetrue
A51
B51
C51
+
+
+

Split

+
+
(tc/group-by DS :V4 {:result-type :as-map})
+
+
+

{

+ ++++ + + + + + + +
+
"A"
+
+
+

Group: A [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
A03
A04
A07
+
+
+ ++++ + + + + + + +
+
"B"
+
+
+

Group: B [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
B40
B48
B15
+
+
+ ++++ + + + + + + +
+
"C"
+
+
+

Group: C [3 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V4:V1:V2
C45
C10
C19
+
+
+

}

+
+
+

Split and transpose a vector/column

+
+
(-> {:a ["A:a" "B:b" "C:c"]}
+    (tc/dataset)
+    (tc/separate-column :a [:V1 :V2] ":"))
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V1:V2
Aa
Bb
Cc
+
+
+
+
Other
+

Skipped

+
+
+
+

Join/Bind data sets

+
+
(def x (tc/dataset {"Id" ["A" "B" "C" "C"]
+                     "X1" [1 3 5 7]
+                     "XY" ["x2" "x4" "x6" "x8"]}))
+
+
+
(def y (tc/dataset {"Id" ["A" "B" "B" "D"]
+                     "Y1" [1 3 5 7]
+                     "XY" ["y1" "y3" "y5" "y7"]}))
+
+
+
x
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XY
A1x2
B3x4
C5x6
C7x8
+
+
+
y
+
+
+

_unnamed [4 3]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdY1XY
A1y1
B3y3
B5y5
D7y7
+
+
+
Join
+

Join matching rows from y to x

+
+
(tc/left-join x y "Id")
+
+
+

left-outer-join [5 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XYright.IdY1right.XY
A1x2A1y1
B3x4B3y3
B3x4B5y5
C5x6
C7x8
+
+
+

Join matching rows from x to y

+
+
(tc/right-join x y "Id")
+
+
+

right-outer-join [4 6]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XYright.IdY1right.XY
A1x2A1y1
B3x4B3y3
B3x4B5y5
D7y7
+
+
+

Join matching rows from both x and y

+
+
(tc/inner-join x y "Id")
+
+
+

inner-join [3 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XYY1right.XY
A1x21y1
B3x43y3
B3x45y5
+
+
+

Join keeping all the rows

+
+
(tc/full-join x y "Id")
+
+
+

outer-join [6 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XYY1right.XY
A1x21y1
B3x43y3
B3x45y5
C5x6
C7x8
D7y7
+
+
+

Return rows from x matching y

+
+
(tc/semi-join x y "Id")
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
IdX1XY
A1x2
B3x4
+
+
+

Return rows from x not matching y

+
+
(tc/anti-join x y "Id")
+
+
+

_unnamed [2 3]:

+ + + + + + + + + + + + + + + + + + + + +
IdX1XY
C5x6
C7x8
+
+
+
+
More joins
+

Select columns while joining

+
+
(tc/right-join (tc/select-columns x ["Id" "X1"])
+                (tc/select-columns y ["Id" "XY"])
+                "Id")
+
+
+

right-outer-join [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1right.IdXY
A1Ay1
B3By3
B3By5
Dy7
+
+
+
(tc/right-join (tc/select-columns x ["Id" "XY"])
+                (tc/select-columns y ["Id" "XY"])
+                "Id")
+
+
+

right-outer-join [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdXYright.Idright.XY
Ax2Ay1
Bx4By3
Bx4By5
Dy7
+
+

Aggregate columns while joining

+
+
(-> y
+    (tc/group-by ["Id"])
+    (tc/aggregate {"sumY1" #(dfn/sum (% "Y1"))})
+    (tc/right-join x "Id")
+    (tc/add-column "X1Y1" (fn [ds] (dfn/* (ds "sumY1")
+                                                    (ds "X1"))))
+    (tc/select-columns ["right.Id" "X1Y1"]))
+
+
+

right-outer-join [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
right.IdX1Y1
A1.0
B24.0
C
C
+
+

Update columns while joining

+
+
(-> x
+    (tc/select-columns ["Id" "X1"])
+    (tc/map-columns "SqX1" "X1" (fn [x] (* x x)))
+    (tc/right-join y "Id")
+    (tc/drop-columns ["X1" "Id"]))
+
+
+

right-outer-join [4 4]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SqX1right.IdY1XY
1A1y1
9B3y3
9B5y5
D7y7
+
+
+

Adds a list column with rows from y matching x (nest-join)

+
+
(-> (tc/left-join x y "Id")
+    (tc/drop-columns ["right.Id"])
+    (tc/fold-by (tc/column-names x)))
+
+
+

_unnamed [4 5]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdX1XYY1right.XY
A1x2[1][y1]
B3x4[3 5][y3 y5]
C5x6[][]
C7x8[][]
+
+
+

Some joins are skipped

+
+

Cross join

+
+
(def cjds (tc/dataset {:V1 [[2 1 1]]
+                        :V2 [[3 2]]}))
+
+
+
cjds
+
+
+

_unnamed [1 2]:

+ + + + + + + + + + + + + +
:V1:V2
[2 1 1][3 2]
+
+
+
(reduce #(tc/unroll %1 %2) cjds (tc/column-names cjds))
+
+
+

_unnamed [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
23
22
13
12
13
12
+
+
+
(-> (reduce #(tc/unroll %1 %2) cjds (tc/column-names cjds))
+    (tc/unique-by))
+
+
+

_unnamed [4 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
23
22
13
12
+
+
+
+
Bind
+
+
(def x (tc/dataset {:V1 [1 2 3]}))
+
+
+
(def y (tc/dataset {:V1 [4 5 6]}))
+
+
+
(def z (tc/dataset {:V1 [7 8 9]
+                     :V2 [0 0 0]}))
+
+
+
x
+
+
+

_unnamed [3 1]:

+ + + + + + + + + + + + + + + + + +
:V1
1
2
3
+
+
+
y
+
+
+

_unnamed [3 1]:

+ + + + + + + + + + + + + + + + + +
:V1
4
5
6
+
+
+
z
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V1:V2
70
80
90
+
+
+

Bind rows

+
+
(tc/bind x y)
+
+
+

_unnamed [6 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
3
4
5
6
+
+
+
(tc/bind x z)
+
+
+

_unnamed [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:V2
1
2
3
70
80
90
+
+
+

Bind rows using a list

+
+
(->> [x y]
+     (map-indexed #(tc/add-column %2 :id (repeat %1)))
+     (apply tc/bind))
+
+
+

_unnamed [6 2]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1:id
10
20
30
41
51
61
+
+
+

Bind columns

+
+
(tc/append x y)
+
+
+

_unnamed [3 2]:

+ + + + + + + + + + + + + + + + + + + + + +
:V1:V1
14
25
36
+
+
+
+
Set operations
+
+
(def x (tc/dataset {:V1 [1 2 2 3 3]}))
+
+
+
(def y (tc/dataset {:V1 [2 2 3 4 4]}))
+
+
+
x
+
+
+

_unnamed [5 1]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
2
3
3
+
+
+
y
+
+
+

_unnamed [5 1]:

+ + + + + + + + + + + + + + + + + + + + + + + +
:V1
2
2
3
4
4
+
+
+

Intersection

+
+
(tc/intersect x y)
+
+
+

intersection [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:V1
2
2
3
3
+
+
+

Difference

+
+
(tc/difference x y)
+
+
+

difference [1 1]:

+ + + + + + + + + + + +
:V1
1
+
+
+

Union

+
+
(tc/union x y)
+
+
+

union [4 1]:

+ + + + + + + + + + + + + + + + + + + + +
:V1
1
2
3
4
+
+
+
(tc/concat x y)
+
+
+

_unnamed [10 1]:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:V1
1
2
2
3
3
2
2
3
4
4
+
+
+

Equality not implemented

+
+ +
+
+
+
+ +
+ + +
+ + + -Clay - -
#error {
- :cause "class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')"
- :via
- [{:type java.lang.ClassCastException
-   :message "class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')"
-   :at [clojure.lang.Numbers lt "Numbers.java" 253]}]
- :trace
- [[clojure.lang.Numbers lt "Numbers.java" 253]
-  [tech.v3.datatype.binary_pred$reify__29061 binaryObject "binary_pred.clj" 186]
-  [tech.v3.datatype.BinaryPredicate invoke "BinaryPredicate.java" 19]
-  [tech.v3.datatype.dispatch$vectorized_dispatch_2 invokeStatic "dispatch.clj" 127]
-  [tech.v3.datatype.dispatch$vectorized_dispatch_2 invoke "dispatch.clj" 114]
-  [tech.v3.datatype.functional_api$eval29478$_LT___29479 invoke "functional_api.clj" 320]
-  [tech.v3.datatype.functional$_LT_ invokeStatic "functional.clj" 61]
-  [tech.v3.datatype.functional$_LT_ invoke "functional.clj" 57]
-  [clojure.lang.AFunction compare "AFunction.java" 51]
-  [tech.v3.datatype.argops$index_comparator$reify__30269 compareInts "argops.clj" 298]
-  [tech.v3.datatype.Comparators$IntComp compare "Comparators.java" 16]
-  [it.unimi.dsi.fastutil.ints.IntArrays selectionSort "IntArrays.java" 416]
-  [it.unimi.dsi.fastutil.ints.IntArrays quickSort "IntArrays.java" 463]
-  [it.unimi.dsi.fastutil.ints.IntArrays parallelQuickSort "IntArrays.java" 602]
-  [it.unimi.dsi.fastutil.ints.IntArrays parallelQuickSort "IntArrays.java" 621]
-  [tech.v3.datatype.argops$argsort invokeStatic "argops.clj" 366]
-  [tech.v3.datatype.argops$argsort invoke "argops.clj" 333]
-  [tech.v3.datatype.argops$argsort invokeStatic "argops.clj" 377]
-  [tech.v3.datatype.argops$argsort invoke "argops.clj" 333]
-  [tablecloth.column.api.column$sort_column invokeStatic "column.clj" 95]
-  [tablecloth.column.api.column$sort_column invoke "column.clj" 78]
-  [tablecloth.column.api.column$sort_column invokeStatic "column.clj" 84]
-  [tablecloth.column.api.column$sort_column invoke "column.clj" 78]
-  [tablecloth.column.api$sort_column invokeStatic "api.clj" 821]
-  [tablecloth.column.api$sort_column invoke "api.clj" 815]
-  [index$eval44288 invokeStatic "form-init1959391337599407025.clj" 4717]
-  [index$eval44288 invoke "form-init1959391337599407025.clj" 4716]
-  [clojure.lang.Compiler eval "Compiler.java" 7194]
-  [clojure.lang.Compiler eval "Compiler.java" 7149]
-  [clojure.core$eval invokeStatic "core.clj" 3215]
-  [clojure.core$eval invoke "core.clj" 3211]
-  [scicloj.clay.v2.notebook$complete invokeStatic "notebook.clj" 40]
-  [scicloj.clay.v2.notebook$complete invoke "notebook.clj" 34]
-  [scicloj.clay.v2.notebook$items_and_test_forms$fn__19799 invoke "notebook.clj" 190]
-  [clojure.lang.PersistentVector reduce "PersistentVector.java" 343]
-  [clojure.core$reduce invokeStatic "core.clj" 6885]
-  [clojure.core$reduce invoke "core.clj" 6868]
-  [scicloj.clay.v2.notebook$items_and_test_forms invokeStatic "notebook.clj" 185]
-  [scicloj.clay.v2.notebook$items_and_test_forms invoke "notebook.clj" 163]
-  [scicloj.clay.v2.make$handle_single_source_spec_BANG_ invokeStatic "make.clj" 240]
-  [scicloj.clay.v2.make$handle_single_source_spec_BANG_ invoke "make.clj" 226]
-  [clojure.core$mapv$fn__8535 invoke "core.clj" 6979]
-  [clojure.lang.ArrayChunk reduce "ArrayChunk.java" 58]
-  [clojure.core.protocols$fn__8244 invokeStatic "protocols.clj" 136]
-  [clojure.core.protocols$fn__8244 invoke "protocols.clj" 124]
-  [clojure.core.protocols$fn__8204$G__8199__8213 invoke "protocols.clj" 19]
-  [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 31]
-  [clojure.core.protocols$fn__8236 invokeStatic "protocols.clj" 75]
-  [clojure.core.protocols$fn__8236 invoke "protocols.clj" 75]
-  [clojure.core.protocols$fn__8178$G__8173__8191 invoke "protocols.clj" 13]
-  [clojure.core$reduce invokeStatic "core.clj" 6886]
-  [clojure.core$mapv invokeStatic "core.clj" 6970]
-  [clojure.core$mapv invoke "core.clj" 6970]
-  [scicloj.clay.v2.make$make_BANG_ invokeStatic "make.clj" 320]
-  [scicloj.clay.v2.make$make_BANG_ invoke "make.clj" 303]
-  [scicloj.clay.v2.api$make_BANG_ invokeStatic "api.clj" 30]
-  [scicloj.clay.v2.api$make_BANG_ invoke "api.clj" 29]
-  [gendocs$eval20472 invokeStatic "form-init1959391337599407025.clj" 4]
-  [gendocs$eval20472 invoke "form-init1959391337599407025.clj" 4]
-  [clojure.lang.Compiler eval "Compiler.java" 7194]
-  [clojure.lang.Compiler eval "Compiler.java" 7149]
-  [clojure.core$eval invokeStatic "core.clj" 3215]
-  [clojure.core$eval invoke "core.clj" 3211]
-  [nrepl.middleware.interruptible_eval$evaluate$fn__1345$fn__1346 invoke "interruptible_eval.clj" 87]
-  [clojure.lang.AFn applyToHelper "AFn.java" 152]
-  [clojure.lang.AFn applyTo "AFn.java" 144]
-  [clojure.core$apply invokeStatic "core.clj" 667]
-  [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990]
-  [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990]
-  [clojure.lang.RestFn invoke "RestFn.java" 425]
-  [nrepl.middleware.interruptible_eval$evaluate$fn__1345 invoke "interruptible_eval.clj" 87]
-  [clojure.main$repl$read_eval_print__9206$fn__9209 invoke "main.clj" 437]
-  [clojure.main$repl$read_eval_print__9206 invoke "main.clj" 437]
-  [clojure.main$repl$fn__9215 invoke "main.clj" 458]
-  [clojure.main$repl invokeStatic "main.clj" 458]
-  [clojure.main$repl doInvoke "main.clj" 368]
-  [clojure.lang.RestFn invoke "RestFn.java" 1523]
-  [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
-  [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
-  [nrepl.middleware.interruptible_eval$interruptible_eval$fn__1378$fn__1382 invoke "interruptible_eval.clj" 152]
-  [clojure.lang.AFn run "AFn.java" 22]
-  [nrepl.middleware.session$session_exec$main_loop__1448$fn__1452 invoke "session.clj" 218]
-  [nrepl.middleware.session$session_exec$main_loop__1448 invoke "session.clj" 217]
-  [clojure.lang.AFn run "AFn.java" 22]
-  [java.lang.Thread run "Thread.java" 1583]]}
-
\ No newline at end of file + \ No newline at end of file diff --git a/docs/index_files/katex2.js b/docs/index_files/katex2.js new file mode 100644 index 0000000..e701cfd --- /dev/null +++ b/docs/index_files/katex2.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.katex=t():e.katex=t()}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={d:function(t,r){for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return Yn}});class r{constructor(e,t){this.name=void 0,this.position=void 0,this.length=void 0,this.rawMessage=void 0;let n,o,s="KaTeX parse error: "+e;const i=t&&t.loc;if(i&&i.start<=i.end){const e=i.lexer.input;n=i.start,o=i.end,n===e.length?s+=" at end of input: ":s+=" at position "+(n+1)+": ";const t=e.slice(n,o).replace(/[^]/g,"$&\u0332");let r,a;r=n>15?"\u2026"+e.slice(n-15,n):e.slice(0,n),a=o+15":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;const a=function(e){return"ordgroup"===e.type||"color"===e.type?1===e.body.length?a(e.body[0]):e:"font"===e.type?a(e.body):e};var l={contains:function(e,t){return-1!==e.indexOf(t)},deflt:function(e,t){return void 0===e?t:e},escape:function(e){return String(e).replace(i,(e=>s[e]))},hyphenate:function(e){return e.replace(o,"-$1").toLowerCase()},getBaseElem:a,isCharacterBox:function(e){const t=a(e);return"mathord"===t.type||"textord"===t.type||"atom"===t.type},protocolFromUrl:function(e){const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"}};const h={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:e=>"#"+e},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(e,t)=>(t.push(e),t)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:e=>Math.max(0,e),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:e=>Math.max(0,e),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:e=>Math.max(0,e),cli:"-e, --max-expand ",cliProcessor:e=>"Infinity"===e?1/0:parseInt(e)},globalGroup:{type:"boolean",cli:!1}};function c(e){if(e.default)return e.default;const t=e.type,r=Array.isArray(t)?t[0]:t;if("string"!=typeof r)return r.enum[0];switch(r){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}class m{constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(const t in h)if(h.hasOwnProperty(t)){const r=h[t];this[t]=void 0!==e[t]?r.processor?r.processor(e[t]):e[t]:c(r)}}reportNonstrict(e,t,r){let o=this.strict;if("function"==typeof o&&(o=o(e,t,r)),o&&"ignore"!==o){if(!0===o||"error"===o)throw new n("LaTeX-incompatible input and strict mode is set to 'error': "+t+" ["+e+"]",r);"warn"===o?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+o+"': "+t+" ["+e+"]")}}useStrictBehavior(e,t,r){let n=this.strict;if("function"==typeof n)try{n=n(e,t,r)}catch(e){n="error"}return!(!n||"ignore"===n)&&(!0===n||"error"===n||("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+t+" ["+e+"]"),!1)))}isTrusted(e){if(e.url&&!e.protocol){const t=l.protocolFromUrl(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}class p{constructor(e,t,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=r}sup(){return u[d[this.id]]}sub(){return u[g[this.id]]}fracNum(){return u[f[this.id]]}fracDen(){return u[b[this.id]]}cramp(){return u[y[this.id]]}text(){return u[x[this.id]]}isTight(){return this.size>=2}}const u=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],d=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],f=[2,3,4,5,6,7,6,7],b=[3,3,5,5,7,7,7,7],y=[1,1,3,3,5,5,7,7],x=[0,1,2,3,2,3,2,3];var w={DISPLAY:u[0],TEXT:u[2],SCRIPT:u[4],SCRIPTSCRIPT:u[6]};const v=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];const k=[];function S(e){for(let t=0;t=k[t]&&e<=k[t+1])return!0;return!1}v.forEach((e=>e.blocks.forEach((e=>k.push(...e)))));const M=80,z={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"};class A{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createDocumentFragment();for(let t=0;te.toText())).join("")}}var T={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}};const B={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},C={"\xc5":"A","\xd0":"D","\xde":"o","\xe5":"a","\xf0":"d","\xfe":"o","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};function N(e,t,r){if(!T[t])throw new Error("Font metrics not found for font: "+t+".");let n=e.charCodeAt(0),o=T[t][n];if(!o&&e[0]in C&&(n=C[e[0]].charCodeAt(0),o=T[t][n]),o||"text"!==r||S(n)&&(o=T[t][77]),o)return{depth:o[0],height:o[1],italic:o[2],skew:o[3],width:o[4]}}const q={};const I=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],R=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],H=function(e,t){return t.size<2?e:I[e-1][t.size-1]};class O{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||O.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=R[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){const t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(const r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return new O(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:H(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:R[e-1]})}havingBaseStyle(e){e=e||this.style.text();const t=H(O.BASESIZE,e);return this.size===t&&this.textSize===O.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){let e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==O.BASESIZE?["sizing","reset-size"+this.size,"size"+O.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=function(e){let t;if(t=e>=5?0:e>=3?1:2,!q[t]){const e=q[t]={cssEmPerMu:B.quad[t]/18};for(const r in B)B.hasOwnProperty(r)&&(e[r]=B[r][t])}return q[t]}(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}}O.BASESIZE=6;var E=O;const L={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},D={ex:!0,em:!0,mu:!0},V=function(e){return"string"!=typeof e&&(e=e.unit),e in L||e in D||"ex"===e},P=function(e,t){let r;if(e.unit in L)r=L[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if("mu"===e.unit)r=t.fontMetrics().cssEmPerMu;else{let o;if(o=t.style.isTight()?t.havingStyle(t.style.text()):t,"ex"===e.unit)r=o.fontMetrics().xHeight;else{if("em"!==e.unit)throw new n("Invalid unit: '"+e.unit+"'");r=o.fontMetrics().quad}o!==t&&(r*=o.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*r,t.maxSize)},F=function(e){return+e.toFixed(4)+"em"},G=function(e){return e.filter((e=>e)).join(" ")},U=function(e,t,r){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},t){t.style.isTight()&&this.classes.push("mtight");const e=t.getColor();e&&(this.style.color=e)}},Y=function(e){const t=document.createElement(e);t.className=G(this.classes);for(const e in this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);for(const e in this.attributes)this.attributes.hasOwnProperty(e)&&t.setAttribute(e,this.attributes[e]);for(let e=0;e",t};class W{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,U.call(this,e,r,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return l.contains(this.classes,e)}toNode(){return Y.call(this,"span")}toMarkup(){return X.call(this,"span")}}class _{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,U.call(this,t,n),this.children=r||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return l.contains(this.classes,e)}toNode(){return Y.call(this,"a")}toMarkup(){return X.call(this,"a")}}class j{constructor(e,t,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.style=r}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(const t in this.style)this.style.hasOwnProperty(t)&&(e.style[t]=this.style[t]);return e}toMarkup(){let e=''+l.escape(this.alt)+'=n[0]&&e<=n[1])return r.name}}return null}(this.text.charCodeAt(0));l&&this.classes.push(l+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=$[this.text])}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createTextNode(this.text);let t=null;this.italic>0&&(t=document.createElement("span"),t.style.marginRight=F(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=G(this.classes));for(const e in this.style)this.style.hasOwnProperty(e)&&(t=t||document.createElement("span"),t.style[e]=this.style[e]);return t?(t.appendChild(e),t):e}toMarkup(){let e=!1,t="0&&(r+="margin-right:"+this.italic+"em;");for(const e in this.style)this.style.hasOwnProperty(e)&&(r+=l.hyphenate(e)+":"+this.style[e]+";");r&&(e=!0,t+=' style="'+l.escape(r)+'"');const n=l.escape(this.text);return e?(t+=">",t+=n,t+="",t):n}}class K{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","svg");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);for(let t=0;t':''}}class Q{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","line");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);return e}toMarkup(){let e="","\\gt",!0),se(ie,le,ye,"\u2208","\\in",!0),se(ie,le,ye,"\ue020","\\@not"),se(ie,le,ye,"\u2282","\\subset",!0),se(ie,le,ye,"\u2283","\\supset",!0),se(ie,le,ye,"\u2286","\\subseteq",!0),se(ie,le,ye,"\u2287","\\supseteq",!0),se(ie,he,ye,"\u2288","\\nsubseteq",!0),se(ie,he,ye,"\u2289","\\nsupseteq",!0),se(ie,le,ye,"\u22a8","\\models"),se(ie,le,ye,"\u2190","\\leftarrow",!0),se(ie,le,ye,"\u2264","\\le"),se(ie,le,ye,"\u2264","\\leq",!0),se(ie,le,ye,"<","\\lt",!0),se(ie,le,ye,"\u2192","\\rightarrow",!0),se(ie,le,ye,"\u2192","\\to"),se(ie,he,ye,"\u2271","\\ngeq",!0),se(ie,he,ye,"\u2270","\\nleq",!0),se(ie,le,xe,"\xa0","\\ "),se(ie,le,xe,"\xa0","\\space"),se(ie,le,xe,"\xa0","\\nobreakspace"),se(ae,le,xe,"\xa0","\\ "),se(ae,le,xe,"\xa0"," "),se(ae,le,xe,"\xa0","\\space"),se(ae,le,xe,"\xa0","\\nobreakspace"),se(ie,le,xe,null,"\\nobreak"),se(ie,le,xe,null,"\\allowbreak"),se(ie,le,be,",",","),se(ie,le,be,";",";"),se(ie,he,me,"\u22bc","\\barwedge",!0),se(ie,he,me,"\u22bb","\\veebar",!0),se(ie,le,me,"\u2299","\\odot",!0),se(ie,le,me,"\u2295","\\oplus",!0),se(ie,le,me,"\u2297","\\otimes",!0),se(ie,le,we,"\u2202","\\partial",!0),se(ie,le,me,"\u2298","\\oslash",!0),se(ie,he,me,"\u229a","\\circledcirc",!0),se(ie,he,me,"\u22a1","\\boxdot",!0),se(ie,le,me,"\u25b3","\\bigtriangleup"),se(ie,le,me,"\u25bd","\\bigtriangledown"),se(ie,le,me,"\u2020","\\dagger"),se(ie,le,me,"\u22c4","\\diamond"),se(ie,le,me,"\u22c6","\\star"),se(ie,le,me,"\u25c3","\\triangleleft"),se(ie,le,me,"\u25b9","\\triangleright"),se(ie,le,fe,"{","\\{"),se(ae,le,we,"{","\\{"),se(ae,le,we,"{","\\textbraceleft"),se(ie,le,pe,"}","\\}"),se(ae,le,we,"}","\\}"),se(ae,le,we,"}","\\textbraceright"),se(ie,le,fe,"{","\\lbrace"),se(ie,le,pe,"}","\\rbrace"),se(ie,le,fe,"[","\\lbrack",!0),se(ae,le,we,"[","\\lbrack",!0),se(ie,le,pe,"]","\\rbrack",!0),se(ae,le,we,"]","\\rbrack",!0),se(ie,le,fe,"(","\\lparen",!0),se(ie,le,pe,")","\\rparen",!0),se(ae,le,we,"<","\\textless",!0),se(ae,le,we,">","\\textgreater",!0),se(ie,le,fe,"\u230a","\\lfloor",!0),se(ie,le,pe,"\u230b","\\rfloor",!0),se(ie,le,fe,"\u2308","\\lceil",!0),se(ie,le,pe,"\u2309","\\rceil",!0),se(ie,le,we,"\\","\\backslash"),se(ie,le,we,"\u2223","|"),se(ie,le,we,"\u2223","\\vert"),se(ae,le,we,"|","\\textbar",!0),se(ie,le,we,"\u2225","\\|"),se(ie,le,we,"\u2225","\\Vert"),se(ae,le,we,"\u2225","\\textbardbl"),se(ae,le,we,"~","\\textasciitilde"),se(ae,le,we,"\\","\\textbackslash"),se(ae,le,we,"^","\\textasciicircum"),se(ie,le,ye,"\u2191","\\uparrow",!0),se(ie,le,ye,"\u21d1","\\Uparrow",!0),se(ie,le,ye,"\u2193","\\downarrow",!0),se(ie,le,ye,"\u21d3","\\Downarrow",!0),se(ie,le,ye,"\u2195","\\updownarrow",!0),se(ie,le,ye,"\u21d5","\\Updownarrow",!0),se(ie,le,ge,"\u2210","\\coprod"),se(ie,le,ge,"\u22c1","\\bigvee"),se(ie,le,ge,"\u22c0","\\bigwedge"),se(ie,le,ge,"\u2a04","\\biguplus"),se(ie,le,ge,"\u22c2","\\bigcap"),se(ie,le,ge,"\u22c3","\\bigcup"),se(ie,le,ge,"\u222b","\\int"),se(ie,le,ge,"\u222b","\\intop"),se(ie,le,ge,"\u222c","\\iint"),se(ie,le,ge,"\u222d","\\iiint"),se(ie,le,ge,"\u220f","\\prod"),se(ie,le,ge,"\u2211","\\sum"),se(ie,le,ge,"\u2a02","\\bigotimes"),se(ie,le,ge,"\u2a01","\\bigoplus"),se(ie,le,ge,"\u2a00","\\bigodot"),se(ie,le,ge,"\u222e","\\oint"),se(ie,le,ge,"\u222f","\\oiint"),se(ie,le,ge,"\u2230","\\oiiint"),se(ie,le,ge,"\u2a06","\\bigsqcup"),se(ie,le,ge,"\u222b","\\smallint"),se(ae,le,ue,"\u2026","\\textellipsis"),se(ie,le,ue,"\u2026","\\mathellipsis"),se(ae,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u22ef","\\@cdots",!0),se(ie,le,ue,"\u22f1","\\ddots",!0),se(ie,le,we,"\u22ee","\\varvdots"),se(ie,le,ce,"\u02ca","\\acute"),se(ie,le,ce,"\u02cb","\\grave"),se(ie,le,ce,"\xa8","\\ddot"),se(ie,le,ce,"~","\\tilde"),se(ie,le,ce,"\u02c9","\\bar"),se(ie,le,ce,"\u02d8","\\breve"),se(ie,le,ce,"\u02c7","\\check"),se(ie,le,ce,"^","\\hat"),se(ie,le,ce,"\u20d7","\\vec"),se(ie,le,ce,"\u02d9","\\dot"),se(ie,le,ce,"\u02da","\\mathring"),se(ie,le,de,"\ue131","\\@imath"),se(ie,le,de,"\ue237","\\@jmath"),se(ie,le,we,"\u0131","\u0131"),se(ie,le,we,"\u0237","\u0237"),se(ae,le,we,"\u0131","\\i",!0),se(ae,le,we,"\u0237","\\j",!0),se(ae,le,we,"\xdf","\\ss",!0),se(ae,le,we,"\xe6","\\ae",!0),se(ae,le,we,"\u0153","\\oe",!0),se(ae,le,we,"\xf8","\\o",!0),se(ae,le,we,"\xc6","\\AE",!0),se(ae,le,we,"\u0152","\\OE",!0),se(ae,le,we,"\xd8","\\O",!0),se(ae,le,ce,"\u02ca","\\'"),se(ae,le,ce,"\u02cb","\\`"),se(ae,le,ce,"\u02c6","\\^"),se(ae,le,ce,"\u02dc","\\~"),se(ae,le,ce,"\u02c9","\\="),se(ae,le,ce,"\u02d8","\\u"),se(ae,le,ce,"\u02d9","\\."),se(ae,le,ce,"\xb8","\\c"),se(ae,le,ce,"\u02da","\\r"),se(ae,le,ce,"\u02c7","\\v"),se(ae,le,ce,"\xa8",'\\"'),se(ae,le,ce,"\u02dd","\\H"),se(ae,le,ce,"\u25ef","\\textcircled");const ve={"--":!0,"---":!0,"``":!0,"''":!0};se(ae,le,we,"\u2013","--",!0),se(ae,le,we,"\u2013","\\textendash"),se(ae,le,we,"\u2014","---",!0),se(ae,le,we,"\u2014","\\textemdash"),se(ae,le,we,"\u2018","`",!0),se(ae,le,we,"\u2018","\\textquoteleft"),se(ae,le,we,"\u2019","'",!0),se(ae,le,we,"\u2019","\\textquoteright"),se(ae,le,we,"\u201c","``",!0),se(ae,le,we,"\u201c","\\textquotedblleft"),se(ae,le,we,"\u201d","''",!0),se(ae,le,we,"\u201d","\\textquotedblright"),se(ie,le,we,"\xb0","\\degree",!0),se(ae,le,we,"\xb0","\\degree"),se(ae,le,we,"\xb0","\\textdegree",!0),se(ie,le,we,"\xa3","\\pounds"),se(ie,le,we,"\xa3","\\mathsterling",!0),se(ae,le,we,"\xa3","\\pounds"),se(ae,le,we,"\xa3","\\textsterling",!0),se(ie,he,we,"\u2720","\\maltese"),se(ae,he,we,"\u2720","\\maltese");const ke='0123456789/@."';for(let e=0;e{if(G(e.classes)!==G(t.classes)||e.skew!==t.skew||e.maxFontSize!==t.maxFontSize)return!1;if(1===e.classes.length){const t=e.classes[0];if("mbin"===t||"mord"===t)return!1}for(const r in e.style)if(e.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;for(const r in t.style)if(t.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;return!0},Ie=function(e){let t=0,r=0,n=0;for(let o=0;ot&&(t=s.height),s.depth>r&&(r=s.depth),s.maxFontSize>n&&(n=s.maxFontSize)}e.height=t,e.depth=r,e.maxFontSize=n},Re=function(e,t,r,n){const o=new W(e,t,r,n);return Ie(o),o},He=(e,t,r,n)=>new W(e,t,r,n),Oe=function(e){const t=new A(e);return Ie(t),t},Ee=function(e,t,r){let n,o="";switch(e){case"amsrm":o="AMS";break;case"textrm":o="Main";break;case"textsf":o="SansSerif";break;case"texttt":o="Typewriter";break;default:o=e}return n="textbf"===t&&"textit"===r?"BoldItalic":"textbf"===t?"Bold":"textit"===t?"Italic":"Regular",o+"-"+n},Le={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},De={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]};var Ve={fontMap:Le,makeSymbol:Ne,mathsym:function(e,t,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Ce(e,"Main-Bold",t).metrics?Ne(e,"Main-Bold",t,r,n.concat(["mathbf"])):"\\"===e||"main"===oe[t][e].font?Ne(e,"Main-Regular",t,r,n):Ne(e,"AMS-Regular",t,r,n.concat(["amsrm"]))},makeSpan:Re,makeSvgSpan:He,makeLineSpan:function(e,t,r){const n=Re([e],[],t);return n.height=Math.max(r||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=F(n.height),n.maxFontSize=1,n},makeAnchor:function(e,t,r,n){const o=new _(e,t,r,n);return Ie(o),o},makeFragment:Oe,wrapFragment:function(e,t){return e instanceof A?Re([],[e],t):e},makeVList:function(e,t){const{children:r,depth:n}=function(e){if("individualShift"===e.positionType){const t=e.children,r=[t[0]],n=-t[0].shift-t[0].elem.depth;let o=n;for(let e=1;e0)return Ne(s,h,o,t,i.concat(c));if(l){let e,n;if("boldsymbol"===l){const t=function(e,t,r,n,o){return"textord"!==o&&Ce(e,"Math-BoldItalic",t).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(s,o,0,0,r);e=t.fontName,n=[t.fontClass]}else a?(e=Le[l].fontName,n=[l]):(e=Ee(l,t.fontWeight,t.fontShape),n=[l,t.fontWeight,t.fontShape]);if(Ce(s,e,o).metrics)return Ne(s,e,o,t,i.concat(n));if(ve.hasOwnProperty(s)&&"Typewriter"===e.slice(0,10)){const r=[];for(let a=0;a{const r=Re(["mspace"],[],t),n=P(e,t);return r.style.marginRight=F(n),r},staticSvg:function(e,t){const[r,n,o]=De[e],s=new J(r),i=new K([s],{width:F(n),height:F(o),style:"width:"+F(n),viewBox:"0 0 "+1e3*n+" "+1e3*o,preserveAspectRatio:"xMinYMin"}),a=He(["overlay"],[i],t);return a.height=o,a.style.height=F(o),a.style.width=F(n),a},svgData:De,tryCombineChars:e=>{for(let t=0;t{const r=t.classes[0],n=e.classes[0];"mbin"===r&&l.contains(et,n)?t.classes[0]="mord":"mbin"===n&&l.contains(Qe,r)&&(e.classes[0]="mord")}),{node:i},a,h),ot(o,((e,t)=>{const r=at(t),n=at(e),o=r&&n?e.hasClass("mtight")?Ye[r][n]:Ue[r][n]:null;if(o)return Ve.makeGlue(o,s)}),{node:i},a,h),o},ot=function(e,t,r,n,o){n&&e.push(n);let s=0;for(;sr=>{e.splice(t+1,0,r),s++})(s)}n&&e.pop()},st=function(e){return e instanceof A||e instanceof _||e instanceof W&&e.hasClass("enclosing")?e:null},it=function(e,t){const r=st(e);if(r){const e=r.children;if(e.length){if("right"===t)return it(e[e.length-1],"right");if("left"===t)return it(e[0],"left")}}return e},at=function(e,t){return e?(t&&(e=it(e,t)),rt[e.classes[0]]||null):null},lt=function(e,t){const r=["nulldelimiter"].concat(e.baseSizingClasses());return Je(t.concat(r))},ht=function(e,t,r){if(!e)return Je();if(We[e.type]){let n=We[e.type](e,t);if(r&&t.size!==r.size){n=Je(t.sizingClasses(r),[n],t);const e=t.sizeMultiplier/r.sizeMultiplier;n.height*=e,n.depth*=e}return n}throw new n("Got group of unknown type: '"+e.type+"'")};function ct(e,t){const r=Je(["base"],e,t),n=Je(["strut"]);return n.style.height=F(r.height+r.depth),r.depth&&(n.style.verticalAlign=F(-r.depth)),r.children.unshift(n),r}function mt(e,t){let r=null;1===e.length&&"tag"===e[0].type&&(r=e[0].tag,e=e[0].body);const n=nt(e,t,"root");let o;2===n.length&&n[1].hasClass("tag")&&(o=n.pop());const s=[];let i,a=[];for(let e=0;e0&&(s.push(ct(a,t)),a=[]),s.push(n[e]));a.length>0&&s.push(ct(a,t)),r?(i=ct(nt(r,t,!0)),i.classes=["tag"],s.push(i)):o&&s.push(o);const l=Je(["katex-html"],s);if(l.setAttribute("aria-hidden","true"),i){const e=i.children[0];e.style.height=F(l.height+l.depth),l.depth&&(e.style.verticalAlign=F(-l.depth))}return l}function pt(e){return new A(e)}class ut{constructor(e,t,r){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=r||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){const e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=G(this.classes));for(let t=0;t0&&(e+=' class ="'+l.escape(G(this.classes))+'"'),e+=">";for(let t=0;t",e}toText(){return this.children.map((e=>e.toText())).join("")}}class dt{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return l.escape(this.toText())}toText(){return this.text}}var gt={MathNode:ut,TextNode:dt,SpaceNode:class{constructor(e){this.width=void 0,this.character=void 0,this.width=e,this.character=e>=.05555&&e<=.05556?"\u200a":e>=.1666&&e<=.1667?"\u2009":e>=.2222&&e<=.2223?"\u2005":e>=.2777&&e<=.2778?"\u2005\u200a":e>=-.05556&&e<=-.05555?"\u200a\u2063":e>=-.1667&&e<=-.1666?"\u2009\u2063":e>=-.2223&&e<=-.2222?"\u205f\u2063":e>=-.2778&&e<=-.2777?"\u2005\u2063":null}toNode(){if(this.character)return document.createTextNode(this.character);{const e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",F(this.width)),e}}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},newDocumentFragment:pt};const ft=function(e,t,r){return!oe[t][e]||!oe[t][e].replace||55349===e.charCodeAt(0)||ve.hasOwnProperty(e)&&r&&(r.fontFamily&&"tt"===r.fontFamily.slice(4,6)||r.font&&"tt"===r.font.slice(4,6))||(e=oe[t][e].replace),new gt.TextNode(e)},bt=function(e){return 1===e.length?e[0]:new gt.MathNode("mrow",e)},yt=function(e,t){if("texttt"===t.fontFamily)return"monospace";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold";const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"textord"===e.type?"bold":"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";let o=e.text;if(l.contains(["\\imath","\\jmath"],o))return null;oe[n][o]&&oe[n][o].replace&&(o=oe[n][o].replace);return N(o,Ve.fontMap[r].fontName,n)?Ve.fontMap[r].variant:null},xt=function(e,t,r){if(1===e.length){const n=vt(e[0],t);return r&&n instanceof ut&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}const n=[];let o;for(let r=0;r0&&(e.text=e.text.slice(0,1)+"\u0338"+e.text.slice(1),n.pop())}}}n.push(s),o=s}return n},wt=function(e,t,r){return bt(xt(e,t,r))},vt=function(e,t){if(!e)return new gt.MathNode("mrow");if(_e[e.type]){return _e[e.type](e,t)}throw new n("Got group of unknown type: '"+e.type+"'")};function kt(e,t,r,n,o){const s=xt(e,r);let i;i=1===s.length&&s[0]instanceof ut&&l.contains(["mrow","mtable"],s[0].type)?s[0]:new gt.MathNode("mrow",s);const a=new gt.MathNode("annotation",[new gt.TextNode(t)]);a.setAttribute("encoding","application/x-tex");const h=new gt.MathNode("semantics",[i,a]),c=new gt.MathNode("math",[h]);c.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&c.setAttribute("display","block");const m=o?"katex":"katex-mathml";return Ve.makeSpan([m],[c])}const St=function(e){return new E({style:e.displayMode?w.DISPLAY:w.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},Mt=function(e,t){if(t.displayMode){const r=["katex-display"];t.leqno&&r.push("leqno"),t.fleqn&&r.push("fleqn"),e=Ve.makeSpan(r,[e])}return e},zt=function(e,t,r){const n=St(r);let o;if("mathml"===r.output)return kt(e,t,n,r.displayMode,!0);if("html"===r.output){const t=mt(e,n);o=Ve.makeSpan(["katex"],[t])}else{const s=kt(e,t,n,r.displayMode,!1),i=mt(e,n);o=Ve.makeSpan(["katex"],[s,i])}return Mt(o,r)};const At={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Tt={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]};var Bt=function(e,t,r,n,o){let s;const i=e.height+e.depth+r+n;if(/fbox|color|angl/.test(t)){if(s=Ve.makeSpan(["stretchy",t],[],o),"fbox"===t){const e=o.color&&o.getColor();e&&(s.style.borderColor=e)}}else{const e=[];/^[bx]cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));const r=new K(e,{width:"100%",height:F(i)});s=Ve.makeSvgSpan([],[r],o)}return s.height=i,s.style.height=F(i),s},Ct=function(e){const t=new gt.MathNode("mo",[new gt.TextNode(At[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},Nt=function(e,t){const{span:r,minWidth:n,height:o}=function(){let r=4e5;const n=e.label.slice(1);if(l.contains(["widehat","widecheck","widetilde","utilde"],n)){const s="ordgroup"===(o=e.base).type?o.body.length:1;let i,a,l;if(s>5)"widehat"===n||"widecheck"===n?(i=420,r=2364,l=.42,a=n+"4"):(i=312,r=2340,l=.34,a="tilde4");else{const e=[1,1,2,2,3,3][s];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][e],i=[0,239,300,360,420][e],l=[0,.24,.3,.3,.36,.42][e],a=n+e):(r=[0,600,1033,2339,2340][e],i=[0,260,286,306,312][e],l=[0,.26,.286,.3,.306,.34][e],a="tilde"+e)}const h=new J(a),c=new K([h],{width:"100%",height:F(l),viewBox:"0 0 "+r+" "+i,preserveAspectRatio:"none"});return{span:Ve.makeSvgSpan([],[c],t),minWidth:0,height:l}}{const e=[],o=Tt[n],[s,i,a]=o,l=a/1e3,h=s.length;let c,m;if(1===h){c=["hide-tail"],m=[o[3]]}else if(2===h)c=["halfarrow-left","halfarrow-right"],m=["xMinYMin","xMaxYMin"];else{if(3!==h)throw new Error("Correct katexImagesData or update code here to support\n "+h+" children.");c=["brace-left","brace-center","brace-right"],m=["xMinYMin","xMidYMin","xMaxYMin"]}for(let n=0;n0&&(r.style.minWidth=F(n)),r};function qt(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function It(e){const t=Rt(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function Rt(e){return e&&("atom"===e.type||re.hasOwnProperty(e.type))?e:null}const Ht=(e,t)=>{let r,n,o;e&&"supsub"===e.type?(n=qt(e.base,"accent"),r=n.base,e.base=r,o=function(e){if(e instanceof W)return e;throw new Error("Expected span but got "+String(e)+".")}(ht(e,t)),e.base=n):(n=qt(e,"accent"),r=n.base);const s=ht(r,t.havingCrampedStyle());let i=0;if(n.isShifty&&l.isCharacterBox(r)){const e=l.getBaseElem(r);i=ee(ht(e,t.havingCrampedStyle())).skew}const a="\\c"===n.label;let h,c=a?s.height+s.depth:Math.min(s.height,t.fontMetrics().xHeight);if(n.isStretchy)h=Nt(n,t),h=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:h,wrapperClasses:["svg-align"],wrapperStyle:i>0?{width:"calc(100% - "+F(2*i)+")",marginLeft:F(2*i)}:void 0}]},t);else{let e,r;"\\vec"===n.label?(e=Ve.staticSvg("vec",t),r=Ve.svgData.vec[1]):(e=Ve.makeOrd({mode:n.mode,text:n.label},t,"textord"),e=ee(e),e.italic=0,r=e.width,a&&(c+=e.depth)),h=Ve.makeSpan(["accent-body"],[e]);const o="\\textcircled"===n.label;o&&(h.classes.push("accent-full"),c=s.height);let l=i;o||(l-=r/2),h.style.left=F(l),"\\textcircled"===n.label&&(h.style.top=".2em"),h=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-c},{type:"elem",elem:h}]},t)}const m=Ve.makeSpan(["mord","accent"],[h],t);return o?(o.children[0]=m,o.height=Math.max(m.height,o.height),o.classes[0]="mord",o):m},Ot=(e,t)=>{const r=e.isStretchy?Ct(e.label):new gt.MathNode("mo",[ft(e.label,e.mode)]),n=new gt.MathNode("mover",[vt(e.base,t),r]);return n.setAttribute("accent","true"),n},Et=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map((e=>"\\"+e)).join("|"));je({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(e,t)=>{const r=Ze(t[0]),n=!Et.test(e.funcName),o=!n||"\\widehat"===e.funcName||"\\widetilde"===e.funcName||"\\widecheck"===e.funcName;return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,isShifty:o,base:r}},htmlBuilder:Ht,mathmlBuilder:Ot}),je({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(e,t)=>{const r=t[0];let n=e.parser.mode;return"math"===n&&(e.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+e.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:e.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Ht,mathmlBuilder:Ot}),je({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"accentUnder",mode:r.mode,label:n,base:o}},htmlBuilder:(e,t)=>{const r=ht(e.base,t),n=Nt(e,t),o="\\utilde"===e.label?.12:0,s=Ve.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:o},{type:"elem",elem:r}]},t);return Ve.makeSpan(["mord","accentunder"],[s],t)},mathmlBuilder:(e,t)=>{const r=Ct(e.label),n=new gt.MathNode("munder",[vt(e.base,t),r]);return n.setAttribute("accentunder","true"),n}});const Lt=e=>{const t=new gt.MathNode("mpadded",e?[e]:[]);return t.setAttribute("width","+0.6em"),t.setAttribute("lspace","0.3em"),t};je({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n,funcName:o}=e;return{type:"xArrow",mode:n.mode,label:o,body:t[0],below:r[0]}},htmlBuilder(e,t){const r=t.style;let n=t.havingStyle(r.sup());const o=Ve.wrapFragment(ht(e.body,n,t),t),s="\\x"===e.label.slice(0,2)?"x":"cd";let i;o.classes.push(s+"-arrow-pad"),e.below&&(n=t.havingStyle(r.sub()),i=Ve.wrapFragment(ht(e.below,n,t),t),i.classes.push(s+"-arrow-pad"));const a=Nt(e,t),l=-t.fontMetrics().axisHeight+.5*a.height;let h,c=-t.fontMetrics().axisHeight-.5*a.height-.111;if((o.depth>.25||"\\xleftequilibrium"===e.label)&&(c-=o.depth),i){const e=-t.fontMetrics().axisHeight+i.height+.5*a.height+.111;h=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l},{type:"elem",elem:i,shift:e}]},t)}else h=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l}]},t);return h.children[0].children[0].children[1].classes.push("svg-align"),Ve.makeSpan(["mrel","x-arrow"],[h],t)},mathmlBuilder(e,t){const r=Ct(e.label);let n;if(r.setAttribute("minsize","x"===e.label.charAt(0)?"1.75em":"3.0em"),e.body){const o=Lt(vt(e.body,t));if(e.below){const s=Lt(vt(e.below,t));n=new gt.MathNode("munderover",[r,s,o])}else n=new gt.MathNode("mover",[r,o])}else if(e.below){const o=Lt(vt(e.below,t));n=new gt.MathNode("munder",[r,o])}else n=Lt(),n=new gt.MathNode("mover",[r,n]);return n}});const Dt=Ve.makeSpan;function Vt(e,t){const r=nt(e.body,t,!0);return Dt([e.mclass],r,t)}function Pt(e,t){let r;const n=xt(e.body,t);return"minner"===e.mclass?r=new gt.MathNode("mpadded",n):"mord"===e.mclass?e.isCharacterBox?(r=n[0],r.type="mi"):r=new gt.MathNode("mi",n):(e.isCharacterBox?(r=n[0],r.type="mo"):r=new gt.MathNode("mo",n),"mbin"===e.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"===e.mclass||"mclose"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0em"):"minner"===e.mclass&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}je({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:Ke(o),isCharacterBox:l.isCharacterBox(o)}},htmlBuilder:Vt,mathmlBuilder:Pt});const Ft=e=>{const t="ordgroup"===e.type&&e.body.length?e.body[0]:e;return"atom"!==t.type||"bin"!==t.family&&"rel"!==t.family?"mord":"m"+t.family};je({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(e,t){let{parser:r}=e;return{type:"mclass",mode:r.mode,mclass:Ft(t[0]),body:Ke(t[1]),isCharacterBox:l.isCharacterBox(t[1])}}}),je({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(e,t){let{parser:r,funcName:n}=e;const o=t[1],s=t[0];let i;i="\\stackrel"!==n?Ft(o):"mrel";const a={type:"op",mode:o.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:Ke(o)},h={type:"supsub",mode:s.mode,base:a,sup:"\\underset"===n?null:s,sub:"\\underset"===n?s:null};return{type:"mclass",mode:r.mode,mclass:i,body:[h],isCharacterBox:l.isCharacterBox(h)}},htmlBuilder:Vt,mathmlBuilder:Pt}),je({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"pmb",mode:r.mode,mclass:Ft(t[0]),body:Ke(t[0])}},htmlBuilder(e,t){const r=nt(e.body,t,!0),n=Ve.makeSpan([e.mclass],r,t);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(e,t){const r=xt(e.body,t),n=new gt.MathNode("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});const Gt={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},Ut=e=>"textord"===e.type&&"@"===e.text;function Yt(e,t,r){const n=Gt[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},o={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])]};return r.callFunction("\\\\cdparent",[o],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}je({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:t[0]}},htmlBuilder(e,t){const r=t.havingStyle(t.style.sup()),n=Ve.wrapFragment(ht(e.label,r,t),t);return n.classes.push("cd-label-"+e.side),n.style.bottom=F(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(e,t){let r=new gt.MathNode("mrow",[vt(e.label,t)]);return r=new gt.MathNode("mpadded",[r]),r.setAttribute("width","0"),"left"===e.side&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new gt.MathNode("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}}),je({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(e,t){let{parser:r}=e;return{type:"cdlabelparent",mode:r.mode,fragment:t[0]}},htmlBuilder(e,t){const r=Ve.wrapFragment(ht(e.fragment,t),t);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(e,t){return new gt.MathNode("mrow",[vt(e.fragment,t)])}}),je({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;const o=qt(t[0],"ordgroup").body;let s="";for(let e=0;e=1114111)throw new n("\\@char with invalid code point "+s);return a<=65535?i=String.fromCharCode(a):(a-=65536,i=String.fromCharCode(55296+(a>>10),56320+(1023&a))),{type:"textord",mode:r.mode,text:i}}});const Xt=(e,t)=>{const r=nt(e.body,t.withColor(e.color),!1);return Ve.makeFragment(r)},Wt=(e,t)=>{const r=xt(e.body,t.withColor(e.color)),n=new gt.MathNode("mstyle",r);return n.setAttribute("mathcolor",e.color),n};je({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(e,t){let{parser:r}=e;const n=qt(t[0],"color-token").color,o=t[1];return{type:"color",mode:r.mode,color:n,body:Ke(o)}},htmlBuilder:Xt,mathmlBuilder:Wt}),je({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(e,t){let{parser:r,breakOnTokenText:n}=e;const o=qt(t[0],"color-token").color;r.gullet.macros.set("\\current@color",o);const s=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:o,body:s}},htmlBuilder:Xt,mathmlBuilder:Wt}),je({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(e,t,r){let{parser:n}=e;const o="["===n.gullet.future().text?n.parseSizeGroup(!0):null,s=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:s,size:o&&qt(o,"size").value}},htmlBuilder(e,t){const r=Ve.makeSpan(["mspace"],[],t);return e.newLine&&(r.classes.push("newline"),e.size&&(r.style.marginTop=F(P(e.size,t)))),r},mathmlBuilder(e,t){const r=new gt.MathNode("mspace");return e.newLine&&(r.setAttribute("linebreak","newline"),e.size&&r.setAttribute("height",F(P(e.size,t)))),r}});const _t={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},jt=e=>{const t=e.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(t))throw new n("Expected a control sequence",e);return t},$t=(e,t,r,n)=>{let o=e.gullet.macros.get(r.text);null==o&&(r.noexpand=!0,o={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,o,n)};je({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t,funcName:r}=e;t.consumeSpaces();const o=t.fetch();if(_t[o.text])return"\\global"!==r&&"\\\\globallong"!==r||(o.text=_t[o.text]),qt(t.parseFunction(),"internal");throw new n("Invalid token after macro prefix",o)}}),je({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e,o=t.gullet.popToken();const s=o.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new n("Expected a control sequence",o);let i,a=0;const l=[[]];for(;"{"!==t.gullet.future().text;)if(o=t.gullet.popToken(),"#"===o.text){if("{"===t.gullet.future().text){i=t.gullet.future(),l[a].push("{");break}if(o=t.gullet.popToken(),!/^[1-9]$/.test(o.text))throw new n('Invalid argument number "'+o.text+'"');if(parseInt(o.text)!==a+1)throw new n('Argument number "'+o.text+'" out of order');a++,l.push([])}else{if("EOF"===o.text)throw new n("Expected a macro definition");l[a].push(o.text)}let{tokens:h}=t.gullet.consumeArg();return i&&h.unshift(i),"\\edef"!==r&&"\\xdef"!==r||(h=t.gullet.expandTokens(h),h.reverse()),t.gullet.macros.set(s,{tokens:h,numArgs:a,delimiters:l},r===_t[r]),{type:"internal",mode:t.mode}}}),je({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=jt(t.gullet.popToken());t.gullet.consumeSpaces();const o=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(t);return $t(t,n,o,"\\\\globallet"===r),{type:"internal",mode:t.mode}}}),je({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=jt(t.gullet.popToken()),o=t.gullet.popToken(),s=t.gullet.popToken();return $t(t,n,s,"\\\\globalfuture"===r),t.gullet.pushToken(s),t.gullet.pushToken(o),{type:"internal",mode:t.mode}}});const Zt=function(e,t,r){const n=N(oe.math[e]&&oe.math[e].replace||e,t,r);if(!n)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return n},Kt=function(e,t,r,n){const o=r.havingBaseStyle(t),s=Ve.makeSpan(n.concat(o.sizingClasses(r)),[e],r),i=o.sizeMultiplier/r.sizeMultiplier;return s.height*=i,s.depth*=i,s.maxFontSize=o.sizeMultiplier,s},Jt=function(e,t,r){const n=t.havingBaseStyle(r),o=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=F(o),e.height-=o,e.depth+=o},Qt=function(e,t,r,n,o,s){const i=function(e,t,r,n){return Ve.makeSymbol(e,"Size"+t+"-Regular",r,n)}(e,t,o,n),a=Kt(Ve.makeSpan(["delimsizing","size"+t],[i],n),w.TEXT,n,s);return r&&Jt(a,n,w.TEXT),a},er=function(e,t,r){let n;n="Size1-Regular"===t?"delim-size1":"delim-size4";return{type:"elem",elem:Ve.makeSpan(["delimsizinginner",n],[Ve.makeSpan([],[Ve.makeSymbol(e,t,r)])])}},tr=function(e,t,r){const n=T["Size4-Regular"][e.charCodeAt(0)]?T["Size4-Regular"][e.charCodeAt(0)][4]:T["Size1-Regular"][e.charCodeAt(0)][4],o=new J("inner",function(e,t){switch(e){case"\u239c":return"M291 0 H417 V"+t+" H291z M291 0 H417 V"+t+" H291z";case"\u2223":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z";case"\u2225":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145zM367 0 H410 V"+t+" H367z M367 0 H410 V"+t+" H367z";case"\u239f":return"M457 0 H583 V"+t+" H457z M457 0 H583 V"+t+" H457z";case"\u23a2":return"M319 0 H403 V"+t+" H319z M319 0 H403 V"+t+" H319z";case"\u23a5":return"M263 0 H347 V"+t+" H263z M263 0 H347 V"+t+" H263z";case"\u23aa":return"M384 0 H504 V"+t+" H384z M384 0 H504 V"+t+" H384z";case"\u23d0":return"M312 0 H355 V"+t+" H312z M312 0 H355 V"+t+" H312z";case"\u2016":return"M257 0 H300 V"+t+" H257z M257 0 H300 V"+t+" H257zM478 0 H521 V"+t+" H478z M478 0 H521 V"+t+" H478z";default:return""}}(e,Math.round(1e3*t))),s=new K([o],{width:F(n),height:F(t),style:"width:"+F(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),i=Ve.makeSvgSpan([],[s],r);return i.height=t,i.style.height=F(t),i.style.width=F(n),{type:"elem",elem:i}},rr={type:"kern",size:-.008},nr=["|","\\lvert","\\rvert","\\vert"],or=["\\|","\\lVert","\\rVert","\\Vert"],sr=function(e,t,r,n,o,s){let i,a,h,c,m="",p=0;i=h=c=e,a=null;let u="Size1-Regular";"\\uparrow"===e?h=c="\u23d0":"\\Uparrow"===e?h=c="\u2016":"\\downarrow"===e?i=h="\u23d0":"\\Downarrow"===e?i=h="\u2016":"\\updownarrow"===e?(i="\\uparrow",h="\u23d0",c="\\downarrow"):"\\Updownarrow"===e?(i="\\Uparrow",h="\u2016",c="\\Downarrow"):l.contains(nr,e)?(h="\u2223",m="vert",p=333):l.contains(or,e)?(h="\u2225",m="doublevert",p=556):"["===e||"\\lbrack"===e?(i="\u23a1",h="\u23a2",c="\u23a3",u="Size4-Regular",m="lbrack",p=667):"]"===e||"\\rbrack"===e?(i="\u23a4",h="\u23a5",c="\u23a6",u="Size4-Regular",m="rbrack",p=667):"\\lfloor"===e||"\u230a"===e?(h=i="\u23a2",c="\u23a3",u="Size4-Regular",m="lfloor",p=667):"\\lceil"===e||"\u2308"===e?(i="\u23a1",h=c="\u23a2",u="Size4-Regular",m="lceil",p=667):"\\rfloor"===e||"\u230b"===e?(h=i="\u23a5",c="\u23a6",u="Size4-Regular",m="rfloor",p=667):"\\rceil"===e||"\u2309"===e?(i="\u23a4",h=c="\u23a5",u="Size4-Regular",m="rceil",p=667):"("===e||"\\lparen"===e?(i="\u239b",h="\u239c",c="\u239d",u="Size4-Regular",m="lparen",p=875):")"===e||"\\rparen"===e?(i="\u239e",h="\u239f",c="\u23a0",u="Size4-Regular",m="rparen",p=875):"\\{"===e||"\\lbrace"===e?(i="\u23a7",a="\u23a8",c="\u23a9",h="\u23aa",u="Size4-Regular"):"\\}"===e||"\\rbrace"===e?(i="\u23ab",a="\u23ac",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\lgroup"===e||"\u27ee"===e?(i="\u23a7",c="\u23a9",h="\u23aa",u="Size4-Regular"):"\\rgroup"===e||"\u27ef"===e?(i="\u23ab",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\lmoustache"===e||"\u23b0"===e?(i="\u23a7",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\rmoustache"!==e&&"\u23b1"!==e||(i="\u23ab",c="\u23a9",h="\u23aa",u="Size4-Regular");const d=Zt(i,u,o),g=d.height+d.depth,f=Zt(h,u,o),b=f.height+f.depth,y=Zt(c,u,o),x=y.height+y.depth;let v=0,k=1;if(null!==a){const e=Zt(a,u,o);v=e.height+e.depth,k=2}const S=g+x+v,M=S+Math.max(0,Math.ceil((t-S)/(k*b)))*k*b;let z=n.fontMetrics().axisHeight;r&&(z*=n.sizeMultiplier);const A=M/2-z,T=[];if(m.length>0){const e=M-g-x,t=Math.round(1e3*M),r=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v1759 h347 v-84\nH403z M403 1759 V0 H319 V1759 v"+t+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v1759 H0 v84 H347z\nM347 1759 V0 H263 V1759 v"+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z";case"doublevert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z\nM367 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M410 15 H367 v585 v"+t+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+t+" v1715 h263 v84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+t+" v1799 H0 v-84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v602 h84z\nM403 1759 V0 H319 V1759 v"+t+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v602 h84z\nM347 1759 V0 h-84 V1759 v"+t+" v602 h84z";case"lparen":return"M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1\nc-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,\n-36,557 l0,"+(t+84)+"c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,\n949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9\nc0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,\n-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189\nl0,-"+(t+92)+"c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,\n-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z";case"rparen":return"M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,\n63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5\nc11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,"+(t+9)+"\nc-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664\nc-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11\nc0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17\nc242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558\nl0,-"+(t+144)+"c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,\n-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z";default:throw new Error("Unknown stretchy delimiter.")}}(m,Math.round(1e3*e)),o=new J(m,r),s=(p/1e3).toFixed(3)+"em",i=(t/1e3).toFixed(3)+"em",a=new K([o],{width:s,height:i,viewBox:"0 0 "+p+" "+t}),l=Ve.makeSvgSpan([],[a],n);l.height=t/1e3,l.style.width=s,l.style.height=i,T.push({type:"elem",elem:l})}else{if(T.push(er(c,u,o)),T.push(rr),null===a){const e=M-g-x+.016;T.push(tr(h,e,n))}else{const e=(M-g-x-v)/2+.016;T.push(tr(h,e,n)),T.push(rr),T.push(er(a,u,o)),T.push(rr),T.push(tr(h,e,n))}T.push(rr),T.push(er(i,u,o))}const B=n.havingBaseStyle(w.TEXT),C=Ve.makeVList({positionType:"bottom",positionData:A,children:T},B);return Kt(Ve.makeSpan(["delimsizing","mult"],[C],B),w.TEXT,n,s)},ir=.08,ar=function(e,t,r,n,o){const s=function(e,t,r){t*=1e3;let n="";switch(e){case"sqrtMain":n=function(e,t){return"M95,"+(622+e+t)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+e/2.075+" -"+e+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+e)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize1":n=function(e,t){return"M263,"+(601+e+t)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+e/2.084+" -"+e+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+e)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize2":n=function(e,t){return"M983 "+(10+e+t)+"\nl"+e/3.13+" -"+e+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+e)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize3":n=function(e,t){return"M424,"+(2398+e+t)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+e/4.223+" -"+e+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+e)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+e)+" "+t+"\nh400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize4":n=function(e,t){return"M473,"+(2713+e+t)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+e/5.298+" -"+e+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+e)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"}(t,M);break;case"sqrtTall":n=function(e,t,r){return"M702 "+(e+t)+"H400000"+(40+e)+"\nH742v"+(r-54-t-e)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+t+"H400000v"+(40+e)+"H742z"}(t,M,r)}return n}(e,n,r),i=new J(e,s),a=new K([i],{width:"400em",height:F(t),viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Ve.makeSvgSpan(["hide-tail"],[a],o)},lr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"],hr=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"],cr=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],mr=[0,1.2,1.8,2.4,3],pr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],ur=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],dr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],gr=function(e){if("small"===e.type)return"Main-Regular";if("large"===e.type)return"Size"+e.size+"-Regular";if("stack"===e.type)return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},fr=function(e,t,r,n){for(let o=Math.min(2,3-n.style.size);ot)return r[o]}return r[r.length-1]},br=function(e,t,r,n,o,s){let i;"<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),i=l.contains(cr,e)?pr:l.contains(lr,e)?dr:ur;const a=fr(e,t,i,n);return"small"===a.type?function(e,t,r,n,o,s){const i=Ve.makeSymbol(e,"Main-Regular",o,n),a=Kt(i,t,n,s);return r&&Jt(a,n,t),a}(e,a.style,r,n,o,s):"large"===a.type?Qt(e,a.size,r,n,o,s):sr(e,t,r,n,o,s)};var yr={sqrtImage:function(e,t){const r=t.havingBaseSizing(),n=fr("\\surd",e*r.sizeMultiplier,dr,r);let o=r.sizeMultiplier;const s=Math.max(0,t.minRuleThickness-t.fontMetrics().sqrtRuleThickness);let i,a,l=0,h=0,c=0;return"small"===n.type?(c=1e3+1e3*s+80,e<1?o=1:e<1.4&&(o=.7),l=(1+s+ir)/o,h=(1+s)/o,i=ar("sqrtMain",l,c,s,t),i.style.minWidth="0.853em",a=.833/o):"large"===n.type?(c=1080*mr[n.size],h=(mr[n.size]+s)/o,l=(mr[n.size]+s+ir)/o,i=ar("sqrtSize"+n.size,l,c,s,t),i.style.minWidth="1.02em",a=1/o):(l=e+s+ir,h=e+s,c=Math.floor(1e3*e+s)+80,i=ar("sqrtTall",l,c,s,t),i.style.minWidth="0.742em",a=1.056),i.height=h,i.style.height=F(l),{span:i,advanceWidth:a,ruleWidth:(t.fontMetrics().sqrtRuleThickness+s)*o}},sizedDelim:function(e,t,r,o,s){if("<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),l.contains(lr,e)||l.contains(cr,e))return Qt(e,t,!1,r,o,s);if(l.contains(hr,e))return sr(e,mr[t],!1,r,o,s);throw new n("Illegal delimiter: '"+e+"'")},sizeToMaxHeight:mr,customSizedDelim:br,leftRightDelim:function(e,t,r,n,o,s){const i=n.fontMetrics().axisHeight*n.sizeMultiplier,a=5/n.fontMetrics().ptPerEm,l=Math.max(t-i,r+i),h=Math.max(l/500*901,2*l-a);return br(e,h,!0,n,o,s)}};const xr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},wr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function vr(e,t){const r=Rt(e);if(r&&l.contains(wr,r.text))return r;throw new n(r?"Invalid delimiter '"+r.text+"' after '"+t.funcName+"'":"Invalid delimiter type '"+e.type+"'",e)}function kr(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}je({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>{const r=vr(t[0],e);return{type:"delimsizing",mode:e.parser.mode,size:xr[e.funcName].size,mclass:xr[e.funcName].mclass,delim:r.text}},htmlBuilder:(e,t)=>"."===e.delim?Ve.makeSpan([e.mclass]):yr.sizedDelim(e.delim,e.size,t,e.mode,[e.mclass]),mathmlBuilder:e=>{const t=[];"."!==e.delim&&t.push(ft(e.delim,e.mode));const r=new gt.MathNode("mo",t);"mopen"===e.mclass||"mclose"===e.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");const n=F(yr.sizeToMaxHeight[e.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r}}),je({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=e.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new n("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:e.parser.mode,delim:vr(t[0],e).text,color:r}}}),je({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=vr(t[0],e),n=e.parser;++n.leftrightDepth;const o=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);const s=qt(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:o,left:r.text,right:s.delim,rightColor:s.color}},htmlBuilder:(e,t)=>{kr(e);const r=nt(e.body,t,!0,["mopen","mclose"]);let n,o,s=0,i=0,a=!1;for(let e=0;e{kr(e);const r=xt(e.body,t);if("."!==e.left){const t=new gt.MathNode("mo",[ft(e.left,e.mode)]);t.setAttribute("fence","true"),r.unshift(t)}if("."!==e.right){const t=new gt.MathNode("mo",[ft(e.right,e.mode)]);t.setAttribute("fence","true"),e.rightColor&&t.setAttribute("mathcolor",e.rightColor),r.push(t)}return bt(r)}}),je({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=vr(t[0],e);if(!e.parser.leftrightDepth)throw new n("\\middle without preceding \\left",r);return{type:"middle",mode:e.parser.mode,delim:r.text}},htmlBuilder:(e,t)=>{let r;if("."===e.delim)r=lt(t,[]);else{r=yr.sizedDelim(e.delim,1,t,e.mode,[]);const n={delim:e.delim,options:t};r.isMiddle=n}return r},mathmlBuilder:(e,t)=>{const r="\\vert"===e.delim||"|"===e.delim?ft("|","text"):ft(e.delim,e.mode),n=new gt.MathNode("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n}});const Sr=(e,t)=>{const r=Ve.wrapFragment(ht(e.body,t),t),n=e.label.slice(1);let o,s=t.sizeMultiplier,i=0;const a=l.isCharacterBox(e.body);if("sout"===n)o=Ve.makeSpan(["stretchy","sout"]),o.height=t.fontMetrics().defaultRuleThickness/s,i=-.5*t.fontMetrics().xHeight;else if("phase"===n){const e=P({number:.6,unit:"pt"},t),n=P({number:.35,unit:"ex"},t);s/=t.havingBaseSizing().sizeMultiplier;const a=r.height+r.depth+e+n;r.style.paddingLeft=F(a/2+e);const l=Math.floor(1e3*a*s),c="M400000 "+(h=l)+" H0 L"+h/2+" 0 l65 45 L145 "+(h-80)+" H400000z",m=new K([new J("phase",c)],{width:"400em",height:F(l/1e3),viewBox:"0 0 400000 "+l,preserveAspectRatio:"xMinYMin slice"});o=Ve.makeSvgSpan(["hide-tail"],[m],t),o.style.height=F(a),i=r.depth+e+n}else{/cancel/.test(n)?a||r.classes.push("cancel-pad"):"angl"===n?r.classes.push("anglpad"):r.classes.push("boxpad");let s=0,l=0,h=0;/box/.test(n)?(h=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness),s=t.fontMetrics().fboxsep+("colorbox"===n?0:h),l=s):"angl"===n?(h=Math.max(t.fontMetrics().defaultRuleThickness,t.minRuleThickness),s=4*h,l=Math.max(0,.25-r.depth)):(s=a?.2:0,l=s),o=Bt(r,n,s,l,t),/fbox|boxed|fcolorbox/.test(n)?(o.style.borderStyle="solid",o.style.borderWidth=F(h)):"angl"===n&&.049!==h&&(o.style.borderTopWidth=F(h),o.style.borderRightWidth=F(h)),i=r.depth+l,e.backgroundColor&&(o.style.backgroundColor=e.backgroundColor,e.borderColor&&(o.style.borderColor=e.borderColor))}var h;let c;if(e.backgroundColor)c=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:i},{type:"elem",elem:r,shift:0}]},t);else{const e=/cancel|phase/.test(n)?["svg-align"]:[];c=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:o,shift:i,wrapperClasses:e}]},t)}return/cancel/.test(n)&&(c.height=r.height,c.depth=r.depth),/cancel/.test(n)&&!a?Ve.makeSpan(["mord","cancel-lap"],[c],t):Ve.makeSpan(["mord"],[c],t)},Mr=(e,t)=>{let r=0;const n=new gt.MathNode(e.label.indexOf("colorbox")>-1?"mpadded":"menclose",[vt(e.body,t)]);switch(e.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=t.fontMetrics().fboxsep*t.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===e.label){const r=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness);n.setAttribute("style","border: "+r+"em solid "+String(e.borderColor))}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n};je({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=qt(t[0],"color-token").color,i=t[1];return{type:"enclose",mode:n.mode,label:o,backgroundColor:s,body:i}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=qt(t[0],"color-token").color,i=qt(t[1],"color-token").color,a=t[2];return{type:"enclose",mode:n.mode,label:o,backgroundColor:i,borderColor:s,body:a}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\fbox",body:t[0]}}}),je({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\angl",body:t[0]}}});const zr={};function Ar(e){let{type:t,names:r,props:n,handler:o,htmlBuilder:s,mathmlBuilder:i}=e;const a={type:t,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:o};for(let e=0;e{if(!e.parser.settings.displayMode)throw new n("{"+e.envName+"} can be used only in display mode.")};function Rr(e){if(-1===e.indexOf("ed"))return-1===e.indexOf("*")}function Hr(e,t,r){let{hskipBeforeAndAfter:o,addJot:s,cols:i,arraystretch:a,colSeparationType:l,autoTag:h,singleRow:c,emptySingleRow:m,maxNumCols:p,leqno:u}=t;if(e.gullet.beginGroup(),c||e.gullet.macros.set("\\cr","\\\\\\relax"),!a){const t=e.gullet.expandMacroAsText("\\arraystretch");if(null==t)a=1;else if(a=parseFloat(t),!a||a<0)throw new n("Invalid \\arraystretch: "+t)}e.gullet.beginGroup();let d=[];const g=[d],f=[],b=[],y=null!=h?[]:void 0;function x(){h&&e.gullet.macros.set("\\@eqnsw","1",!0)}function w(){y&&(e.gullet.macros.get("\\df@tag")?(y.push(e.subparse([new Nr("\\df@tag")])),e.gullet.macros.set("\\df@tag",void 0,!0)):y.push(Boolean(h)&&"1"===e.gullet.macros.get("\\@eqnsw")))}for(x(),b.push(qr(e));;){let t=e.parseExpression(!1,c?"\\end":"\\\\");e.gullet.endGroup(),e.gullet.beginGroup(),t={type:"ordgroup",mode:e.mode,body:t},r&&(t={type:"styling",mode:e.mode,style:r,body:[t]}),d.push(t);const o=e.fetch().text;if("&"===o){if(p&&d.length===p){if(c||l)throw new n("Too many tab characters: &",e.nextToken);e.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}e.consume()}else{if("\\end"===o){w(),1===d.length&&"styling"===t.type&&0===t.body[0].body.length&&(g.length>1||!m)&&g.pop(),b.length0&&(x+=.25),c.push({pos:x,isDashed:e[t]})}for(v(i[0]),r=0;r0&&(p+=y,le)))for(r=0;r=a)continue;(o>0||e.hskipBeforeAndAfter)&&(i=l.deflt(c.pregap,u),0!==i&&(z=Ve.makeSpan(["arraycolsep"],[]),z.style.width=F(i),M.push(z)));let d=[];for(r=0;r0){const e=Ve.makeLineSpan("hline",t,m),r=Ve.makeLineSpan("hdashline",t,m),n=[{type:"elem",elem:h,shift:0}];for(;c.length>0;){const t=c.pop(),o=t.pos-k;t.isDashed?n.push({type:"elem",elem:r,shift:o}):n.push({type:"elem",elem:e,shift:o})}h=Ve.makeVList({positionType:"individualShift",children:n},t)}if(0===T.length)return Ve.makeSpan(["mord"],[h],t);{let e=Ve.makeVList({positionType:"individualShift",children:T},t);return e=Ve.makeSpan(["tag"],[e],t),Ve.makeFragment([h,e])}},Lr={c:"center ",l:"left ",r:"right "},Dr=function(e,t){const r=[],n=new gt.MathNode("mtd",[],["mtr-glue"]),o=new gt.MathNode("mtd",[],["mml-eqn-num"]);for(let s=0;s0){const t=e.cols;let r="",n=!1,o=0,i=t.length;"separator"===t[0].type&&(a+="top ",o=1),"separator"===t[t.length-1].type&&(a+="bottom ",i-=1);for(let e=o;e0?"left ":"",a+=c[c.length-1].length>0?"right ":"";for(let e=1;e-1?"alignat":"align",s="split"===e.envName,i=Hr(e.parser,{cols:r,addJot:!0,autoTag:s?void 0:Rr(e.envName),emptySingleRow:!0,colSeparationType:o,maxNumCols:s?2:void 0,leqno:e.parser.settings.leqno},"display");let a,l=0;const h={type:"ordgroup",mode:e.mode,body:[]};if(t[0]&&"ordgroup"===t[0].type){let e="";for(let r=0;r0&&c&&(n=1),r[e]={type:"align",align:t,pregap:n,postgap:0}}return i.colSeparationType=c?"align":"alignat",i};Ar({type:"array",names:["array","darray"],props:{numArgs:1},handler(e,t){const r=(Rt(t[0])?[t[0]]:qt(t[0],"ordgroup").body).map((function(e){const t=It(e).text;if(-1!=="lcr".indexOf(t))return{type:"align",align:t};if("|"===t)return{type:"separator",separator:"|"};if(":"===t)return{type:"separator",separator:":"};throw new n("Unknown column alignment: "+t,e)})),o={cols:r,hskipBeforeAndAfter:!0,maxNumCols:r.length};return Hr(e.parser,o,Or(e.envName))},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(e){const t={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[e.envName.replace("*","")];let r="c";const o={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if("*"===e.envName.charAt(e.envName.length-1)){const t=e.parser;if(t.consumeSpaces(),"["===t.fetch().text){if(t.consume(),t.consumeSpaces(),r=t.fetch().text,-1==="lcr".indexOf(r))throw new n("Expected l or c or r",t.nextToken);t.consume(),t.consumeSpaces(),t.expect("]"),t.consume(),o.cols=[{type:"align",align:r}]}}const s=Hr(e.parser,o,Or(e.envName)),i=Math.max(0,...s.body.map((e=>e.length)));return s.cols=new Array(i).fill({type:"align",align:r}),t?{type:"leftright",mode:e.mode,body:[s],left:t[0],right:t[1],rightColor:void 0}:s},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(e){const t=Hr(e.parser,{arraystretch:.5},"script");return t.colSeparationType="small",t},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["subarray"],props:{numArgs:1},handler(e,t){const r=(Rt(t[0])?[t[0]]:qt(t[0],"ordgroup").body).map((function(e){const t=It(e).text;if(-1!=="lc".indexOf(t))return{type:"align",align:t};throw new n("Unknown column alignment: "+t,e)}));if(r.length>1)throw new n("{subarray} can contain only one column");let o={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if(o=Hr(e.parser,o,"script"),o.body.length>0&&o.body[0].length>1)throw new n("{subarray} can contain only one column");return o},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=Hr(e.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},Or(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.indexOf("r")>-1?".":"\\{",right:e.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:Vr,htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){l.contains(["gather","gather*"],e.envName)&&Ir(e);const t={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Rr(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return Hr(e.parser,t,"display")},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:Vr,htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){Ir(e);const t={autoTag:Rr(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:e.parser.settings.leqno};return Hr(e.parser,t,"display")},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["CD"],props:{numArgs:0},handler(e){return Ir(e),function(e){const t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();const r=e.fetch().text;if("&"!==r&&"\\\\"!==r){if("\\end"===r){0===t[t.length-1].length&&t.pop();break}throw new n("Expected \\\\ or \\cr or \\end",e.nextToken)}e.consume()}let r=[];const o=[r];for(let a=0;a-1);else{if(!("<>AV".indexOf(o)>-1))throw new n('Expected one of "<>AV=|." after @',l[t]);for(let e=0;e<2;e++){let r=!0;for(let h=t+1;h{const r=e.font,n=t.withFont(r);return ht(e.body,n)},Gr=(e,t)=>{const r=e.font,n=t.withFont(r);return vt(e.body,n)},Ur={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};je({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=Ze(t[0]);let s=n;return s in Ur&&(s=Ur[s]),{type:"font",mode:r.mode,font:s.slice(1),body:o}},htmlBuilder:Fr,mathmlBuilder:Gr}),je({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(e,t)=>{let{parser:r}=e;const n=t[0],o=l.isCharacterBox(n);return{type:"mclass",mode:r.mode,mclass:Ft(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:o}}}),je({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n,breakOnTokenText:o}=e;const{mode:s}=r,i=r.parseExpression(!0,o);return{type:"font",mode:s,font:"math"+n.slice(1),body:{type:"ordgroup",mode:r.mode,body:i}}},htmlBuilder:Fr,mathmlBuilder:Gr});const Yr=(e,t)=>{let r=t;return"display"===e?r=r.id>=w.SCRIPT.id?r.text():w.DISPLAY:"text"===e&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===e?r=w.SCRIPT:"scriptscript"===e&&(r=w.SCRIPTSCRIPT),r},Xr=(e,t)=>{const r=Yr(e.size,t.style),n=r.fracNum(),o=r.fracDen();let s;s=t.havingStyle(n);const i=ht(e.numer,s,t);if(e.continued){const e=8.5/t.fontMetrics().ptPerEm,r=3.5/t.fontMetrics().ptPerEm;i.height=i.height0?3*c:7*c,u=t.fontMetrics().denom1):(h>0?(m=t.fontMetrics().num2,p=c):(m=t.fontMetrics().num3,p=3*c),u=t.fontMetrics().denom2),l){const e=t.fontMetrics().axisHeight;m-i.depth-(e+.5*h){let r=new gt.MathNode("mfrac",[vt(e.numer,t),vt(e.denom,t)]);if(e.hasBarLine){if(e.barSize){const n=P(e.barSize,t);r.setAttribute("linethickness",F(n))}}else r.setAttribute("linethickness","0px");const n=Yr(e.size,t.style);if(n.size!==t.style.size){r=new gt.MathNode("mstyle",[r]);const e=n.size===w.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",e),r.setAttribute("scriptlevel","0")}if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new gt.MathNode("mo",[new gt.TextNode(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(r),null!=e.rightDelim){const r=new gt.MathNode("mo",[new gt.TextNode(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}return bt(t)}return r};je({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];let i,a=null,l=null,h="auto";switch(n){case"\\dfrac":case"\\frac":case"\\tfrac":i=!0;break;case"\\\\atopfrac":i=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=!1,a="(",l=")";break;case"\\\\bracefrac":i=!1,a="\\{",l="\\}";break;case"\\\\brackfrac":i=!1,a="[",l="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\dfrac":case"\\dbinom":h="display";break;case"\\tfrac":case"\\tbinom":h="text"}return{type:"genfrac",mode:r.mode,continued:!1,numer:o,denom:s,hasBarLine:i,leftDelim:a,rightDelim:l,size:h,barSize:null}},htmlBuilder:Xr,mathmlBuilder:Wr}),je({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];return{type:"genfrac",mode:r.mode,continued:!0,numer:o,denom:s,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}}}),je({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(e){let t,{parser:r,funcName:n,token:o}=e;switch(n){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;case"\\brace":t="\\\\bracefrac";break;case"\\brack":t="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:t,token:o}}});const _r=["display","text","script","scriptscript"],jr=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};je({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(e,t){let{parser:r}=e;const n=t[4],o=t[5],s=Ze(t[0]),i="atom"===s.type&&"open"===s.family?jr(s.text):null,a=Ze(t[1]),l="atom"===a.type&&"close"===a.family?jr(a.text):null,h=qt(t[2],"size");let c,m=null;h.isBlank?c=!0:(m=h.value,c=m.number>0);let p="auto",u=t[3];if("ordgroup"===u.type){if(u.body.length>0){const e=qt(u.body[0],"textord");p=_r[Number(e.text)]}}else u=qt(u,"textord"),p=_r[Number(u.text)];return{type:"genfrac",mode:r.mode,numer:n,denom:o,continued:!1,hasBarLine:c,barSize:m,leftDelim:i,rightDelim:l,size:p}},htmlBuilder:Xr,mathmlBuilder:Wr}),je({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(e,t){let{parser:r,funcName:n,token:o}=e;return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:qt(t[0],"size").value,token:o}}}),je({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e}(qt(t[1],"infix").size),i=t[2],a=s.number>0;return{type:"genfrac",mode:r.mode,numer:o,denom:i,continued:!1,hasBarLine:a,barSize:s,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Xr,mathmlBuilder:Wr});const $r=(e,t)=>{const r=t.style;let n,o;"supsub"===e.type?(n=e.sup?ht(e.sup,t.havingStyle(r.sup()),t):ht(e.sub,t.havingStyle(r.sub()),t),o=qt(e.base,"horizBrace")):o=qt(e,"horizBrace");const s=ht(o.base,t.havingBaseStyle(w.DISPLAY)),i=Nt(o,t);let a;if(o.isOver?(a=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:i}]},t),a.children[0].children[0].children[1].classes.push("svg-align")):(a=Ve.makeVList({positionType:"bottom",positionData:s.depth+.1+i.height,children:[{type:"elem",elem:i},{type:"kern",size:.1},{type:"elem",elem:s}]},t),a.children[0].children[0].children[0].classes.push("svg-align")),n){const e=Ve.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t);a=o.isOver?Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:e},{type:"kern",size:.2},{type:"elem",elem:n}]},t):Ve.makeVList({positionType:"bottom",positionData:e.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:e}]},t)}return Ve.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t)};je({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"horizBrace",mode:r.mode,label:n,isOver:/^\\over/.test(n),base:t[0]}},htmlBuilder:$r,mathmlBuilder:(e,t)=>{const r=Ct(e.label);return new gt.MathNode(e.isOver?"mover":"munder",[vt(e.base,t),r])}}),je({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[1],o=qt(t[0],"url").url;return r.settings.isTrusted({command:"\\href",url:o})?{type:"href",mode:r.mode,href:o,body:Ke(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:(e,t)=>{const r=nt(e.body,t,!1);return Ve.makeAnchor(e.href,[],r,t)},mathmlBuilder:(e,t)=>{let r=wt(e.body,t);return r instanceof ut||(r=new ut("mrow",[r])),r.setAttribute("href",e.href),r}}),je({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=qt(t[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");const o=[];for(let e=0;e{let{parser:r,funcName:o,token:s}=e;const i=qt(t[0],"raw").string,a=t[1];let l;r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");const h={};switch(o){case"\\htmlClass":h.class=i,l={command:"\\htmlClass",class:i};break;case"\\htmlId":h.id=i,l={command:"\\htmlId",id:i};break;case"\\htmlStyle":h.style=i,l={command:"\\htmlStyle",style:i};break;case"\\htmlData":{const e=i.split(",");for(let t=0;t{const r=nt(e.body,t,!1),n=["enclosing"];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/));const o=Ve.makeSpan(n,r,t);for(const t in e.attributes)"class"!==t&&e.attributes.hasOwnProperty(t)&&o.setAttribute(t,e.attributes[t]);return o},mathmlBuilder:(e,t)=>wt(e.body,t)}),je({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"htmlmathml",mode:r.mode,html:Ke(t[0]),mathml:Ke(t[1])}},htmlBuilder:(e,t)=>{const r=nt(e.html,t,!1);return Ve.makeFragment(r)},mathmlBuilder:(e,t)=>wt(e.mathml,t)});const Zr=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};{const t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new n("Invalid size: '"+e+"' in \\includegraphics");const r={number:+(t[1]+t[2]),unit:t[3]};if(!V(r))throw new n("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r}};je({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(e,t,r)=>{let{parser:o}=e,s={number:0,unit:"em"},i={number:.9,unit:"em"},a={number:0,unit:"em"},l="";if(r[0]){const e=qt(r[0],"raw").string.split(",");for(let t=0;t{const r=P(e.height,t);let n=0;e.totalheight.number>0&&(n=P(e.totalheight,t)-r);let o=0;e.width.number>0&&(o=P(e.width,t));const s={height:F(r+n)};o>0&&(s.width=F(o)),n>0&&(s.verticalAlign=F(-n));const i=new j(e.src,e.alt,s);return i.height=r,i.depth=n,i},mathmlBuilder:(e,t)=>{const r=new gt.MathNode("mglyph",[]);r.setAttribute("alt",e.alt);const n=P(e.height,t);let o=0;if(e.totalheight.number>0&&(o=P(e.totalheight,t)-n,r.setAttribute("valign",F(-o))),r.setAttribute("height",F(n+o)),e.width.number>0){const n=P(e.width,t);r.setAttribute("width",F(n))}return r.setAttribute("src",e.src),r}}),je({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=qt(t[0],"size");if(r.settings.strict){const e="m"===n[1],t="mu"===o.value.unit;e?(t||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+o.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):t&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:o.value}},htmlBuilder(e,t){return Ve.makeGlue(e.dimension,t)},mathmlBuilder(e,t){const r=P(e.dimension,t);return new gt.SpaceNode(r)}}),je({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:o}},htmlBuilder:(e,t)=>{let r;"clap"===e.alignment?(r=Ve.makeSpan([],[ht(e.body,t)]),r=Ve.makeSpan(["inner"],[r],t)):r=Ve.makeSpan(["inner"],[ht(e.body,t)]);const n=Ve.makeSpan(["fix"],[]);let o=Ve.makeSpan([e.alignment],[r,n],t);const s=Ve.makeSpan(["strut"]);return s.style.height=F(o.height+o.depth),o.depth&&(s.style.verticalAlign=F(-o.depth)),o.children.unshift(s),o=Ve.makeSpan(["thinbox"],[o],t),Ve.makeSpan(["mord","vbox"],[o],t)},mathmlBuilder:(e,t)=>{const r=new gt.MathNode("mpadded",[vt(e.body,t)]);if("rlap"!==e.alignment){const t="llap"===e.alignment?"-1":"-0.5";r.setAttribute("lspace",t+"width")}return r.setAttribute("width","0px"),r}}),je({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){let{funcName:r,parser:n}=e;const o=n.mode;n.switchMode("math");const s="\\("===r?"\\)":"$",i=n.parseExpression(!1,s);return n.expect(s),n.switchMode(o),{type:"styling",mode:n.mode,style:"text",body:i}}}),je({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){throw new n("Mismatched "+e.funcName)}});const Kr=(e,t)=>{switch(t.style.size){case w.DISPLAY.size:return e.display;case w.TEXT.size:return e.text;case w.SCRIPT.size:return e.script;case w.SCRIPTSCRIPT.size:return e.scriptscript;default:return e.text}};je({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"mathchoice",mode:r.mode,display:Ke(t[0]),text:Ke(t[1]),script:Ke(t[2]),scriptscript:Ke(t[3])}},htmlBuilder:(e,t)=>{const r=Kr(e,t),n=nt(r,t,!1);return Ve.makeFragment(n)},mathmlBuilder:(e,t)=>{const r=Kr(e,t);return wt(r,t)}});const Jr=(e,t,r,n,o,s,i)=>{e=Ve.makeSpan([],[e]);const a=r&&l.isCharacterBox(r);let h,c,m;if(t){const e=ht(t,n.havingStyle(o.sup()),n);c={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-e.depth)}}if(r){const e=ht(r,n.havingStyle(o.sub()),n);h={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-e.height)}}if(c&&h){const t=n.fontMetrics().bigOpSpacing5+h.elem.height+h.elem.depth+h.kern+e.depth+i;m=Ve.makeVList({positionType:"bottom",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:F(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:F(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else if(h){const t=e.height-i;m=Ve.makeVList({positionType:"top",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:F(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e}]},n)}else{if(!c)return e;{const t=e.depth+i;m=Ve.makeVList({positionType:"bottom",positionData:t,children:[{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:F(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}}const p=[m];if(h&&0!==s&&!a){const e=Ve.makeSpan(["mspace"],[],n);e.style.marginRight=F(s),p.unshift(e)}return Ve.makeSpan(["mop","op-limits"],p,n)},Qr=["\\smallint"],en=(e,t)=>{let r,n,o,s=!1;"supsub"===e.type?(r=e.sup,n=e.sub,o=qt(e.base,"op"),s=!0):o=qt(e,"op");const i=t.style;let a,h=!1;if(i.size===w.DISPLAY.size&&o.symbol&&!l.contains(Qr,o.name)&&(h=!0),o.symbol){const e=h?"Size2-Regular":"Size1-Regular";let r="";if("\\oiint"!==o.name&&"\\oiiint"!==o.name||(r=o.name.slice(1),o.name="oiint"===r?"\\iint":"\\iiint"),a=Ve.makeSymbol(o.name,e,"math",t,["mop","op-symbol",h?"large-op":"small-op"]),r.length>0){const e=a.italic,n=Ve.staticSvg(r+"Size"+(h?"2":"1"),t);a=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:a,shift:0},{type:"elem",elem:n,shift:h?.08:0}]},t),o.name="\\"+r,a.classes.unshift("mop"),a.italic=e}}else if(o.body){const e=nt(o.body,t,!0);1===e.length&&e[0]instanceof Z?(a=e[0],a.classes[0]="mop"):a=Ve.makeSpan(["mop"],e,t)}else{const e=[];for(let r=1;r{let r;if(e.symbol)r=new ut("mo",[ft(e.name,e.mode)]),l.contains(Qr,e.name)&&r.setAttribute("largeop","false");else if(e.body)r=new ut("mo",xt(e.body,t));else{r=new ut("mi",[new dt(e.name.slice(1))]);const t=new ut("mo",[ft("\u2061","text")]);r=e.parentIsSupSub?new ut("mrow",[r,t]):pt([r,t])}return r},rn={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};je({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:(e,t)=>{let{parser:r,funcName:n}=e,o=n;return 1===o.length&&(o=rn[o]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:o}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:Ke(n)}},htmlBuilder:en,mathmlBuilder:tn});const nn={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};je({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e,n=r;return 1===n.length&&(n=nn[n]),{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:en,mathmlBuilder:tn});const on=(e,t)=>{let r,n,o,s,i=!1;if("supsub"===e.type?(r=e.sup,n=e.sub,o=qt(e.base,"operatorname"),i=!0):o=qt(e,"operatorname"),o.body.length>0){const e=o.body.map((e=>{const t=e.text;return"string"==typeof t?{type:"textord",mode:e.mode,text:t}:e})),r=nt(e,t.withFont("mathrm"),!0);for(let e=0;e{let{parser:r,funcName:n}=e;const o=t[0];return{type:"operatorname",mode:r.mode,body:Ke(o),alwaysHandleSupSub:"\\operatornamewithlimits"===n,limits:!1,parentIsSupSub:!1}},htmlBuilder:on,mathmlBuilder:(e,t)=>{let r=xt(e.body,t.withFont("mathrm")),n=!0;for(let e=0;ee.toText())).join("");r=[new gt.TextNode(e)]}const o=new gt.MathNode("mi",r);o.setAttribute("mathvariant","normal");const s=new gt.MathNode("mo",[ft("\u2061","text")]);return e.parentIsSupSub?new gt.MathNode("mrow",[o,s]):gt.newDocumentFragment([o,s])}}),Br("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),$e({type:"ordgroup",htmlBuilder(e,t){return e.semisimple?Ve.makeFragment(nt(e.body,t,!1)):Ve.makeSpan(["mord"],nt(e.body,t,!0),t)},mathmlBuilder(e,t){return wt(e.body,t,!0)}}),je({type:"overline",names:["\\overline"],props:{numArgs:1},handler(e,t){let{parser:r}=e;const n=t[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(e,t){const r=ht(e.body,t.havingCrampedStyle()),n=Ve.makeLineSpan("overline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*o},{type:"elem",elem:n},{type:"kern",size:o}]},t);return Ve.makeSpan(["mord","overline"],[s],t)},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[new gt.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new gt.MathNode("mover",[vt(e.body,t),r]);return n.setAttribute("accent","true"),n}}),je({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"phantom",mode:r.mode,body:Ke(n)}},htmlBuilder:(e,t)=>{const r=nt(e.body,t.withPhantom(),!1);return Ve.makeFragment(r)},mathmlBuilder:(e,t)=>{const r=xt(e.body,t);return new gt.MathNode("mphantom",r)}}),je({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"hphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{let r=Ve.makeSpan([],[ht(e.body,t.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(let e=0;e{const r=xt(Ke(e.body),t),n=new gt.MathNode("mphantom",r),o=new gt.MathNode("mpadded",[n]);return o.setAttribute("height","0px"),o.setAttribute("depth","0px"),o}}),je({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"vphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{const r=Ve.makeSpan(["inner"],[ht(e.body,t.withPhantom())]),n=Ve.makeSpan(["fix"],[]);return Ve.makeSpan(["mord","rlap"],[r,n],t)},mathmlBuilder:(e,t)=>{const r=xt(Ke(e.body),t),n=new gt.MathNode("mphantom",r),o=new gt.MathNode("mpadded",[n]);return o.setAttribute("width","0px"),o}}),je({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;const n=qt(t[0],"size").value,o=t[1];return{type:"raisebox",mode:r.mode,dy:n,body:o}},htmlBuilder(e,t){const r=ht(e.body,t),n=P(e.dy,t);return Ve.makeVList({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){const r=new gt.MathNode("mpadded",[vt(e.body,t)]),n=e.dy.number+e.dy.unit;return r.setAttribute("voffset",n),r}}),je({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t}=e;return{type:"internal",mode:t.mode}}}),je({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler(e,t,r){let{parser:n}=e;const o=r[0],s=qt(t[0],"size"),i=qt(t[1],"size");return{type:"rule",mode:n.mode,shift:o&&qt(o,"size").value,width:s.value,height:i.value}},htmlBuilder(e,t){const r=Ve.makeSpan(["mord","rule"],[],t),n=P(e.width,t),o=P(e.height,t),s=e.shift?P(e.shift,t):0;return r.style.borderRightWidth=F(n),r.style.borderTopWidth=F(o),r.style.bottom=F(s),r.width=n,r.height=o+s,r.depth=-s,r.maxFontSize=1.125*o*t.sizeMultiplier,r},mathmlBuilder(e,t){const r=P(e.width,t),n=P(e.height,t),o=e.shift?P(e.shift,t):0,s=t.color&&t.getColor()||"black",i=new gt.MathNode("mspace");i.setAttribute("mathbackground",s),i.setAttribute("width",F(r)),i.setAttribute("height",F(n));const a=new gt.MathNode("mpadded",[i]);return o>=0?a.setAttribute("height",F(o)):(a.setAttribute("height",F(o)),a.setAttribute("depth",F(-o))),a.setAttribute("voffset",F(o)),a}});const an=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];je({type:"sizing",names:an,props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!1,r);return{type:"sizing",mode:o.mode,size:an.indexOf(n)+1,body:s}},htmlBuilder:(e,t)=>{const r=t.havingSize(e.size);return sn(e.body,r,t)},mathmlBuilder:(e,t)=>{const r=t.havingSize(e.size),n=xt(e.body,r),o=new gt.MathNode("mstyle",n);return o.setAttribute("mathsize",F(r.sizeMultiplier)),o}}),je({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(e,t,r)=>{let{parser:n}=e,o=!1,s=!1;const i=r[0]&&qt(r[0],"ordgroup");if(i){let e="";for(let t=0;t{const r=Ve.makeSpan([],[ht(e.body,t)]);if(!e.smashHeight&&!e.smashDepth)return r;if(e.smashHeight&&(r.height=0,r.children))for(let e=0;e{const r=new gt.MathNode("mpadded",[vt(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}}),je({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n}=e;const o=r[0],s=t[0];return{type:"sqrt",mode:n.mode,body:s,index:o}},htmlBuilder(e,t){let r=ht(e.body,t.havingCrampedStyle());0===r.height&&(r.height=t.fontMetrics().xHeight),r=Ve.wrapFragment(r,t);const n=t.fontMetrics().defaultRuleThickness;let o=n;t.style.idr.height+r.depth+s&&(s=(s+c-r.height-r.depth)/2);const m=a.height-r.height-s-l;r.style.paddingLeft=F(h);const p=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+m)},{type:"elem",elem:a},{type:"kern",size:l}]},t);if(e.index){const r=t.havingStyle(w.SCRIPTSCRIPT),n=ht(e.index,r,t),o=.6*(p.height-p.depth),s=Ve.makeVList({positionType:"shift",positionData:-o,children:[{type:"elem",elem:n}]},t),i=Ve.makeSpan(["root"],[s]);return Ve.makeSpan(["mord","sqrt"],[i,p],t)}return Ve.makeSpan(["mord","sqrt"],[p],t)},mathmlBuilder(e,t){const{body:r,index:n}=e;return n?new gt.MathNode("mroot",[vt(r,t),vt(n,t)]):new gt.MathNode("msqrt",[vt(r,t)])}});const ln={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};je({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e,t){let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!0,r),i=n.slice(1,n.length-5);return{type:"styling",mode:o.mode,style:i,body:s}},htmlBuilder(e,t){const r=ln[e.style],n=t.havingStyle(r).withFont("");return sn(e.body,n,t)},mathmlBuilder(e,t){const r=ln[e.style],n=t.havingStyle(r),o=xt(e.body,n),s=new gt.MathNode("mstyle",o),i={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[e.style];return s.setAttribute("scriptlevel",i[0]),s.setAttribute("displaystyle",i[1]),s}});$e({type:"supsub",htmlBuilder(e,t){const r=function(e,t){const r=e.base;if(r)return"op"===r.type?r.limits&&(t.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?en:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(t.style.size===w.DISPLAY.size||r.limits)?on:null:"accent"===r.type?l.isCharacterBox(r.base)?Ht:null:"horizBrace"===r.type&&!e.sub===r.isOver?$r:null;return null}(e,t);if(r)return r(e,t);const{base:n,sup:o,sub:s}=e,i=ht(n,t);let a,h;const c=t.fontMetrics();let m=0,p=0;const u=n&&l.isCharacterBox(n);if(o){const e=t.havingStyle(t.style.sup());a=ht(o,e,t),u||(m=i.height-e.fontMetrics().supDrop*e.sizeMultiplier/t.sizeMultiplier)}if(s){const e=t.havingStyle(t.style.sub());h=ht(s,e,t),u||(p=i.depth+e.fontMetrics().subDrop*e.sizeMultiplier/t.sizeMultiplier)}let d;d=t.style===w.DISPLAY?c.sup1:t.style.cramped?c.sup3:c.sup2;const g=t.sizeMultiplier,f=F(.5/c.ptPerEm/g);let b,y=null;if(h){const t=e.base&&"op"===e.base.type&&e.base.name&&("\\oiint"===e.base.name||"\\oiiint"===e.base.name);(i instanceof Z||t)&&(y=F(-i.italic))}if(a&&h){m=Math.max(m,d,a.depth+.25*c.xHeight),p=Math.max(p,c.sub2);const e=4*c.defaultRuleThickness;if(m-a.depth-(h.height-p)0&&(m+=t,p-=t)}const r=[{type:"elem",elem:h,shift:p,marginRight:f,marginLeft:y},{type:"elem",elem:a,shift:-m,marginRight:f}];b=Ve.makeVList({positionType:"individualShift",children:r},t)}else if(h){p=Math.max(p,c.sub1,h.height-.8*c.xHeight);const e=[{type:"elem",elem:h,marginLeft:y,marginRight:f}];b=Ve.makeVList({positionType:"shift",positionData:p,children:e},t)}else{if(!a)throw new Error("supsub must have either sup or sub.");m=Math.max(m,d,a.depth+.25*c.xHeight),b=Ve.makeVList({positionType:"shift",positionData:-m,children:[{type:"elem",elem:a,marginRight:f}]},t)}const x=at(i,"right")||"mord";return Ve.makeSpan([x],[i,Ve.makeSpan(["msupsub"],[b])],t)},mathmlBuilder(e,t){let r,n,o=!1;e.base&&"horizBrace"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(o=!0,r=e.base.isOver)),!e.base||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0);const s=[vt(e.base,t)];let i;if(e.sub&&s.push(vt(e.sub,t)),e.sup&&s.push(vt(e.sup,t)),o)i=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;i=r&&"op"===r.type&&r.limits&&t.style===w.DISPLAY||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.style===w.DISPLAY||r.limits)?"munderover":"msubsup"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"munder":"msub"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"mover":"msup"}return new gt.MathNode(i,s)}}),$e({type:"atom",htmlBuilder(e,t){return Ve.mathsym(e.text,e.mode,t,["m"+e.family])},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[ft(e.text,e.mode)]);if("bin"===e.family){const n=yt(e,t);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===e.family?r.setAttribute("separator","true"):"open"!==e.family&&"close"!==e.family||r.setAttribute("stretchy","false");return r}});const hn={mi:"italic",mn:"normal",mtext:"normal"};$e({type:"mathord",htmlBuilder(e,t){return Ve.makeOrd(e,t,"mathord")},mathmlBuilder(e,t){const r=new gt.MathNode("mi",[ft(e.text,e.mode,t)]),n=yt(e,t)||"italic";return n!==hn[r.type]&&r.setAttribute("mathvariant",n),r}}),$e({type:"textord",htmlBuilder(e,t){return Ve.makeOrd(e,t,"textord")},mathmlBuilder(e,t){const r=ft(e.text,e.mode,t),n=yt(e,t)||"normal";let o;return o="text"===e.mode?new gt.MathNode("mtext",[r]):/[0-9]/.test(e.text)?new gt.MathNode("mn",[r]):"\\prime"===e.text?new gt.MathNode("mo",[r]):new gt.MathNode("mi",[r]),n!==hn[o.type]&&o.setAttribute("mathvariant",n),o}});const cn={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},mn={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};$e({type:"spacing",htmlBuilder(e,t){if(mn.hasOwnProperty(e.text)){const r=mn[e.text].className||"";if("text"===e.mode){const n=Ve.makeOrd(e,t,"textord");return n.classes.push(r),n}return Ve.makeSpan(["mspace",r],[Ve.mathsym(e.text,e.mode,t)],t)}if(cn.hasOwnProperty(e.text))return Ve.makeSpan(["mspace",cn[e.text]],[],t);throw new n('Unknown type of space "'+e.text+'"')},mathmlBuilder(e,t){let r;if(!mn.hasOwnProperty(e.text)){if(cn.hasOwnProperty(e.text))return new gt.MathNode("mspace");throw new n('Unknown type of space "'+e.text+'"')}return r=new gt.MathNode("mtext",[new gt.TextNode("\xa0")]),r}});const pn=()=>{const e=new gt.MathNode("mtd",[]);return e.setAttribute("width","50%"),e};$e({type:"tag",mathmlBuilder(e,t){const r=new gt.MathNode("mtable",[new gt.MathNode("mtr",[pn(),new gt.MathNode("mtd",[wt(e.body,t)]),pn(),new gt.MathNode("mtd",[wt(e.tag,t)])])]);return r.setAttribute("width","100%"),r}});const un={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},dn={"\\textbf":"textbf","\\textmd":"textmd"},gn={"\\textit":"textit","\\textup":"textup"},fn=(e,t)=>{const r=e.font;return r?un[r]?t.withTextFontFamily(un[r]):dn[r]?t.withTextFontWeight(dn[r]):t.withTextFontShape(gn[r]):t};je({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"text",mode:r.mode,body:Ke(o),font:n}},htmlBuilder(e,t){const r=fn(e,t),n=nt(e.body,r,!0);return Ve.makeSpan(["mord","text"],n,r)},mathmlBuilder(e,t){const r=fn(e,t);return wt(e.body,r)}}),je({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"underline",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ht(e.body,t),n=Ve.makeLineSpan("underline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ve.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:o},{type:"elem",elem:n},{type:"kern",size:3*o},{type:"elem",elem:r}]},t);return Ve.makeSpan(["mord","underline"],[s],t)},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[new gt.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new gt.MathNode("munder",[vt(e.body,t),r]);return n.setAttribute("accentunder","true"),n}}),je({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"vcenter",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ht(e.body,t),n=t.fontMetrics().axisHeight,o=.5*(r.height-n-(r.depth+n));return Ve.makeVList({positionType:"shift",positionData:o,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){return new gt.MathNode("mpadded",[vt(e.body,t)],["vcenter"])}}),je({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(e,t,r){throw new n("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(e,t){const r=bn(e),n=[],o=t.havingStyle(t.style.text());for(let t=0;te.body.replace(/ /g,e.star?"\u2423":"\xa0");var yn=Xe;const xn="[ \r\n\t]",wn="(\\\\[a-zA-Z@]+)"+xn+"*",vn="[\u0300-\u036f]",kn=new RegExp(vn+"+$"),Sn="("+xn+"+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff]"+vn+"*|[\ud800-\udbff][\udc00-\udfff]"+vn+"*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|"+wn+"|\\\\[^\ud800-\udfff])";class Mn{constructor(e,t){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=t,this.tokenRegex=new RegExp(Sn,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new Nr("EOF",new Cr(this,t,t));const r=this.tokenRegex.exec(e);if(null===r||r.index!==t)throw new n("Unexpected character: '"+e[t]+"'",new Nr(e[t],new Cr(this,t,t+1)));const o=r[6]||r[3]||(r[2]?"\\ ":" ");if(14===this.catcodes[o]){const t=e.indexOf("\n",this.tokenRegex.lastIndex);return-1===t?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=t+1,this.lex()}return new Nr(o,new Cr(this,t,this.tokenRegex.lastIndex))}}class zn{constructor(e,t){void 0===e&&(e={}),void 0===t&&(t={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new n("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const e=this.undefStack.pop();for(const t in e)e.hasOwnProperty(t)&&(null==e[t]?delete this.current[t]:this.current[t]=e[t])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,r){if(void 0===r&&(r=!1),r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!t.hasOwnProperty(e)&&(t[e]=this.current[e])}null==t?delete this.current[e]:this.current[e]=t}}var An=Tr;Br("\\noexpand",(function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}})),Br("\\expandafter",(function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}})),Br("\\@firstoftwo",(function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}})),Br("\\@secondoftwo",(function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}})),Br("\\@ifnextchar",(function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}})),Br("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Br("\\TextOrMath",(function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}}));const Tn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};Br("\\char",(function(e){let t,r=e.popToken(),o="";if("'"===r.text)t=8,r=e.popToken();else if('"'===r.text)t=16,r=e.popToken();else if("`"===r.text)if(r=e.popToken(),"\\"===r.text[0])o=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new n("\\char` missing argument");o=r.text.charCodeAt(0)}else t=10;if(t){if(o=Tn[r.text],null==o||o>=t)throw new n("Invalid base-"+t+" digit "+r.text);let s;for(;null!=(s=Tn[e.future().text])&&s{let o=e.consumeArg().tokens;if(1!==o.length)throw new n("\\newcommand's first argument must be a macro name");const s=o[0].text,i=e.isDefined(s);if(i&&!t)throw new n("\\newcommand{"+s+"} attempting to redefine "+s+"; use \\renewcommand");if(!i&&!r)throw new n("\\renewcommand{"+s+"} when command "+s+" does not yet exist; use \\newcommand");let a=0;if(o=e.consumeArg().tokens,1===o.length&&"["===o[0].text){let t="",r=e.expandNextToken();for(;"]"!==r.text&&"EOF"!==r.text;)t+=r.text,r=e.expandNextToken();if(!t.match(/^\s*[0-9]+\s*$/))throw new n("Invalid number of arguments: "+t);a=parseInt(t),o=e.consumeArg().tokens}return e.macros.set(s,{tokens:o,numArgs:a}),""};Br("\\newcommand",(e=>Bn(e,!1,!0))),Br("\\renewcommand",(e=>Bn(e,!0,!1))),Br("\\providecommand",(e=>Bn(e,!0,!0))),Br("\\message",(e=>{const t=e.consumeArgs(1)[0];return console.log(t.reverse().map((e=>e.text)).join("")),""})),Br("\\errmessage",(e=>{const t=e.consumeArgs(1)[0];return console.error(t.reverse().map((e=>e.text)).join("")),""})),Br("\\show",(e=>{const t=e.popToken(),r=t.text;return console.log(t,e.macros.get(r),yn[r],oe.math[r],oe.text[r]),""})),Br("\\bgroup","{"),Br("\\egroup","}"),Br("~","\\nobreakspace"),Br("\\lq","`"),Br("\\rq","'"),Br("\\aa","\\r a"),Br("\\AA","\\r A"),Br("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),Br("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Br("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),Br("\u212c","\\mathscr{B}"),Br("\u2130","\\mathscr{E}"),Br("\u2131","\\mathscr{F}"),Br("\u210b","\\mathscr{H}"),Br("\u2110","\\mathscr{I}"),Br("\u2112","\\mathscr{L}"),Br("\u2133","\\mathscr{M}"),Br("\u211b","\\mathscr{R}"),Br("\u212d","\\mathfrak{C}"),Br("\u210c","\\mathfrak{H}"),Br("\u2128","\\mathfrak{Z}"),Br("\\Bbbk","\\Bbb{k}"),Br("\xb7","\\cdotp"),Br("\\llap","\\mathllap{\\textrm{#1}}"),Br("\\rlap","\\mathrlap{\\textrm{#1}}"),Br("\\clap","\\mathclap{\\textrm{#1}}"),Br("\\mathstrut","\\vphantom{(}"),Br("\\underbar","\\underline{\\text{#1}}"),Br("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),Br("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),Br("\\ne","\\neq"),Br("\u2260","\\neq"),Br("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),Br("\u2209","\\notin"),Br("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),Br("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),Br("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),Br("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),Br("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),Br("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),Br("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),Br("\u27c2","\\perp"),Br("\u203c","\\mathclose{!\\mkern-0.8mu!}"),Br("\u220c","\\notni"),Br("\u231c","\\ulcorner"),Br("\u231d","\\urcorner"),Br("\u231e","\\llcorner"),Br("\u231f","\\lrcorner"),Br("\xa9","\\copyright"),Br("\xae","\\textregistered"),Br("\ufe0f","\\textregistered"),Br("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Br("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Br("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Br("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Br("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}"),Br("\u22ee","\\vdots"),Br("\\varGamma","\\mathit{\\Gamma}"),Br("\\varDelta","\\mathit{\\Delta}"),Br("\\varTheta","\\mathit{\\Theta}"),Br("\\varLambda","\\mathit{\\Lambda}"),Br("\\varXi","\\mathit{\\Xi}"),Br("\\varPi","\\mathit{\\Pi}"),Br("\\varSigma","\\mathit{\\Sigma}"),Br("\\varUpsilon","\\mathit{\\Upsilon}"),Br("\\varPhi","\\mathit{\\Phi}"),Br("\\varPsi","\\mathit{\\Psi}"),Br("\\varOmega","\\mathit{\\Omega}"),Br("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Br("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Br("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Br("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Br("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Br("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");const Cn={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};Br("\\dots",(function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in Cn?t=Cn[r]:("\\not"===r.slice(0,4)||r in oe.math&&l.contains(["bin","rel"],oe.math[r].group))&&(t="\\dotsb"),t}));const Nn={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Br("\\dotso",(function(e){return e.future().text in Nn?"\\ldots\\,":"\\ldots"})),Br("\\dotsc",(function(e){const t=e.future().text;return t in Nn&&","!==t?"\\ldots\\,":"\\ldots"})),Br("\\cdots",(function(e){return e.future().text in Nn?"\\@cdots\\,":"\\@cdots"})),Br("\\dotsb","\\cdots"),Br("\\dotsm","\\cdots"),Br("\\dotsi","\\!\\cdots"),Br("\\dotsx","\\ldots\\,"),Br("\\DOTSI","\\relax"),Br("\\DOTSB","\\relax"),Br("\\DOTSX","\\relax"),Br("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Br("\\,","\\tmspace+{3mu}{.1667em}"),Br("\\thinspace","\\,"),Br("\\>","\\mskip{4mu}"),Br("\\:","\\tmspace+{4mu}{.2222em}"),Br("\\medspace","\\:"),Br("\\;","\\tmspace+{5mu}{.2777em}"),Br("\\thickspace","\\;"),Br("\\!","\\tmspace-{3mu}{.1667em}"),Br("\\negthinspace","\\!"),Br("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Br("\\negthickspace","\\tmspace-{5mu}{.277em}"),Br("\\enspace","\\kern.5em "),Br("\\enskip","\\hskip.5em\\relax"),Br("\\quad","\\hskip1em\\relax"),Br("\\qquad","\\hskip2em\\relax"),Br("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Br("\\tag@paren","\\tag@literal{({#1})}"),Br("\\tag@literal",(e=>{if(e.macros.get("\\df@tag"))throw new n("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"})),Br("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Br("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Br("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Br("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Br("\\newline","\\\\\\relax"),Br("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");const qn=F(T["Main-Regular"]["T".charCodeAt(0)][1]-.7*T["Main-Regular"]["A".charCodeAt(0)][1]);Br("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+qn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),Br("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+qn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),Br("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Br("\\@hspace","\\hskip #1\\relax"),Br("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Br("\\ordinarycolon",":"),Br("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Br("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Br("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Br("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Br("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Br("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Br("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Br("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Br("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Br("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Br("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Br("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Br("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Br("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Br("\u2237","\\dblcolon"),Br("\u2239","\\eqcolon"),Br("\u2254","\\coloneqq"),Br("\u2255","\\eqqcolon"),Br("\u2a74","\\Coloneqq"),Br("\\ratio","\\vcentcolon"),Br("\\coloncolon","\\dblcolon"),Br("\\colonequals","\\coloneqq"),Br("\\coloncolonequals","\\Coloneqq"),Br("\\equalscolon","\\eqqcolon"),Br("\\equalscoloncolon","\\Eqqcolon"),Br("\\colonminus","\\coloneq"),Br("\\coloncolonminus","\\Coloneq"),Br("\\minuscolon","\\eqcolon"),Br("\\minuscoloncolon","\\Eqcolon"),Br("\\coloncolonapprox","\\Colonapprox"),Br("\\coloncolonsim","\\Colonsim"),Br("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Br("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Br("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Br("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Br("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),Br("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Br("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Br("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Br("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Br("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Br("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Br("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Br("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Br("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),Br("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),Br("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),Br("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),Br("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),Br("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),Br("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),Br("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),Br("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),Br("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),Br("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),Br("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),Br("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),Br("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),Br("\\imath","\\html@mathml{\\@imath}{\u0131}"),Br("\\jmath","\\html@mathml{\\@jmath}{\u0237}"),Br("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),Br("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),Br("\u27e6","\\llbracket"),Br("\u27e7","\\rrbracket"),Br("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),Br("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),Br("\u2983","\\lBrace"),Br("\u2984","\\rBrace"),Br("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29b5}}"),Br("\u29b5","\\minuso"),Br("\\darr","\\downarrow"),Br("\\dArr","\\Downarrow"),Br("\\Darr","\\Downarrow"),Br("\\lang","\\langle"),Br("\\rang","\\rangle"),Br("\\uarr","\\uparrow"),Br("\\uArr","\\Uparrow"),Br("\\Uarr","\\Uparrow"),Br("\\N","\\mathbb{N}"),Br("\\R","\\mathbb{R}"),Br("\\Z","\\mathbb{Z}"),Br("\\alef","\\aleph"),Br("\\alefsym","\\aleph"),Br("\\Alpha","\\mathrm{A}"),Br("\\Beta","\\mathrm{B}"),Br("\\bull","\\bullet"),Br("\\Chi","\\mathrm{X}"),Br("\\clubs","\\clubsuit"),Br("\\cnums","\\mathbb{C}"),Br("\\Complex","\\mathbb{C}"),Br("\\Dagger","\\ddagger"),Br("\\diamonds","\\diamondsuit"),Br("\\empty","\\emptyset"),Br("\\Epsilon","\\mathrm{E}"),Br("\\Eta","\\mathrm{H}"),Br("\\exist","\\exists"),Br("\\harr","\\leftrightarrow"),Br("\\hArr","\\Leftrightarrow"),Br("\\Harr","\\Leftrightarrow"),Br("\\hearts","\\heartsuit"),Br("\\image","\\Im"),Br("\\infin","\\infty"),Br("\\Iota","\\mathrm{I}"),Br("\\isin","\\in"),Br("\\Kappa","\\mathrm{K}"),Br("\\larr","\\leftarrow"),Br("\\lArr","\\Leftarrow"),Br("\\Larr","\\Leftarrow"),Br("\\lrarr","\\leftrightarrow"),Br("\\lrArr","\\Leftrightarrow"),Br("\\Lrarr","\\Leftrightarrow"),Br("\\Mu","\\mathrm{M}"),Br("\\natnums","\\mathbb{N}"),Br("\\Nu","\\mathrm{N}"),Br("\\Omicron","\\mathrm{O}"),Br("\\plusmn","\\pm"),Br("\\rarr","\\rightarrow"),Br("\\rArr","\\Rightarrow"),Br("\\Rarr","\\Rightarrow"),Br("\\real","\\Re"),Br("\\reals","\\mathbb{R}"),Br("\\Reals","\\mathbb{R}"),Br("\\Rho","\\mathrm{P}"),Br("\\sdot","\\cdot"),Br("\\sect","\\S"),Br("\\spades","\\spadesuit"),Br("\\sub","\\subset"),Br("\\sube","\\subseteq"),Br("\\supe","\\supseteq"),Br("\\Tau","\\mathrm{T}"),Br("\\thetasym","\\vartheta"),Br("\\weierp","\\wp"),Br("\\Zeta","\\mathrm{Z}"),Br("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Br("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Br("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Br("\\bra","\\mathinner{\\langle{#1}|}"),Br("\\ket","\\mathinner{|{#1}\\rangle}"),Br("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Br("\\Bra","\\left\\langle#1\\right|"),Br("\\Ket","\\left|#1\\right\\rangle");const In=e=>t=>{const r=t.consumeArg().tokens,n=t.consumeArg().tokens,o=t.consumeArg().tokens,s=t.consumeArg().tokens,i=t.macros.get("|"),a=t.macros.get("\\|");t.macros.beginGroup();const l=t=>r=>{e&&(r.macros.set("|",i),o.length&&r.macros.set("\\|",a));let s=t;if(!t&&o.length){"|"===r.future().text&&(r.popToken(),s=!0)}return{tokens:s?o:n,numArgs:0}};t.macros.set("|",l(!1)),o.length&&t.macros.set("\\|",l(!0));const h=t.consumeArg().tokens,c=t.expandTokens([...s,...h,...r]);return t.macros.endGroup(),{tokens:c.reverse(),numArgs:0}};Br("\\bra@ket",In(!1)),Br("\\bra@set",In(!0)),Br("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Br("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Br("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Br("\\angln","{\\angl n}"),Br("\\blue","\\textcolor{##6495ed}{#1}"),Br("\\orange","\\textcolor{##ffa500}{#1}"),Br("\\pink","\\textcolor{##ff00af}{#1}"),Br("\\red","\\textcolor{##df0030}{#1}"),Br("\\green","\\textcolor{##28ae7b}{#1}"),Br("\\gray","\\textcolor{gray}{#1}"),Br("\\purple","\\textcolor{##9d38bd}{#1}"),Br("\\blueA","\\textcolor{##ccfaff}{#1}"),Br("\\blueB","\\textcolor{##80f6ff}{#1}"),Br("\\blueC","\\textcolor{##63d9ea}{#1}"),Br("\\blueD","\\textcolor{##11accd}{#1}"),Br("\\blueE","\\textcolor{##0c7f99}{#1}"),Br("\\tealA","\\textcolor{##94fff5}{#1}"),Br("\\tealB","\\textcolor{##26edd5}{#1}"),Br("\\tealC","\\textcolor{##01d1c1}{#1}"),Br("\\tealD","\\textcolor{##01a995}{#1}"),Br("\\tealE","\\textcolor{##208170}{#1}"),Br("\\greenA","\\textcolor{##b6ffb0}{#1}"),Br("\\greenB","\\textcolor{##8af281}{#1}"),Br("\\greenC","\\textcolor{##74cf70}{#1}"),Br("\\greenD","\\textcolor{##1fab54}{#1}"),Br("\\greenE","\\textcolor{##0d923f}{#1}"),Br("\\goldA","\\textcolor{##ffd0a9}{#1}"),Br("\\goldB","\\textcolor{##ffbb71}{#1}"),Br("\\goldC","\\textcolor{##ff9c39}{#1}"),Br("\\goldD","\\textcolor{##e07d10}{#1}"),Br("\\goldE","\\textcolor{##a75a05}{#1}"),Br("\\redA","\\textcolor{##fca9a9}{#1}"),Br("\\redB","\\textcolor{##ff8482}{#1}"),Br("\\redC","\\textcolor{##f9685d}{#1}"),Br("\\redD","\\textcolor{##e84d39}{#1}"),Br("\\redE","\\textcolor{##bc2612}{#1}"),Br("\\maroonA","\\textcolor{##ffbde0}{#1}"),Br("\\maroonB","\\textcolor{##ff92c6}{#1}"),Br("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Br("\\maroonD","\\textcolor{##ca337c}{#1}"),Br("\\maroonE","\\textcolor{##9e034e}{#1}"),Br("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Br("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Br("\\purpleC","\\textcolor{##aa87ff}{#1}"),Br("\\purpleD","\\textcolor{##7854ab}{#1}"),Br("\\purpleE","\\textcolor{##543b78}{#1}"),Br("\\mintA","\\textcolor{##f5f9e8}{#1}"),Br("\\mintB","\\textcolor{##edf2df}{#1}"),Br("\\mintC","\\textcolor{##e0e5cc}{#1}"),Br("\\grayA","\\textcolor{##f6f7f7}{#1}"),Br("\\grayB","\\textcolor{##f0f1f2}{#1}"),Br("\\grayC","\\textcolor{##e3e5e6}{#1}"),Br("\\grayD","\\textcolor{##d6d8da}{#1}"),Br("\\grayE","\\textcolor{##babec2}{#1}"),Br("\\grayF","\\textcolor{##888d93}{#1}"),Br("\\grayG","\\textcolor{##626569}{#1}"),Br("\\grayH","\\textcolor{##3b3e40}{#1}"),Br("\\grayI","\\textcolor{##21242c}{#1}"),Br("\\kaBlue","\\textcolor{##314453}{#1}"),Br("\\kaGreen","\\textcolor{##71B307}{#1}");const Rn={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class Hn{constructor(e,t,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new zn(An,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Mn(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken(),({tokens:n,end:r}=this.consumeArg(["]"]))}else({tokens:n,start:t,end:r}=this.consumeArg());return this.pushToken(new Nr("EOF",r.loc)),this.pushTokens(n),t.range(r,"")}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(e){const t=[],r=e&&e.length>0;r||this.consumeSpaces();const o=this.future();let s,i=0,a=0;do{if(s=this.popToken(),t.push(s),"{"===s.text)++i;else if("}"===s.text){if(--i,-1===i)throw new n("Extra }",s)}else if("EOF"===s.text)throw new n("Unexpected end of input in a macro argument, expected '"+(e&&r?e[a]:"}")+"'",s);if(e&&r)if((0===i||1===i&&"{"===e[a])&&s.text===e[a]){if(++a,a===e.length){t.splice(-a,a);break}}else a=0}while(0!==i||r);return"{"===o.text&&"}"===t[t.length-1].text&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:o,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new n("The length of delimiters doesn't match the number of args!");const r=t[0];for(let e=0;ethis.settings.maxExpand)throw new n("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){const t=this.popToken(),r=t.text,o=t.noexpand?null:this._getExpansion(r);if(null==o||e&&o.unexpandable){if(e&&null==o&&"\\"===r[0]&&!this.isDefined(r))throw new n("Undefined control sequence: "+r);return this.pushToken(t),!1}this.countExpansion(1);let s=o.tokens;const i=this.consumeArgs(o.numArgs,o.delimiters);if(o.numArgs){s=s.slice();for(let e=s.length-1;e>=0;--e){let t=s[e];if("#"===t.text){if(0===e)throw new n("Incomplete placeholder at end of macro body",t);if(t=s[--e],"#"===t.text)s.splice(e+1,1);else{if(!/^[1-9]$/.test(t.text))throw new n("Not a valid argument number",t);s.splice(e,2,...i[+t.text-1])}}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new Nr(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return this.countExpansion(t.length),t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map((e=>e.text)).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(-1!==r.indexOf("#")){const t=r.replace(/##/g,"");for(;-1!==t.indexOf("#"+(e+1));)++e}const t=new Mn(r,this.settings),n=[];let o=t.lex();for(;"EOF"!==o.text;)n.push(o),o=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||yn.hasOwnProperty(e)||oe.math.hasOwnProperty(e)||oe.text.hasOwnProperty(e)||Rn.hasOwnProperty(e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:yn.hasOwnProperty(e)&&!yn[e].primitive}}const On=/^[\u208a\u208b\u208c\u208d\u208e\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2090\u2091\u2095\u1d62\u2c7c\u2096\u2097\u2098\u2099\u2092\u209a\u1d63\u209b\u209c\u1d64\u1d65\u2093\u1d66\u1d67\u1d68\u1d69\u1d6a]/,En=Object.freeze({"\u208a":"+","\u208b":"-","\u208c":"=","\u208d":"(","\u208e":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1d62":"i","\u2c7c":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209a":"p","\u1d63":"r","\u209b":"s","\u209c":"t","\u1d64":"u","\u1d65":"v","\u2093":"x","\u1d66":"\u03b2","\u1d67":"\u03b3","\u1d68":"\u03c1","\u1d69":"\u03d5","\u1d6a":"\u03c7","\u207a":"+","\u207b":"-","\u207c":"=","\u207d":"(","\u207e":")","\u2070":"0","\xb9":"1","\xb2":"2","\xb3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1d2c":"A","\u1d2e":"B","\u1d30":"D","\u1d31":"E","\u1d33":"G","\u1d34":"H","\u1d35":"I","\u1d36":"J","\u1d37":"K","\u1d38":"L","\u1d39":"M","\u1d3a":"N","\u1d3c":"O","\u1d3e":"P","\u1d3f":"R","\u1d40":"T","\u1d41":"U","\u2c7d":"V","\u1d42":"W","\u1d43":"a","\u1d47":"b","\u1d9c":"c","\u1d48":"d","\u1d49":"e","\u1da0":"f","\u1d4d":"g","\u02b0":"h","\u2071":"i","\u02b2":"j","\u1d4f":"k","\u02e1":"l","\u1d50":"m","\u207f":"n","\u1d52":"o","\u1d56":"p","\u02b3":"r","\u02e2":"s","\u1d57":"t","\u1d58":"u","\u1d5b":"v","\u02b7":"w","\u02e3":"x","\u02b8":"y","\u1dbb":"z","\u1d5d":"\u03b2","\u1d5e":"\u03b3","\u1d5f":"\u03b4","\u1d60":"\u03d5","\u1d61":"\u03c7","\u1dbf":"\u03b8"}),Ln={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"},"\u0327":{text:"\\c"}},Dn={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u1e09":"c\u0327\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\xe7":"c\u0327","\u010f":"d\u030c","\u1e0b":"d\u0307","\u1e11":"d\u0327","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u1e1d":"e\u0327\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u0229":"e\u0327","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u0123":"g\u0327","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\u1e29":"h\u0327","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u0137":"k\u0327","\u013a":"l\u0301","\u013e":"l\u030c","\u013c":"l\u0327","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\u0146":"n\u0327","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u0157":"r\u0327","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u015f":"s\u0327","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\u0163":"t\u0327","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u1e08":"C\u0327\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\xc7":"C\u0327","\u010e":"D\u030c","\u1e0a":"D\u0307","\u1e10":"D\u0327","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u1e1c":"E\u0327\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u0228":"E\u0327","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u0122":"G\u0327","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\u1e28":"H\u0327","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0136":"K\u0327","\u0139":"L\u0301","\u013d":"L\u030c","\u013b":"L\u0327","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\u0145":"N\u0327","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u0156":"R\u0327","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u015e":"S\u0327","\u0164":"T\u030c","\u1e6a":"T\u0307","\u0162":"T\u0327","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"};class Vn{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new Hn(e,t,this.mode),this.settings=t,this.leftrightDepth=0}expect(e,t){if(void 0===t&&(t=!0),this.fetch().text!==e)throw new n("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{const e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new Nr("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t){const r=[];for(;;){"math"===this.mode&&this.consumeSpaces();const n=this.fetch();if(-1!==Vn.endOfExpression.indexOf(n.text))break;if(t&&n.text===t)break;if(e&&yn[n.text]&&yn[n.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&r.push(o)}return"text"===this.mode&&this.formLigatures(r),this.handleInfixNodes(r)}handleInfixNodes(e){let t,r=-1;for(let o=0;o=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+t[0]+'" used in math mode',e);const r=oe[this.mode][t].group,n=Cr.range(e);let s;if(te.hasOwnProperty(r)){const e=r;s={type:"atom",mode:this.mode,family:e,loc:n,text:t}}else s={type:r,mode:this.mode,loc:n,text:t};o=s}else{if(!(t.charCodeAt(0)>=128))return null;this.settings.strict&&(S(t.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'" ('+t.charCodeAt(0)+")",e)),o={type:"textord",mode:"text",loc:Cr.range(e),text:t}}if(this.consume(),r)for(let t=0;t>` to send it to the second argument position. -;; #### Sort +;; #### Sort ;; Use `sort-column` to sort a column: diff --git a/project.clj b/project.clj index e6939db..93c6e6b 100644 --- a/project.clj +++ b/project.clj @@ -8,6 +8,6 @@ :lein-tools-deps/config {:config-files [:install :user :project]} :profiles {:dev {:cloverage {:runner :midje} :dependencies [[midje "1.10.10"] - [org.scicloj/clay "2-beta9"]] + [org.scicloj/clay "2-beta11"]] :plugins [[lein-midje "3.2.1"] [lein-cloverage "1.2.4"]]}}) diff --git a/src/tablecloth/column/api/column.clj b/src/tablecloth/column/api/column.clj index 15d3c5d..6dd83b9 100644 --- a/src/tablecloth/column/api/column.clj +++ b/src/tablecloth/column/api/column.clj @@ -88,10 +88,10 @@ (fn? order-or-comparator))) (throw (IllegalArgumentException. "`order-or-comparator` must be `:asc`, `:desc`, or a function."))) - (let [order-fn-lookup {:asc fun/<, :desc fun/>} - comparator-fn (if (fn? order-or-comparator) - order-or-comparator - (order-fn-lookup order-or-comparator)) + (let [comparator-fn (cond + (fn? order-or-comparator) order-or-comparator + (= :asc order-or-comparator) compare + (= :desc order-or-comparator) #(compare %2 %1)) sorted-indices (argsort comparator-fn col)] (col/select col sorted-indices))))