diff --git a/latest/404.html b/latest/404.html index 2aed054..29096d6 100644 --- a/latest/404.html +++ b/latest/404.html @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -194,7 +194,7 @@
- +
TidierDB.jl @@ -242,7 +242,7 @@
- +
TidierDB.jl @@ -625,7 +625,7 @@

404 - Not found

- + @@ -636,7 +636,7 @@

404 - Not found

- +
diff --git a/latest/assets/stylesheets/main.0253249f.min.css b/latest/assets/stylesheets/main.0253249f.min.css deleted file mode 100644 index 9a7a698..0000000 --- a/latest/assets/stylesheets/main.0253249f.min.css +++ /dev/null @@ -1 +0,0 @@ -@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol ol ol ol,.md-typeset ul ol ol ol{list-style-type:upper-alpha}.md-typeset ol ol ol ol ol,.md-typeset ul ol ol ol ol{list-style-type:upper-roman}.md-typeset ol[type],.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .md-code__content{display:grid}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}@media print{.md-feedback{display:none}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"ยท";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em;position:relative}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-style:normal;font-weight:400;outline:none;text-align:initial;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__alias{margin-left:.3rem;opacity:.7}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"โŽ‡";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"โŒ˜";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"โŒƒ";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"โ—†";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"โŒฅ";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"โ‡ง";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"โ–";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"โŠž";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"โ†“";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"โ†";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"โ†’";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"โ†‘";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"โŒซ";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"โ‡ค";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"โ‡ช";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"โŒง";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"โ˜ฐ";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"โŒฆ";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"โ";padding-right:.4em}.md-typeset .keys .key-end:before{content:"โค“";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"โŽ‹";padding-right:.4em}.md-typeset .keys .key-home:before{content:"โค’";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"โŽ€";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"โ‡Ÿ";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"โ‡ž";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"โŽ™";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"โ‡ฅ";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"โŒค";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"โŽ";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media print{.giscus,[id=__comments]{display:none}}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/latest/assets/stylesheets/main.0253249f.min.css.map b/latest/assets/stylesheets/main.0253249f.min.css.map deleted file mode 100644 index 7481947..0000000 --- a/latest/assets/stylesheets/main.0253249f.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"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,4DACE,oBlC05GJ,CkCv5GI,sFACE,gClCy5GN,CkCp5GE,oEACE,0BlCu5GJ,CkCp5GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCs5GN,CkCl5GI,gFACE,alCo5GN,CkCz6GE,4DACE,oBlC46GJ,CkCz6GI,sFACE,gClC26GN,CkCt6GE,oEACE,0BlCy6GJ,CkCt6GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCw6GN,CkCp6GI,gFACE,alCs6GN,CkC37GE,0DACE,oBlC87GJ,CkC37GI,oFACE,gClC67GN,CkCx7GE,kEACE,0BlC27GJ,CkCx7GI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4ClC07GN,CkCt7GI,8EACE,alCw7GN,CkC78GE,oDACE,oBlCg9GJ,CkC78GI,8EACE,gClC+8GN,CkC18GE,4DACE,0BlC68GJ,CkC18GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClC48GN,CkCx8GI,wEACE,alC08GN,CkC/9GE,4DACE,oBlCk+GJ,CkC/9GI,sFACE,gClCi+GN,CkC59GE,oEACE,0BlC+9GJ,CkC59GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC89GN,CkC19GI,gFACE,alC49GN,CkCj/GE,wDACE,oBlCo/GJ,CkCj/GI,kFACE,gClCm/GN,CkC9+GE,gEACE,0BlCi/GJ,CkC9+GI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2ClCg/GN,CkC5+GI,4EACE,alC8+GN,CmClpHA,MACE,qMnCqpHF,CmC5oHE,sBAEE,uCAAA,CADA,gBnCgpHJ,CmC5oHI,mCACE,anC8oHN,CmC/oHI,mCACE,cnC8oHN,CmC1oHM,4BACE,sBnC4oHR,CmCzoHQ,mCACE,gCnC2oHV,CmCvoHQ,2DACE,SAAA,CAEA,uBAAA,CADA,enC0oHV,CmCroHQ,yGACE,SAAA,CACA,uBnCuoHV,CmCnoHQ,yCACE,YnCqoHV,CmC9nHE,0BACE,eAAA,CACA,enCgoHJ,CmC7nHI,+BACE,oBnC+nHN,CmC1nHE,gDACE,YnC4nHJ,CmCxnHE,8BAIE,+BAAA,CAHA,oBAAA,CAEA,WAAA,CAGA,SAAA,CAKA,4B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","file":"main.css"} \ No newline at end of file diff --git a/latest/assets/stylesheets/main.6f8fc17f.min.css b/latest/assets/stylesheets/main.6f8fc17f.min.css new file mode 100644 index 0000000..a0d06b0 --- /dev/null +++ b/latest/assets/stylesheets/main.6f8fc17f.min.css @@ -0,0 +1 @@ +@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset h5 code{text-transform:none}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol ol ol ol,.md-typeset ul ol ol ol{list-style-type:upper-alpha}.md-typeset ol ol ol ol ol,.md-typeset ul ol ol ol ol{list-style-type:upper-roman}.md-typeset ol[type],.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .md-code__content{display:grid}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}@media print{.md-feedback{display:none}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"ยท";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em;position:relative}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-style:normal;font-weight:400;outline:none;text-align:initial;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__alias{margin-left:.3rem;opacity:.7}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"โŽ‡";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"โŒ˜";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"โŒƒ";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"โ—†";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"โŒฅ";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"โ‡ง";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"โ–";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"โŠž";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"โ†“";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"โ†";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"โ†’";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"โ†‘";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"โŒซ";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"โ‡ค";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"โ‡ช";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"โŒง";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"โ˜ฐ";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"โŒฆ";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"โ";padding-right:.4em}.md-typeset .keys .key-end:before{content:"โค“";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"โŽ‹";padding-right:.4em}.md-typeset .keys .key-home:before{content:"โค’";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"โŽ€";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"โ‡Ÿ";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"โ‡ž";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"โŽ™";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"โ‡ฅ";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"โŒค";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"โŽ";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media print{.giscus,[id=__comments]{display:none}}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/latest/assets/stylesheets/main.6f8fc17f.min.css.map b/latest/assets/stylesheets/main.6f8fc17f.min.css.map new file mode 100644 index 0000000..8ba5ce3 --- /dev/null +++ b/latest/assets/stylesheets/main.6f8fc17f.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"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FACE,alCq4GN,CkC15GE,4DACE,oBlC65GJ,CkC15GI,sFACE,gClC45GN,CkCv5GE,oEACE,0BlC05GJ,CkCv5GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCy5GN,CkCr5GI,gFACE,alCu5GN,CkC56GE,4DACE,oBlC+6GJ,CkC56GI,sFACE,gClC86GN,CkCz6GE,oEACE,0BlC46GJ,CkCz6GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC26GN,CkCv6GI,gFACE,alCy6GN,CkC97GE,0DACE,oBlCi8GJ,CkC97GI,oFACE,gClCg8GN,CkC37GE,kEACE,0BlC87GJ,CkC37GI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4ClC67GN,CkCz7GI,8EACE,alC27GN,CkCh9GE,oDACE,oBlCm9GJ,CkCh9GI,8EACE,gClCk9GN,CkC78GE,4DACE,0BlCg9GJ,CkC78GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClC+8GN,CkC38GI,wEACE,alC68GN,CkCl+GE,4DACE,oBlCq+GJ,CkCl+GI,sFACE,gClCo+GN,CkC/9GE,oEACE,0BlCk+GJ,CkC/9GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCi+GN,CkC79GI,gFACE,alC+9GN,CkCp/GE,wDACE,oBlCu/GJ,CkCp/GI,kFACE,gClCs/GN,CkCj/GE,gEACE,0BlCo/GJ,CkCj/GI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2ClCm/GN,CkC/+GI,4EACE,alCi/GN,CmCrpHA,MACE,qMnCwpHF,CmC/oHE,sBAEE,uCAAA,CADA,gBnCmpHJ,CmC/oHI,mCACE,anCipHN,CmClpHI,mCACE,cnCipHN,CmC7oHM,4BACE,sBnC+oHR,CmC5oHQ,mCACE,gCnC8oHV,CmC1oHQ,2DACE,SAAA,CAEA,uBAAA,CADA,enC6oHV,CmCxoHQ,yGACE,SAAA,CACA,uBnC0oHV,CmCtoHQ,yCACE,YnCwoHV,CmCjoHE,0BACE,eAAA,CACA,enCmoHJ,CmChoHI,+BACE,oBnCkoHN,CmC7nHE,gDACE,YnC+nHJ,CmC3nHE,8BAIE,+BAAA,CAHA,oBAAA,CAEA,WA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","file":"main.css"} \ No newline at end of file diff --git a/latest/examples/generated/UserGuide/Snowflake/index.html b/latest/examples/generated/UserGuide/Snowflake/index.html index 974851d..d0ad5e8 100644 --- a/latest/examples/generated/UserGuide/Snowflake/index.html +++ b/latest/examples/generated/UserGuide/Snowflake/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/athena/index.html b/latest/examples/generated/UserGuide/athena/index.html index f7e2031..e60c10f 100644 --- a/latest/examples/generated/UserGuide/athena/index.html +++ b/latest/examples/generated/UserGuide/athena/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/databricks/index.html b/latest/examples/generated/UserGuide/databricks/index.html index 817ae6d..4f6ddb8 100644 --- a/latest/examples/generated/UserGuide/databricks/index.html +++ b/latest/examples/generated/UserGuide/databricks/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/duckplyr_reprex/index.html b/latest/examples/generated/UserGuide/duckplyr_reprex/index.html index b08d22e..31ec481 100644 --- a/latest/examples/generated/UserGuide/duckplyr_reprex/index.html +++ b/latest/examples/generated/UserGuide/duckplyr_reprex/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/ex_joining/index.html b/latest/examples/generated/UserGuide/ex_joining/index.html index 572605f..d909f35 100644 --- a/latest/examples/generated/UserGuide/ex_joining/index.html +++ b/latest/examples/generated/UserGuide/ex_joining/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/from_queryex/index.html b/latest/examples/generated/UserGuide/from_queryex/index.html index 88d78ad..05e320f 100644 --- a/latest/examples/generated/UserGuide/from_queryex/index.html +++ b/latest/examples/generated/UserGuide/from_queryex/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
TidierDB.jl @@ -932,7 +932,7 @@

Preview or save an intermediate t - + @@ -943,7 +943,7 @@

Preview or save an intermediate t - +

diff --git a/latest/examples/generated/UserGuide/functions_pass_to_DB/index.html b/latest/examples/generated/UserGuide/functions_pass_to_DB/index.html index 674b924..d7588c1 100644 --- a/latest/examples/generated/UserGuide/functions_pass_to_DB/index.html +++ b/latest/examples/generated/UserGuide/functions_pass_to_DB/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/getting_started/index.html b/latest/examples/generated/UserGuide/getting_started/index.html index ed7009f..d12fe54 100644 --- a/latest/examples/generated/UserGuide/getting_started/index.html +++ b/latest/examples/generated/UserGuide/getting_started/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/ibis_comp/index.html b/latest/examples/generated/UserGuide/ibis_comp/index.html index 1860941..f2ee5d2 100644 --- a/latest/examples/generated/UserGuide/ibis_comp/index.html +++ b/latest/examples/generated/UserGuide/ibis_comp/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/key_differences/index.html b/latest/examples/generated/UserGuide/key_differences/index.html index 40a33cb..f58b59b 100644 --- a/latest/examples/generated/UserGuide/key_differences/index.html +++ b/latest/examples/generated/UserGuide/key_differences/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/outofmemex/index.html b/latest/examples/generated/UserGuide/outofmemex/index.html index 2a43d57..51f84f7 100644 --- a/latest/examples/generated/UserGuide/outofmemex/index.html +++ b/latest/examples/generated/UserGuide/outofmemex/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -198,7 +198,7 @@
- +
TidierDB.jl @@ -246,7 +246,7 @@
- +
TidierDB.jl @@ -734,7 +734,7 @@

Working With Larger than RAM Datasets

- + @@ -745,7 +745,7 @@

Working With Larger than RAM Datasets

- +
diff --git a/latest/examples/generated/UserGuide/s3viaduckdb/index.html b/latest/examples/generated/UserGuide/s3viaduckdb/index.html index dcc0634..e8c4b7f 100644 --- a/latest/examples/generated/UserGuide/s3viaduckdb/index.html +++ b/latest/examples/generated/UserGuide/s3viaduckdb/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -198,7 +198,7 @@
- +
TidierDB.jl @@ -246,7 +246,7 @@
- +
diff --git a/latest/examples/generated/UserGuide/udfs_ex/index.html b/latest/examples/generated/UserGuide/udfs_ex/index.html index d5c31cb..015318a 100644 --- a/latest/examples/generated/UserGuide/udfs_ex/index.html +++ b/latest/examples/generated/UserGuide/udfs_ex/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -203,7 +203,7 @@
- +
TidierDB.jl @@ -251,7 +251,7 @@
- +
TidierDB.jl @@ -983,7 +983,7 @@

How to create UDF in DuckDB - + @@ -994,7 +994,7 @@

How to create UDF in DuckDB - +

diff --git a/latest/index.html b/latest/index.html index fe36bea..403e654 100644 --- a/latest/index.html +++ b/latest/index.html @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -201,7 +201,7 @@
- +
TidierDB.jl @@ -249,7 +249,7 @@
- +
TidierDB.jl @@ -1099,7 +1099,7 @@

Missing a function or backend? - + @@ -1110,7 +1110,7 @@

Missing a function or backend? - +

diff --git a/latest/reference/index.html b/latest/reference/index.html index b510492..df28bd9 100644 --- a/latest/reference/index.html +++ b/latest/reference/index.html @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -201,7 +201,7 @@
- +
TidierDB.jl @@ -249,7 +249,7 @@
- +
-

source

+

source

# TidierDB.copy_toMethod.

   copy_to(conn, df_or_path, "name")
@@ -808,7 +808,7 @@ 

Reference - Exported functionsjulia> copy_to(db, df, "test");

-

source

+

source

# TidierDB.db_tableFunction.

db_table(database, table_name, athena_params, delta = false, iceberg = false)
@@ -848,7 +848,7 @@ 

Reference - Exported functions 3 โ”‚ value BIGINT 1 df_mem 4 โ”‚ percent DOUBLE 1 df_mem, false, DuckDB.Connection(":memory:"), TidierDB.CTE[], 0, nothing)

-

source

+

source

# TidierDB.show_tablesMethod.

show_tables(con; GBQ_datasetname)
@@ -864,7 +864,7 @@ 

Reference - Exported functionsjulia> show_tables(db);

-

source

+

source

# TidierDB.warningsMethod.

warnings(show::Bool)
@@ -879,7 +879,7 @@ 

Reference - Exported functions
julia> warnings(true);
 

-

source

+

source

# TidierDB.@anti_joinMacro.

@anti_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -922,7 +922,7 @@ 

Reference - Exported functions 4 โ”‚ AH aa 3 0.8 5 โ”‚ AJ aa 5 1.0

-

source

+

source

# TidierDB.@arrangeMacro.

@arrange(sql_query, columns...)
@@ -962,7 +962,7 @@ 

Reference - Exported functions 9 โ”‚ AJ aa 5 1.0 10 โ”‚ AE bb 5 0.5

-

source

+

source

# TidierDB.@collectMacro.

@collect(sql_query, stream = false)
@@ -999,7 +999,7 @@ 

Reference - Exported functions 9 โ”‚ AI bb 4 0.9 10 โ”‚ AJ aa 5 1.0

-

source

+

source

# TidierDB.@countMacro.

@count(sql_query, columns...)
@@ -1032,7 +1032,7 @@ 

Reference - Exported functions 1 โ”‚ aa 5 2 โ”‚ bb 5

-

source

+

source

# TidierDB.@create_viewMacro.

@view(sql_query, name, replace = true)
@@ -1061,7 +1061,7 @@ 

Reference - Exported functions 1 โ”‚ id BIGINT 1 viewer 2 โ”‚ value BIGINT 1 viewer, false, DuckDB.DB(":memory:"), TidierDB.CTE[], 0, nothing, "", "", 0)

-

source

+

source

# TidierDB.@distinctMacro.

@distinct(sql_query, columns...)
@@ -1114,7 +1114,7 @@ 

Reference - Exported functions 9 โ”‚ AI bb 4 0.9 10 โ”‚ AJ aa 5 1.0

-

source

+

source

# TidierDB.@filterMacro.

@filter(sql_query, conditions...)
@@ -1170,7 +1170,7 @@ 

Reference - Exported functions 1 โ”‚ aa 0.6 2 โ”‚ bb 0.5

-

source

+

source

# TidierDB.@full_joinMacro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -1220,7 +1220,7 @@ 

Reference - Exported functions 10 โ”‚ AJ aa 5 1.0 missing missing missing 11 โ”‚ missing missing missing missing AM X 74

-

source

+

source

# TidierDB.@group_byMacro.

@group_by(sql_query, columns...)
@@ -1253,7 +1253,7 @@ 

Reference - Exported functions 1 โ”‚ aa 2 โ”‚ bb

-

source

+

source

# TidierDB.@headMacro.

-

source

+

source

# TidierDB.@inner_joinMacro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -1327,7 +1327,7 @@ 

Reference - Exported functions 4 โ”‚ AG bb 2 0.7 AG Y 83 5 โ”‚ AI bb 4 0.9 AI X 95

-

source

+

source

# TidierDB.@left_joinMacro.

@left_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -1398,16 +1398,17 @@ 

Reference - Exported functions 9 โ”‚ AH aa 3 0.8 missing missing missing 10 โ”‚ AJ aa 5 1.0 missing missing missing

-

source

+

source

# TidierDB.@mutateMacro.

-
@mutate(sql_query, exprs...)
+
@mutate(sql_query, exprs...; _by)
 

Mutate SQL table rows by adding new columns or modifying existing ones.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions for mutating the table. New columns can be added or existing columns modified using column_name = expression syntax, where expression can involve existing columns.
  • +
  • _by: optional argument that supports single column names, or vectors of columns to allow for grouping for the transformation in the macro call

Examples

-

source

+

source

# TidierDB.@renameMacro.

@rename(sql_query, renamings...)
@@ -1475,7 +1495,7 @@ 

Reference - Exported functions 9 โ”‚ AI bb 4 0.9 10 โ”‚ AJ aa 5 1.0

-

source

+

source

# TidierDB.@right_joinMacro.

@right_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -1539,7 +1559,7 @@ 

Reference - Exported functions 5 โ”‚ AI bb 4 0.9 AI X 95 6 โ”‚ missing missing missing missing AM X 74

-

source

+

source

# TidierDB.@selectMacro.

@select(sql_query, columns)
@@ -1600,7 +1620,7 @@ 

Reference - Exported functions 9 โ”‚ 4 0.9 10 โ”‚ 5 1.0

-

source

+

source

# TidierDB.@semi_joinMacro.

@semi_join(sql_query, join_table, orignal_table_col = new_table_col)
@@ -1643,7 +1663,7 @@ 

Reference - Exported functions 4 โ”‚ AG bb 2 0.7 5 โ”‚ AI bb 4 0.9

-

source

+

source

# TidierDB.@slice_maxMacro.

@slice_max(sql_query, column, n = 1)
@@ -1682,7 +1702,7 @@ 

Reference - Exported functions 1 โ”‚ AE bb 5 0.5 1 2 โ”‚ AJ aa 5 1.0 1

-

source

+

source

# TidierDB.@slice_minMacro.

@slice_min(sql_query, column, n = 1)
@@ -1721,7 +1741,7 @@ 

Reference - Exported functions 1 โ”‚ AA bb 1 0.1 1 2 โ”‚ AF aa 1 0.6 1

-

source

+

source

# TidierDB.@slice_sampleMacro.

@slice_sample(sql_query, n)
@@ -1753,7 +1773,7 @@ 

Reference - Exported functions @collect end;

-

source

+

source

# TidierDB.@summariseMacro.

   @summarise(sql_query, exprs...)
@@ -1800,16 +1820,17 @@ 

Reference - Exported functions 1 โ”‚ aa 3.0 5 2 โ”‚ bb 2.5 5

-

source

+

source

# TidierDB.@summarizeMacro.

-
   @summarize(sql_query, exprs...)
+
   @summarize(sql_query, exprs...; _by)
 

Aggregate and summarize specified columns of a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions defining the aggregation and summarization operations. These can specify simple aggregations like mean, sum, and count, or more complex expressions involving existing column values.
  • +
  • _by: optional argument that supports single column names, or vectors of columns to allow for grouping for the aggregatation in the macro call

Examples

-

source

+

source

# TidierDB.@unionMacro.

@union(sql_query1, sql_query2)
@@ -1916,7 +1949,7 @@ 

Reference - Exported functions 2 โ”‚ 2 20 3 โ”‚ 3 30

-

source

+

source

# TidierDB.@window_frameMacro.

@window_frame(sql_query, args...)
@@ -1976,7 +2009,7 @@ 

Reference - Exported functions #@show_query end;

-

source

+

source

# TidierDB.@window_orderMacro.

   @window_order(sql_query, columns...)
@@ -2005,7 +2038,7 @@ 

Reference - Exported functions #@show_query end;

-

source

+

source

Reference - Internal functionsยค

@@ -2044,7 +2077,7 @@

Reference - Internal functions 5 โ”‚ 2 20 6 โ”‚ 3 30

-

source

+

source

@@ -2121,7 +2154,7 @@

Reference - Internal functions - + @@ -2132,7 +2165,7 @@

Reference - Internal functions - +

diff --git a/latest/search/search_index.json b/latest/search/search_index.json index 8e6744b..f7c620c 100644 --- a/latest/search/search_index.json +++ b/latest/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#what-is-tidierdbjl","title":"What is TidierDB.jl?","text":"

TiderDB.jl is a 100% Julia implementation of the dbplyr R package, and similar to Python's ibis package.

The main goal of TidierDB.jl is to bring the syntax of Tidier.jl to multiple SQL backends, making it possible to analyze data directly on databases without needing to copy the entire database into memory.

"},{"location":"#currently-supported-backends-include","title":"Currently supported backends include:","text":"DuckDB (default) duckdb() ClickHouse clickhouse() SQLite sqlite() Postgres postgres() MySQL mysql() MariaDB mysql() MSSQL mssql() Athena athena() Snowflake snowflake() Databricks databricks() Google Big Query gbq() Oracle oracle()

Change the backend using set_sql_mode() - for example - set_sql_mode(databricks())

"},{"location":"#installation","title":"Installation","text":"

For the stable version:

] add TidierDB\n

TidierDB.jl currently supports:

Category Supported Macros and Functions Data Manipulation @arrange, @group_by, @filter, @select, @mutate (supports across), @summarize/@summarise (supports across), @distinct Joining @left_join, @right_join, @inner_join, @anti_join, @full_join, @semi_join, @union, @union_all Slice and Order @slice_min, @slice_max, @slice_sample, @order, @window_order, @window_frame Utility @show_query, @collect, @head, @count, show_tables, @create_view , drop_view Helper Functions across, desc, if_else, case_when, n, starts_with, ends_with, contains, as_float, as_integer, as_string, is_missing, missing_if, replace_missing TidierStrings.jl Functions str_detect, str_replace, str_replace_all, str_remove_all, str_remove TidierDates.jl Functions year, month, day, hour, min, second, floor_date, difftime, mdy, ymd, dmy Aggregate Functions mean, minimum, maximum, std, sum, cumsum, cor, cov, var, all aggregate sql fxns

@summarize supports any SQL aggregate function in addition to the list above. Simply write the function as written in SQL syntax and it will work. @mutate supports all builtin SQL functions as well.

When using the DuckDB backend, if db_table recieves a file path ( .parquet, .json, .csv, iceberg or delta), it does not copy it into memory. This allows for queries on files too big for memory. db_table also supports S3 bucket locations via DuckDB.

"},{"location":"#what-is-the-recommended-way-to-use-tidierdb","title":"What is the recommended way to use TidierDB?","text":"

Typically, you will want to use TidierDB alongside TidierData because there are certain functionality (such as pivoting) which are only supported in TidierData and can only be performed on data frames.

Our recommended path for using TidierDB is to import the package so that there are no namespace conflicts with TidierData. Once TidierDB is integrated with Tidier, then Tidier will automatically load the packages in this fashion.

First, let's develop and execute a query using TidierDB. Notice that all top-level macros and functions originating from TidierDB start with a DB prefix. Any functions defined within macros do not need to be prefixed within DB because they are actually pseudofunctions that are in actuality converted into SQL code.

Even though the code reads similarly to TidierData, note that no computational work actually occurs until you run DB.@collect(), which runs the SQL query and instantiates the result as a DataFrame.

using TidierData\nimport TidierDB as DB\n\ndb = DB.connect(DB.duckdb());\npath_or_name = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\n\n@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@collect\nend\n
2\u00d75 DataFrame\n Row \u2502 cyl     mpg       mpg_squared  mpg_rounded  mpg_efficiency \n     \u2502 Int64?  Float64?  Float64?     Float64?     String?        \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4   27.3444      747.719         27.0  efficient\n   2 \u2502      6   19.7333      389.404         20.0  moderate\n

"},{"location":"#what-if-we-wanted-to-pivot-the-result","title":"What if we wanted to pivot the result?","text":"

We cannot do this using TidierDB. However, we can call @pivot_longer() from TidierData after the result of the query has been instantiated as a DataFrame, like this:

@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@collect\n    @pivot_longer(everything(), names_to = \"variable\", values_to = \"value\")\nend\n
10\u00d72 DataFrame\n Row \u2502 variable        value     \n     \u2502 String          Any       \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 cyl             4\n   2 \u2502 cyl             6\n   3 \u2502 mpg             27.3444\n   4 \u2502 mpg             19.7333\n   5 \u2502 mpg_squared     747.719\n   6 \u2502 mpg_squared     389.404\n   7 \u2502 mpg_rounded     27.0\n   8 \u2502 mpg_rounded     20.0\n   9 \u2502 mpg_efficiency  efficient\n  10 \u2502 mpg_efficiency  moderate\n

"},{"location":"#what-sql-query-does-tidierdb-generate-for-a-given-piece-of-julia-code","title":"What SQL query does TidierDB generate for a given piece of Julia code?","text":"

We can replace DB.collect() with DB.@show_query to reveal the underlying SQL query being generated by TidierDB. To handle complex queries, TidierDB makes heavy use of Common Table Expressions (CTE), which are a useful tool to organize long queries.

@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@show_query\nend\n
WITH cte_1 AS (\nSELECT *\n        FROM mtcars\n        WHERE NOT (starts_with(model, 'M'))),\ncte_2 AS (\nSELECT cyl, AVG(mpg) AS mpg\n        FROM cte_1\n        GROUP BY cyl),\ncte_3 AS (\nSELECT  cyl, mpg, POWER(mpg, 2) AS mpg_squared, ROUND(mpg) AS mpg_rounded, CASE WHEN mpg >= POWER(cyl, 2) THEN 'efficient' WHEN mpg < 15.2 THEN 'inefficient' ELSE 'moderate' END AS mpg_efficiency\n        FROM cte_2 ),\ncte_4 AS (\nSELECT *\n        FROM cte_3\n        WHERE mpg_efficiency in ('moderate', 'efficient'))  \nSELECT *\n        FROM cte_4  \n        ORDER BY mpg_rounded DESC\n

"},{"location":"#tidierdb-is-already-quite-fully-featured-supporting-advanced-tidierdata-functions-like-across-for-multi-column-selection","title":"TidierDB is already quite fully-featured, supporting advanced TidierData functions like across() for multi-column selection.","text":"
@chain DB.db_table(db, path_or_name) begin\n    DB.@group_by(cyl)\n    DB.@summarize(across((starts_with(\"a\"), ends_with(\"s\")), (mean, sum)))\n    DB.@collect\nend\n
3\u00d75 DataFrame\n Row \u2502 cyl     am_mean   vs_mean   am_sum   vs_sum  \n     \u2502 Int64?  Float64?  Float64?  Int128?  Int128? \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4  0.727273  0.909091        8       10\n   2 \u2502      6  0.428571  0.571429        3        4\n   3 \u2502      8  0.142857  0.0             2        0\n

Bang bang !! interpolation for columns and values is also supported.

There are a few subtle but important differences from Tidier.jl outlined here.

"},{"location":"#missing-a-function-or-backend","title":"Missing a function or backend?","text":"

You can use any existing SQL function within @mutate with the correct SQL syntax and it should just work.

But if you run into problems please open an issue, and we will be happy to take a look!

"},{"location":"reference/","title":"Reference","text":""},{"location":"reference/#index","title":"Index","text":"
  • TidierDB.connect
  • TidierDB.copy_to
  • TidierDB.db_table
  • TidierDB.show_tables
  • TidierDB.warnings
  • TidierDB.@anti_join
  • TidierDB.@arrange
  • TidierDB.@collect
  • TidierDB.@count
  • TidierDB.@create_view
  • TidierDB.@distinct
  • TidierDB.@filter
  • TidierDB.@full_join
  • TidierDB.@group_by
  • TidierDB.@head
  • TidierDB.@inner_join
  • TidierDB.@left_join
  • TidierDB.@mutate
  • TidierDB.@rename
  • TidierDB.@right_join
  • TidierDB.@select
  • TidierDB.@semi_join
  • TidierDB.@slice_max
  • TidierDB.@slice_min
  • TidierDB.@slice_sample
  • TidierDB.@summarise
  • TidierDB.@summarize
  • TidierDB.@union
  • TidierDB.@union_all
  • TidierDB.@window_frame
  • TidierDB.@window_order
"},{"location":"reference/#reference-exported-functions","title":"Reference - Exported functions","text":"

# TidierDB.connect \u2014 Method.

connect(backend; kwargs...)\n

This function establishes a database connection based on the specified backend and connection parameters and sets the SQL mode

Arguments

  • backend: type specifying the database backend to connect to. Supported backends are:

    • duckdb(), sqlite()(SQLite), mssql(), mysql()(for MariaDB and MySQL), clickhouse(), postgres()
    • kwargs: Keyword arguments specifying the connection parameters for the selected backend. The required parameters vary depending on the backend:

    • MySQL:

      • host: The host name or IP address of the MySQL server. Default is \"localhost\".
      • user: The username for authentication. Default is an empty string.
      • password: The password for authentication.
      • db: The name of the database to connect to (optional).
      • port: The port number of the MySQL server (optional).

Returns

  • A database connection object based on the selected backend.

Examples

# Connect to MySQL\n# conn = connect(mysql(); host=\"localhost\", user=\"root\", password=\"password\", db=\"mydb\")\n# Connect to PostgreSQL using LibPQ\n# conn = connect(postgres(); host=\"localhost\", dbname=\"mydb\", user=\"postgres\", password=\"password\")\n# Connect to ClickHouse\n# conn = connect(clickhouse(); host=\"localhost\", port=9000, database=\"mydb\", user=\"default\", password=\"\")\n# Connect to SQLite\n# conn = connect(sqlite())\n# Connect to Google Big Query\n# conn = connect(gbq(), \"json_user_key_path\", \"location\")\n# Connect to Snowflake\n# conn = connect(snowflake(), \"ac_id\", \"token\", \"Database_name\", \"Schema_name\", \"warehouse_name\")\n# Connect to Microsoft SQL Server\n# conn = connect(mssql(), \"DRIVER={ODBC Driver 18 for SQL Server};SERVER=host,1433;UID=sa;PWD=YourPassword;Encrypt=no;TrustServerCertificate=yes\")\n# Connect to DuckDB\n# connect to Google Cloud via DuckDB\n# google_db = connect(duckdb(), :gbq, access_key=\"string\", secret_key=\"string\")\n# Connect to AWS via DuckDB\n# aws_db = connect2(duckdb(), :aws, aws_access_key_id=get(ENV, \"AWS_ACCESS_KEY_ID\", \"access_key\"), aws_secret_access_key=get(ENV, \"AWS_SECRET_ACCESS_KEY\", \"secret_access key\"), aws_region=get(ENV, \"AWS_DEFAULT_REGION\", \"us-east-1\"))\n# Connect to MotherDuck\n# connect(duckdb(), \"\"md://...\"\") for first connection, vs connect(duckdb(), \"md:\") for reconnection\n# Connect to exisiting database file\n# connect(duckdb(), \"path/to/database.duckdb\")\n# Open an in-memory database\njulia> db = connect(duckdb())\nDuckDB.Connection(\":memory:\")\n

source

# TidierDB.copy_to \u2014 Method.

   copy_to(conn, df_or_path, \"name\")\n

Allows user to copy a df to the database connection. Currently supports DuckDB, SQLite, MySql

Arguments

-conn: the database connection -df: dataframe to be copied or path to serve as source. With DuckDB, path supports .csv, .json, .parquet to be used without copying intermediary df. -name: name as string for the database to be used

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"test\");\n

source

# TidierDB.db_table \u2014 Function.

db_table(database, table_name, athena_params, delta = false, iceberg = false)\n

db_table starts the underlying SQL query struct, adding the metadata and table. If paths are passed directly to db*table instead of a name it will not copy it to memory, but rather ready directly from the file. db*tableonly supports direct file paths to a table. It does not support database file paths such asdbname.duckdbordbname.sqlite. Such files must be used withconnect first

Arguments

  • database: The Database or connection object
  • table_name: tablename as a string (name, local path, or URL). - CSV/TSV - Parquet - Json - Iceberg - Delta - S3 tables from AWS or Google Cloud

    • DuckDB and ClickHouse support vectors of paths and URLs.
    • DuckDB and ClickHouse also support use of * wildcards to read all files of a type in a location such as:
    • db_table(db, \"Path/to/testing_files/*.parquet\")
    • delta: must be true to read delta files
    • iceberg: must be true to read iceberg finalize_ctes

Example

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> db_table(db, \"df_mem\")\nTidierDB.SQLQuery(\"\", \"df_mem\", \"\", \"\", \"\", \"\", \"\", \"\", false, false, 4\u00d74 DataFrame\n Row \u2502 name     type     current_selxn  table_name \n     \u2502 String?  String?  Int64          String     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 id       VARCHAR              1  df_mem\n   2 \u2502 groups   VARCHAR              1  df_mem\n   3 \u2502 value    BIGINT               1  df_mem\n   4 \u2502 percent  DOUBLE               1  df_mem, false, DuckDB.Connection(\":memory:\"), TidierDB.CTE[], 0, nothing)\n

source

# TidierDB.show_tables \u2014 Method.

show_tables(con; GBQ_datasetname)\n

Shows tables available in database. currently supports DuckDB, databricks, Snowflake, GBQ, SQLite, LibPQ

Arguments

  • con : connection to backend
  • GBQ_datasetname : string of dataset name

Examples

julia> db = connect(duckdb());\n\njulia> show_tables(db);\n

source

# TidierDB.warnings \u2014 Method.

warnings(show::Bool)\n

Sets the global warning flag to the specified boolean value.

Arguments

  • flag::Bool: A boolean value to set the warning flag. If true, warnings will be enabled; if false, warnings will be disabled.

Default Behavior

By default, the warning flag is set to false, meaning that warnings are disabled unless explicitly enabled by setting this function with true.

Example

julia> warnings(true);\n

source

# TidierDB.@anti_join \u2014 Macro.

@anti_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an anti join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n        @anti_join(\"df_join\", id = id2)\n        @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AB      aa          2      0.2\n   2 \u2502 AD      aa          4      0.4\n   3 \u2502 AF      aa          1      0.6\n   4 \u2502 AH      aa          3      0.8\n   5 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@arrange \u2014 Macro.

@arrange(sql_query, columns...)\n

Order SQL table rows based on specified column(s).

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to order the rows by. Can include multiple columns for nested sorting. Wrap column name with desc() for descending order.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @arrange(value, desc(percent))\n         @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AF      aa          1      0.6\n   2 \u2502 AA      bb          1      0.1\n   3 \u2502 AG      bb          2      0.7\n   4 \u2502 AB      aa          2      0.2\n   5 \u2502 AH      aa          3      0.8\n   6 \u2502 AC      bb          3      0.3\n   7 \u2502 AI      bb          4      0.9\n   8 \u2502 AD      aa          4      0.4\n   9 \u2502 AJ      aa          5      1.0\n  10 \u2502 AE      bb          5      0.5\n

source

# TidierDB.@collect \u2014 Macro.

@collect(sql_query, stream = false)\n

db_table starts the underlying SQL query struct, adding the metadata and table.

Arguments

  • sql_query: The SQL query to operate on.
  • stream: optional streaming for query/execution of results when using duck db. Defaults to false

Example

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @collect db_table(db, \"df_mem\")\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AB      aa          2      0.2\n   3 \u2502 AC      bb          3      0.3\n   4 \u2502 AD      aa          4      0.4\n   5 \u2502 AE      bb          5      0.5\n   6 \u2502 AF      aa          1      0.6\n   7 \u2502 AG      bb          2      0.7\n   8 \u2502 AH      aa          3      0.8\n   9 \u2502 AI      bb          4      0.9\n  10 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@count \u2014 Macro.

@count(sql_query, columns...)\n

Count the number of rows grouped by specified column(s).

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to group by before counting. If no columns are specified, counts all rows in the query.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @count(groups)\n         @arrange(groups)\n         @collect\n       end\n2\u00d72 DataFrame\n Row \u2502 groups  count \n     \u2502 String  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          5\n   2 \u2502 bb          5\n

source

# TidierDB.@create_view \u2014 Macro.

@view(sql_query, name, replace = true)\n

Create a view from a SQL query. Currently supports DuckDB, MySQL, GBQ, Postgres

Arguments

  • sql_query: The SQL query to create a view from.
  • name: The name of the view to create.
  • replace: defaults to true if view should be replaced

Examples

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> copy_to(db, df, \"df1\");\n\njulia> @chain db_table(db, \"df1\") @create_view(viewer);\n\njulia> db_table(db, \"viewer\")\nTidierDB.SQLQuery(\"\", \"viewer\", \"\", \"\", \"\", \"\", \"\", \"\", false, false, 2\u00d74 DataFrame\n Row \u2502 name    type    current_selxn  table_name \n     \u2502 String  String  Int64          String     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 id      BIGINT              1  viewer\n   2 \u2502 value   BIGINT              1  viewer, false, DuckDB.DB(\":memory:\"), TidierDB.CTE[], 0, nothing, \"\", \"\", 0)\n

source

# TidierDB.@distinct \u2014 Macro.

@distinct(sql_query, columns...)\n

Select distinct rows based on specified column(s). Distinct works differently in TidierData vs SQL and therefore TidierDB. Distinct will also select only the only columns it is given (or all if given none)

Arguments

sql_query: The SQL query to operate on. columns: Columns to determine uniqueness. If no columns are specified, all columns are used to identify distinct rows.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @distinct(value)\n         @arrange(value)\n         @collect\n       end\n5\u00d71 DataFrame\n Row \u2502 value \n     \u2502 Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1\n   2 \u2502     2\n   3 \u2502     3\n   4 \u2502     4\n   5 \u2502     5\n\njulia> @chain db_table(db, :df_mem) begin\n         @distinct\n         @arrange(id)\n         @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AB      aa          2      0.2\n   3 \u2502 AC      bb          3      0.3\n   4 \u2502 AD      aa          4      0.4\n   5 \u2502 AE      bb          5      0.5\n   6 \u2502 AF      aa          1      0.6\n   7 \u2502 AG      bb          2      0.7\n   8 \u2502 AH      aa          3      0.8\n   9 \u2502 AI      bb          4      0.9\n  10 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@filter \u2014 Macro.

@filter(sql_query, conditions...)\n

Filter rows in a SQL table based on specified conditions.

Arguments

  • sql_query: The SQL query to filter rows from.
  • conditions: Expressions specifying the conditions that rows must satisfy to be included in the output. Rows for which the expression evaluates to true will be included in the result. Multiple conditions can be combined using logical operators (&&, ||). It will automatically detect whether the conditions belong in WHERE vs HAVING.

                 Temporarily, it is best to use begin and end when filtering multiple conditions. (ex 2 below)\n

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @filter(percent > .5)\n         @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AF      aa          1      0.6\n   2 \u2502 AG      bb          2      0.7\n   3 \u2502 AH      aa          3      0.8\n   4 \u2502 AI      bb          4      0.9\n   5 \u2502 AJ      aa          5      1.0\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(mean = mean(percent))\n         @filter begin \n           groups == \"bb\" || # logical operators can still be used like this\n           mean > .5\n         end\n         @arrange(groups)\n         @collect\n       end\n2\u00d72 DataFrame\n Row \u2502 groups  mean    \n     \u2502 String  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          0.6\n   2 \u2502 bb          0.5\n

source

# TidierDB.@full_join \u2014 Macro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an full join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @full_join((@chain db_table(db, \"df_join\") @filter(score > 70)), id)\n         #@aside @show_query _\n         @collect\n       end\n11\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id_1     category  score   \n     \u2502 String?  String?  Int64?   Float64?   String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA       X              88\n   2 \u2502 AC       bb             3        0.3  AC       Y              92\n   3 \u2502 AE       bb             5        0.5  AE       X              77\n   4 \u2502 AG       bb             2        0.7  AG       Y              83\n   5 \u2502 AI       bb             4        0.9  AI       X              95\n   6 \u2502 AB       aa             2        0.2  missing  missing   missing \n   7 \u2502 AD       aa             4        0.4  missing  missing   missing \n   8 \u2502 AF       aa             1        0.6  missing  missing   missing \n   9 \u2502 AH       aa             3        0.8  missing  missing   missing \n  10 \u2502 AJ       aa             5        1.0  missing  missing   missing \n  11 \u2502 missing  missing  missing  missing    AM       X              74\n

source

# TidierDB.@group_by \u2014 Macro.

@group_by(sql_query, columns...)\n

Group SQL table rows by specified column(s). If grouping is performed as a terminal operation without a subsequent mutatation or summarization (as in the example below), then the resulting data frame will be ungrouped when @collect is applied.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions specifying the columns to group by. Columns can be specified by name.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @arrange(groups)\n         @collect\n       end\n2\u00d71 DataFrame\n Row \u2502 groups \n     \u2502 String \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa\n   2 \u2502 bb\n

source

# TidierDB.@head \u2014 Macro.

@head(sql_query, value)\n

Limit SQL table number of rows returned based on specified value. LIMIT in SQL

Arguments

  • sql_query: The SQL query to operate on.
  • value: Number to limit how many rows are returned. If left empty, it will default to 6 rows

Examples

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> copy_to(db, df, \"df_mem\");                     \n\njulia> @chain db_table(db, :df_mem) begin\n        @head(1) ## supports expressions ie `3-2` would return the same df below\n        @collect\n       end\n1\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n

source

# TidierDB.@inner_join \u2014 Macro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an inner join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @inner_join(\"df_join\", \"id\" = id2)\n         @collect\n       end\n5\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2     category  score \n     \u2502 String  String  Int64  Float64  String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA      X            88\n   2 \u2502 AC      bb          3      0.3  AC      Y            92\n   3 \u2502 AE      bb          5      0.5  AE      X            77\n   4 \u2502 AG      bb          2      0.7  AG      Y            83\n   5 \u2502 AI      bb          4      0.9  AI      X            95\n

source

# TidierDB.@left_join \u2014 Macro.

@left_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform a left join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, \"df_mem\") begin\n         @left_join(\"df_join\", \"id\" = \"id2\" )\n         @collect\n       end\n10\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2      category  score   \n     \u2502 String  String  Int64  Float64  String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA       X              88\n   2 \u2502 AC      bb          3      0.3  AC       Y              92\n   3 \u2502 AE      bb          5      0.5  AE       X              77\n   4 \u2502 AG      bb          2      0.7  AG       Y              83\n   5 \u2502 AI      bb          4      0.9  AI       X              95\n   6 \u2502 AB      aa          2      0.2  missing  missing   missing \n   7 \u2502 AD      aa          4      0.4  missing  missing   missing \n   8 \u2502 AF      aa          1      0.6  missing  missing   missing \n   9 \u2502 AH      aa          3      0.8  missing  missing   missing \n  10 \u2502 AJ      aa          5      1.0  missing  missing   missing \n\njulia> query = @chain db_table(db, \"df_join\") begin\n                  @filter(score > 85) # only show scores above 85 in joining table\n                end;\n\njulia> @chain db_table(db, \"df_mem\") begin\n         @left_join(t(query), id = id2)\n         @collect\n       end\n10\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2      category  score   \n     \u2502 String  String  Int64  Float64  String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA       X              88\n   2 \u2502 AC      bb          3      0.3  AC       Y              92\n   3 \u2502 AI      bb          4      0.9  AI       X              95\n   4 \u2502 AB      aa          2      0.2  missing  missing   missing \n   5 \u2502 AD      aa          4      0.4  missing  missing   missing \n   6 \u2502 AE      bb          5      0.5  missing  missing   missing \n   7 \u2502 AF      aa          1      0.6  missing  missing   missing \n   8 \u2502 AG      bb          2      0.7  missing  missing   missing \n   9 \u2502 AH      aa          3      0.8  missing  missing   missing \n  10 \u2502 AJ      aa          5      1.0  missing  missing   missing \n

source

# TidierDB.@mutate \u2014 Macro.

@mutate(sql_query, exprs...)\n

Mutate SQL table rows by adding new columns or modifying existing ones.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions for mutating the table. New columns can be added or existing columns modified using column_name = expression syntax, where expression can involve existing columns.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @mutate(value = value * 4, new_col = percent^2)\n         @collect\n       end\n10\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  new_col \n     \u2502 String  String  Int64  Float64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          4      0.1     0.01\n   2 \u2502 AB      aa          8      0.2     0.04\n   3 \u2502 AC      bb         12      0.3     0.09\n   4 \u2502 AD      aa         16      0.4     0.16\n   5 \u2502 AE      bb         20      0.5     0.25\n   6 \u2502 AF      aa          4      0.6     0.36\n   7 \u2502 AG      bb          8      0.7     0.49\n   8 \u2502 AH      aa         12      0.8     0.64\n   9 \u2502 AI      bb         16      0.9     0.81\n  10 \u2502 AJ      aa         20      1.0     1.0\n

source

# TidierDB.@rename \u2014 Macro.

@rename(sql_query, renamings...)\n

Rename one or more columns in a SQL query.

Arguments

-sql_query: The SQL query to operate on. -renamings: One or more pairs of old and new column names, specified as new name = old name

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n       @rename(new_name = percent)\n       @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  new_name \n     \u2502 String  String  Int64  Float64  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1       0.1\n   2 \u2502 AB      aa          2       0.2\n   3 \u2502 AC      bb          3       0.3\n   4 \u2502 AD      aa          4       0.4\n   5 \u2502 AE      bb          5       0.5\n   6 \u2502 AF      aa          1       0.6\n   7 \u2502 AG      bb          2       0.7\n   8 \u2502 AH      aa          3       0.8\n   9 \u2502 AI      bb          4       0.9\n  10 \u2502 AJ      aa          5       1.0\n

source

# TidierDB.@right_join \u2014 Macro.

@right_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform a right join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @right_join(\"df_join\", id = id2)\n         @collect\n       end\n7\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id2     category  score \n     \u2502 String?  String?  Int64?   Float64?   String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA      X            88\n   2 \u2502 AC       bb             3        0.3  AC      Y            92\n   3 \u2502 AE       bb             5        0.5  AE      X            77\n   4 \u2502 AG       bb             2        0.7  AG      Y            83\n   5 \u2502 AI       bb             4        0.9  AI      X            95\n   6 \u2502 missing  missing  missing  missing    AK      Y            68\n   7 \u2502 missing  missing  missing  missing    AM      X            74\n\njulia> query = @chain db_table(db, \"df_join\") begin\n                  @filter(score >= 74) # only show scores above 85 in joining table\n                end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @right_join(t(query), id = id2)\n         @collect\n       end\n6\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id2     category  score \n     \u2502 String?  String?  Int64?   Float64?   String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA      X            88\n   2 \u2502 AC       bb             3        0.3  AC      Y            92\n   3 \u2502 AE       bb             5        0.5  AE      X            77\n   4 \u2502 AG       bb             2        0.7  AG      Y            83\n   5 \u2502 AI       bb             4        0.9  AI      X            95\n   6 \u2502 missing  missing  missing  missing    AM      X            74\n

source

# TidierDB.@select \u2014 Macro.

@select(sql_query, columns)\n

Select specified columns from a SQL table.

Arguments

  • sql_query: The SQL query to select columns from.
  • columns: Expressions specifying the columns to select. Columns can be specified by name, and new columns can be created with expressions using existing column values.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> df_mem = db_table(db, :df_mem);\n\njulia> @chain t(df_mem) begin\n         @select(groups:percent)\n         @collect\n       end\n10\u00d73 DataFrame\n Row \u2502 groups  value  percent \n     \u2502 String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 bb          1      0.1\n   2 \u2502 aa          2      0.2\n   3 \u2502 bb          3      0.3\n   4 \u2502 aa          4      0.4\n   5 \u2502 bb          5      0.5\n   6 \u2502 aa          1      0.6\n   7 \u2502 bb          2      0.7\n   8 \u2502 aa          3      0.8\n   9 \u2502 bb          4      0.9\n  10 \u2502 aa          5      1.0\n\njulia> @chain t(df_mem) begin\n         @select(contains(\"e\"))\n         @collect\n       end\n10\u00d72 DataFrame\n Row \u2502 value  percent \n     \u2502 Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1      0.1\n   2 \u2502     2      0.2\n   3 \u2502     3      0.3\n   4 \u2502     4      0.4\n   5 \u2502     5      0.5\n   6 \u2502     1      0.6\n   7 \u2502     2      0.7\n   8 \u2502     3      0.8\n   9 \u2502     4      0.9\n  10 \u2502     5      1.0\n

source

# TidierDB.@semi_join \u2014 Macro.

@semi_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an semi join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @semi_join(\"df_join\", id = id2)\n         @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AC      bb          3      0.3\n   3 \u2502 AE      bb          5      0.5\n   4 \u2502 AG      bb          2      0.7\n   5 \u2502 AI      bb          4      0.9\n

source

# TidierDB.@slice_max \u2014 Macro.

@slice_max(sql_query, column, n = 1)\n

Select rows with the largest values in specified column. This will always return ties.

Arguments

  • sql_query: The SQL query to operate on.
  • column: Column to identify the smallest values.
  • n: The number of rows to select with the largest values for each specified column. Default is 1, which selects the row with the smallest value.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_max(value, n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @slice_max(value)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  rank_col \n     \u2502 String  String  Int64  Float64  Int64    \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AE      bb          5      0.5         1\n   2 \u2502 AJ      aa          5      1.0         1\n

source

# TidierDB.@slice_min \u2014 Macro.

@slice_min(sql_query, column, n = 1)\n

Select rows with the smallest values in specified column. This will always return ties.

Arguments

  • sql_query: The SQL query to operate on.
  • column: Column to identify the smallest values.
  • n: The number of rows to select with the smallest values for each specified column. Default is 1, which selects the row with the smallest value.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_min(value, n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @slice_min(value)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  rank_col \n     \u2502 String  String  Int64  Float64  Int64    \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1         1\n   2 \u2502 AF      aa          1      0.6         1\n

source

# TidierDB.@slice_sample \u2014 Macro.

@slice_sample(sql_query, n)\n

Randomly select a specified number of rows from a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • n: The number of rows to randomly select.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_sample(n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n       @slice_sample()\n       @collect\n       end;\n

source

# TidierDB.@summarise \u2014 Macro.

   @summarise(sql_query, exprs...)\n

Aggregate and summarize specified columns of a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions defining the aggregation and summarization operations. These can specify simple aggregations like mean, sum, and count, or more complex expressions involving existing column values.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(across((value:percent), (mean, sum)))\n         @arrange(groups)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 groups  value_mean  percent_mean  value_sum  percent_sum \n     \u2502 String  Float64     Float64       Int128     Float64     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa             3.0           0.6         15          3.0\n   2 \u2502 bb             3.0           0.5         15          2.5\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(test = sum(percent), n = n())\n         @arrange(groups)\n         @collect\n       end\n2\u00d73 DataFrame\n Row \u2502 groups  test     n     \n     \u2502 String  Float64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          3.0      5\n   2 \u2502 bb          2.5      5\n

source

# TidierDB.@summarize \u2014 Macro.

   @summarize(sql_query, exprs...)\n

Aggregate and summarize specified columns of a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions defining the aggregation and summarization operations. These can specify simple aggregations like mean, sum, and count, or more complex expressions involving existing column values.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(across((ends_with(\"e\"), starts_with(\"p\")), (mean, sum)))\n         @arrange(groups)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 groups  value_mean  percent_mean  value_sum  percent_sum \n     \u2502 String  Float64     Float64       Int128     Float64     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa             3.0           0.6         15          3.0\n   2 \u2502 bb             3.0           0.5         15          2.5\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(test = sum(percent), n = n())\n         @arrange(groups)\n         @collect\n       end\n2\u00d73 DataFrame\n Row \u2502 groups  test     n     \n     \u2502 String  Float64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          3.0      5\n   2 \u2502 bb          2.5      5\n

source

# TidierDB.@union \u2014 Macro.

@union(sql_query1, sql_query2)\n

Combine two SQL queries using the UNION operator.

Arguments

  • sql_query1: The first SQL query to combine.
  • sql_query2: The second SQL query to combine.

Returns

  • A lazy query of all distinct rows in the second query bound to the first

Examples

julia> db = connect(duckdb());\n\njulia> df1 = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> df2 = DataFrame(id = [4, 5, 6], value = [40, 50, 60]);\n\njulia> copy_to(db, df1, \"df1\");\n\njulia> copy_to(db, df2, \"df2\");\n\njulia> df1_table = db_table(db, \"df1\");\n\njulia> df2_table = db_table(db, \"df2\");\n\njulia> @chain t(df1_table) @union(df2_table) @collect\n6\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     4     40\n   5 \u2502     5     50\n   6 \u2502     6     60\n\njulia> query = @chain t(df2_table) @filter(value == 50);\n\njulia> @chain t(df1_table) begin \n        @union(t(query))\n        @collect\n       end\n4\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     5     50\n\njulia> @chain t(df1_table) begin \n        @union(t(df1_table))\n        @collect\n       end\n3\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n

source

# TidierDB.@window_frame \u2014 Macro.

@window_frame(sql_query, args...)\n

Define the window frame for window functions in a SQL query, specifying the range of rows to include in the calculation relative to the current row.

Arguments

  • sqlquery::SQLQuery: The SQLQuery instance to which the window frame will be applied.
  • args...: A variable number of arguments specifying the frame boundaries. These can be:

    • from: The starting point of the frame. Can be a positive or negative integer, 0 or empty. When empty, it will use UNBOUNDED
    • to: The ending point of the frame. Can be a positive or negative integer, 0 or empty. When empty, it will use UNBOUNDED
    • if only one integer is provided without specifying to or from it will default to from, and to will be UNBOUNDED.
    • if no arguments are given, both will be UNBOUNDED

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> df_mem = db_table(db, :df_mem);\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame(3)\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame(-3, 3)\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n       # @window_frame(to = -3)\n        @mutate(avg = mean(percent))\n        #@show_query\n        @collect\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame()\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n

source

# TidierDB.@window_order \u2014 Macro.

   @window_order(sql_query, columns...)\n

Specify the order of rows for window functions within a SQL query.

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to order the rows by for the window function. Can include multiple columns for nested sorting. Prepend a column name with - for descending order.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n        @group_by groups\n        @window_frame(3)\n        @window_order(desc(percent))\n        @mutate(avg = mean(value))\n       #@show_query \n       end;\n

source

"},{"location":"reference/#reference-internal-functions","title":"Reference - Internal functions","text":"

# TidierDB.@union_all \u2014 Macro.

@union(sql_query1, sql_query2)\n

Combine two SQL queries using the UNION ALL operator.

Arguments

  • sql_query1: The first SQL query to combine.
  • sql_query2: The second SQL query to combine.

Returns

  • A lazy query of all rows in the second query bound to the first

Examples

julia> db = connect(duckdb());\n\njulia> df1 = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> copy_to(db, df1, \"df1\");\n\njulia> df1_table = db_table(db, \"df1\");\n\njulia> @chain t(df1_table) @union_all(df1_table) @collect\n6\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     1     10\n   5 \u2502     2     20\n   6 \u2502     3     30\n

source

"},{"location":"examples/generated/UserGuide/Snowflake/","title":"Using Snowflake","text":"

Establishing a connection with the Snowflake SQL Rest API requires a OAuth token specific to the Role the user will use to query tables with.

"},{"location":"examples/generated/UserGuide/Snowflake/#connecting","title":"Connecting","text":"

Connection is established with the connect function as shown below. Connection requires 5 items as strings

  • Account Identifier
  • OAuth token
  • Database Name
  • Schema Name
  • Compute Warehouse name

Two things to note:

  • Your OAuth Token may frequently expire, which may require you to rerun your connection line.
  • Since each time db_table runs, it runs a query to pull the metadata, you may choose to use run db_table and save the results, and use these results withfrom_query()

    • This will reduce the number of queries to your database
    • Allow you to build a a SQL query and @show_query even if the OAuthtoken has expired. To @collect you will have to reconnect and rerun dbtable if your OAuth token has expired
set_sql_mode(snowflake())\nac_id = \"string_id\"\ntoken = \"OAuth_token_string\"\ncon = connect(:snowflake, ac_id, token, \"DEMODB\", \"PUBLIC\", \"COMPUTE_WH\")\n# After connection is established, a you may begin querying.\nstable_table_metadata = db_table(con, \"MTCARS\")\n@chain from_query(stable_table_metadata) begin\n   @select(WT)\n   @mutate(TEST = WT *2)\n   #@aside @show_query _\n   @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 WT       TEST\n     \u2502 Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502   2.62     5.24\n   2 \u2502   2.875    5.75\n   3 \u2502   2.32     4.64\n   4 \u2502   3.215    6.43\n  \u22ee  \u2502    \u22ee        \u22ee\n  29 \u2502   3.17     6.34\n  30 \u2502   2.77     5.54\n  31 \u2502   3.57     7.14\n  32 \u2502   2.78     5.56\n         24 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/athena/","title":"Using Athena","text":"

To use the Athena AWS backend with TidierDB, set up and a small syntax difference are covered here.

"},{"location":"examples/generated/UserGuide/athena/#connecting","title":"Connecting","text":"

Connection is established through AWS.jl as shwon below.

using TidierDB, AWS\nset_sql_mode(athena())\n# Replace your credentials as needed below\naws_access_key_id = get(ENV,\"AWS_ACCESS_KEY_ID\",\"key\")\naws_secret_access_key = get(ENV, \"AWS_SECRET_ACCESS_KEY\",\"secret_key\")\naws_region = get(ENV,\"AWS_DEFAULT_REGION\",\"region\")\n\nconst AWS_GLOBAL_CONFIG = Ref{AWS.AWSConfig}()\ncreds = AWSCredentials(aws_access_key_id, aws_secret_access_key)\n\nAWS_GLOBAL_CONFIG[] = AWS.global_aws_config(region=aws_region, creds=creds)\n\ncatalog = \"AwsDataCatalog\"\nworkgroup = \"primary\"\ndb = \"demodb\"\nall_results = true\nresults_per_increment = 10\nout_loc = \"s3://location/\"\n\nathena_params = Dict(\n    \"ResultConfiguration\" => Dict(\n        \"OutputLocation\" => out_loc\n    ),\n    \"QueryExecutionContext\" => Dict(\n        \"Database\" => db,\n        \"Catalog\" => catalog\n    ),\n    \"Workgroup\" => workgroup\n)\n

"},{"location":"examples/generated/UserGuide/athena/#db_table-differences","title":"db_table differences","text":"

There are two differences for db_table which are seen in the query below

  1. The table needs to be passed as a string in the format database.table, ie \"demodb.table_name
  2. db_table requires a third argument: the athena_params from above.

"},{"location":"examples/generated/UserGuide/athena/#leveraging-from_query-with-athena-to-reduce-number-of-queries","title":"Leveraging from_query with Athena to reduce number of queries","text":"

Throughout TidierDB, each time db_table is called, it queries the databases to get the metadata. Consider how AWS Athena logs queries, a user may want to reduce the number of queries. This can be done saving the results of db_table, and then using from_query with those results for furthe queries as shown below.

mtcars = db_table(AWS_GLOBAL_CONFIG[], \"demodb.mtcars\", athena_params)\n@chain from_query(mtcars) begin\n    @filter(cyl > 4)\n    @group_by(cyl)\n    @summarize(mpg = mean(mpg))\n   #@show_query\n    @collect\nend\n
2\u00d72 DataFrame\n Row \u2502 cyl    mpg\n     \u2502 Int64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     6  19.7429\n   2 \u2502     8  15.1\n

I would like to acknowledge the work of Manu Francis and this blog post, which helped guide this process

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/databricks/","title":"Using Databricks","text":"

Establishing a connection with the Databricks SQL Rest API requires a token.

"},{"location":"examples/generated/UserGuide/databricks/#connecting","title":"Connecting","text":"

Connection is established with the connect function as shown below. Connection requires 5 items as strings

  • Account Instance : how to find your instance
  • OAuth token : how to generate your token
  • Database Name
  • Schema Name
  • warehouse_id

One thing to note, Since each time db_table runs, it runs a query to pull the metadata, you may choose to use run db_table and save the results, and use these results with from_query(). This will reduce the number of queries to your database and is illustrated below.

set_sql_mode(databricks())\ninstance_id = \"string_id\"\ntoken \"string_token\"\nwarehouse_id = \"e673cd4f387f964a\"\ncon = connect(:databricks, instance_id, token, \"DEMODB\", \"PUBLIC\", warehouse_id)\n# After connection is established, a you may begin querying.\nstable_table_metadata = db_table(con, \"mtcars\")\n@chain from_query(stable_table_metadata) begin\n   @select(wt)\n   @mutate(test = wt *2)\n   #@aside @show_query _\n   @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 wt       test\n     \u2502 Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502   2.62     5.24\n   2 \u2502   2.875    5.75\n   3 \u2502   2.32     4.64\n   4 \u2502   3.215    6.43\n  \u22ee  \u2502    \u22ee        \u22ee\n  29 \u2502   3.17     6.34\n  30 \u2502   2.77     5.54\n  31 \u2502   3.57     7.14\n  32 \u2502   2.78     5.56\n         24 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/","title":"Reproduce a duckplyr example","text":"

In this example, we will reproduce a DuckDB and duckplyr blog post example to demonstrate TidierDB's v0.5.0 capability.

The example by Hannes that is being reproduced is exploring Open Data from the New Zealand government that is ~ 1GB.

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#set-up","title":"Set up","text":"

First we will set up the local duckdb database and pull in the metadata for the files. Notice we are not reading this data into memory, only the paths and and column, and table names. To follow along, copy the set up code below after downloading the data, but add the directory to the local data.

import TidierDB as DB\ndb = DB.connect(DB.duckdb())\n\ndir = \"/Downloads/nzcensus/\"\ndata   = dir * \"Data8277.csv\"\nage    = dir * \"DimenLookupAge8277.csv\"\narea   = dir * \"DimenLookupArea8277.csv\"\nethnic = dir * \"DimenLookupEthnic8277.csv\"\nsex    = dir * \"DimenLookupSex8277.csv\"\nyear   = dir * \"DimenLookupYear8277.csv\"\n\ndata = DB.db_table(db, data);\nage = DB.db_table(db, age);\narea = DB.db_table(db, area);\nethnic = DB.db_table(db, ethnic);\nsex = DB.db_table(db, sex);\nyear = DB.db_table(db, year);\n

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#exploration","title":"Exploration","text":"

While this long chain could be broken up into multiple smaller chains, lets reproduce the duckplyr code from example and demonstrate how TidierDB also supports multiple joins after filtering, mutating, etc the joining tables. 6 different tables are being joined together through sequential inner joins.

@chain DB.t(data) begin\n  DB.@filter(str_detect(count, r\"^\\d+$\"))\n  DB.@mutate(count_ = as_integer(count))\n  DB.@filter(count_ > 0)\n  DB.@inner_join(\n    (@chain DB.t(age) begin\n    DB.@filter(str_detect(Description, r\"^\\d+ years$\"))\n    DB.@mutate(age_ = as_integer(str_remove(Code, \"years\"))) end),\n    Age = Code\n  )\n  DB.@inner_join((@chain DB.t(year) DB.@mutate(year_ = Description)), year = Code)\n  DB.@inner_join((@chain DB.t(area) begin\n    DB.@mutate(area_ = Description)\n    DB.@filter(!str_detect(area_, r\"^Total\"))\n  end)\n    , Area = Code)\n    DB.@inner_join((@chain DB.t(ethnic) begin\n      DB.@mutate(ethnic_ = Description)\n      DB.@filter(!str_detect( ethnic_, r\"^Total\",)) end), Ethnic = Code)\n  DB.@inner_join((@chain DB.t(sex) begin\n    DB.@mutate(sex_ = Description)\n    DB.@filter(!str_detect( sex_, r\"^Total\"))\n  end)\n   , Sex = Code)\n  DB.@inner_join((@chain DB.t(year) DB.@mutate(year_ = Description)), Year = Code)\n  @aside DB.@show_query _\n  DB.@create_view(joined_up)\nend;\n\n@chain DB.db_table(db, \"joined_up\") begin\n  DB.@filter begin\n    age_ >= 20\n    age_ <= 40\n    str_detect(area_, r\"^Auckland\")\n    year_ == \"2018\"\n    ethnic_ != \"European\"\n    end\n  DB.@group_by sex_\n  DB.@summarise(group_count = sum(count_))\n  DB.@collect\nend\n

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#results","title":"Results","text":"

When we collect this to a local dataframe, we can see that the results match the duckplyr/DuckDB example.

2\u00d72 DataFrame\n Row \u2502 sex_    group_count\n     \u2502 String  Int128\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Female       398556\n   2 \u2502 Male         397326\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/ex_joining/","title":"Joining Tables","text":"

This page will illustrate how to join different tables in TidierDB. The examples will use the mtcars dataset and a synthetic dataset called mt2 hosted on a personal MotherDuck instance. Examples will cover how to join tables with different schemas in different databases, and how to write queries on tables and then join them together, and how to do this by levaraging views.

"},{"location":"examples/generated/UserGuide/ex_joining/#setup","title":"Setup","text":"
using TidierDB\ndb = connect(duckdb(), \"md:\")\n\nmtcars = db_table(db, \"my_db.mtcars\")\nmt2 = db_table(db, \"ducks_db.mt2\")\n
"},{"location":"examples/generated/UserGuide/ex_joining/#wrangle-tables-and-self-join","title":"Wrangle tables and self join","text":"
query = @chain t(mtcars) begin\n    @group_by cyl\n    @summarize begin\n        across(mpg, (mean, minimum, maximum))\n        num_cars = n()\n        end\n    @mutate begin\n        efficiency = case_when(\n            mpg_mean >= 25, \"High\",\n            mpg_mean >= 15, \"Moderate\",\n            \"Low\" )\n      end\nend;\n\nquery2 = @chain t(mtcars) @filter(mpg>20) @mutate(mpg = mpg *4);\n\n@chain t(query) begin\n    @left_join(t(query2), cyl, cyl)\n    @group_by(efficiency)\n    @summarize(avg_mean = mean(mpg))\n    @mutate(mean = avg_mean / 4 )\n    @aside @show_query _\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n
"},{"location":"examples/generated/UserGuide/ex_joining/#different-schemas","title":"Different schemas","text":"

To connect to a table in a different schema, prefix it with a dot. For example, \"schemaname.tablename\". In this query, we are also filtering out cars that contain \"M\" in the name from the mt2 table before joining.

other_db = @chain db_table(db, \"ducks_db.mt2\") @filter(!str_detect(car, \"M\"))\n@chain t(mtcars) begin\n    @left_join(t(other_db), car, model)\n    @select(car, model)\n    @head(5)\n    @collect\nend\n
5\u00d72 DataFrame\n Row \u2502 car                model\n     \u2502 String             String\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Datsun 710         Datsun 710\n   2 \u2502 Hornet 4 Drive     Hornet 4 Drive\n   3 \u2502 Hornet Sportabout  Hornet Sportabout\n   4 \u2502 Valiant            Valiant\n   5 \u2502 Duster 360         Duster 360\n

To join directly to the table, you can use the @left_join macro with the table name as a string.

@chain t(mtcars) begin\n    @left_join(\"ducks_db.mt2\", car, model)\n    @select(car, model)\n    @head(5)\n    @collect\nend\n
5\u00d72 DataFrame\n Row \u2502 car                model\n     \u2502 String             String\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4          Mazda RX4\n   2 \u2502 Mazda RX4 Wag      Mazda RX4 Wag\n   3 \u2502 Datsun 710         Datsun 710\n   4 \u2502 Hornet 4 Drive     Hornet 4 Drive\n   5 \u2502 Hornet Sportabout  Hornet Sportabout\n

"},{"location":"examples/generated/UserGuide/ex_joining/#using-a-view","title":"Using a View","text":"

You can also use @create_view to create views and then join them. This is an alternate reuse complex queries.

# notice, this is not begin saved, bc a view is created in the database at the end of the chain\n@chain t(mtcars) begin\n       @group_by cyl\n       @summarize begin\n            across(mpg, (mean, minimum, maximum))\n            num_cars = n()\n        end\n       @mutate begin\n           efficiency = case_when(\n           mpg_mean >= 25, \"High\",\n           mpg_mean >= 15, \"Moderate\",\n              \"Low\" )\n        end\n       #create a view in the database\n       @create_view(viewer)\nend;\n\n# access the view like as if it was any other table\n@chain db_table(db, \"viewer\") begin\n    @left_join(t(query2), cyl, cyl)\n    @group_by(efficiency)\n    @summarize(avg_mean = mean(mpg))\n    @mutate(mean = avg_mean / 4 )\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/from_queryex/","title":"Reusing a Query (and Views)","text":"

While using TidierDB, you may need to generate part of a query and reuse it multiple times. There are two ways to do this

  1. from_query(query) or its alias t(query)
  2. @create_view(name)

"},{"location":"examples/generated/UserGuide/from_queryex/#setup","title":"Setup","text":"
import TidierDB as DB\ncon = DB.connect(duckdb())\nmtcars_path = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\nmtcars = DB.db_table(con, mtcars_path)\n

Start a query to analyze fuel efficiency by number of cylinders. However, to further build on this query later, end the chain without using @show_query or @collect

query = DB.@chain DB.t(mtcars) begin\n    DB.@group_by cyl\n    DB.@summarize begin\n        across(mpg, (mean, minimum, maximum))\n        num_cars = n()\n        end\n    DB.@mutate begin\n        efficiency = case_when(\n            mpg_mean >= 25, \"High\",\n            mpg_mean >= 15, \"Moderate\",\n            \"Low\" )\n       end\nend;\n

"},{"location":"examples/generated/UserGuide/from_queryex/#from_query-or-tquery","title":"from_query() or t(query)","text":"

Now, from_query, or t() a convienece wrapper, will allow you to reuse the query to calculate the average horsepower for each efficiency category

DB.@chain DB.t(query) begin\n   DB.@left_join(DB.t(mtcars), cyl = cyl)\n   DB.@group_by(efficiency)\n   DB.@summarize(avg_hp = mean(hp))\n   DB.@collect\nend\n
2\u00d72 DataFrame\n Row \u2502 efficiency  avg_hp\n     \u2502 String?     Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Moderate    180.238\n   2 \u2502 High         82.6364\n

"},{"location":"examples/generated/UserGuide/from_queryex/#create_view","title":"@create_view","text":"

This can also be done with @create_view.

query2 = @chain t(mtcars) @filter(mpg>20) @mutate(mpg = mpg *4);\nDB.@chain  DB.db_table(db, \"mtcars\") begin\n           DB.@group_by cyl\n           DB.@summarize begin\n               across(mpg, (mean, minimum, maximum))\n               num_cars = n()\n               end\n           DB.@mutate begin\n               efficiency = case_when(\n                   mpg_mean >= 25, \"High\",\n                   mpg_mean >= 15, \"Moderate\",\n                   \"Low\" )\n             end\n       DB.@create_view(viewer)\n       end;\n\n\nDB.@chain DB.db_table(db, \"viewer\") begin\n           DB.@left_join(DB.t(query2), cyl = cyl)\n           DB.@group_by(efficiency)\n           DB.@summarize(avg_mean = mean(mpg))\n           DB.@mutate(mean = avg_mean / 4 )\n           @aside DB.@show_query _\n           DB.@collect\nend\n2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n

"},{"location":"examples/generated/UserGuide/from_queryex/#preview-or-save-an-intermediate-table","title":"Preview or save an intermediate table","text":"

While querying a dataset, you may wish to see an intermediate table, or even save it. You can use @aside and from_query(_), illustrated below, to do just that. While we opted to print the results in this simple example below, we could have saved them by using name = DB.@chain...

import ClickHouse;\nconn = conn = DB.connect(DB.clickhouse(); host=\"localhost\", port=19000, database=\"default\", user=\"default\", password=\"\")\npath = \"https://huggingface.co/datasets/maharshipandya/spotify-tracks-dataset/resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet\"\nDB.@chain DB.db_table(conn, path) begin\n   DB.@count(cyl)\n   @aside println(DB.@chain DB.from_query(_) DB.@head(5) DB.@collect)\n   DB.@arrange(desc(count))\n   DB.@collect\nend\n
5\u00d72 DataFrame\n Row \u2502 artists  count\n     \u2502 String?  UInt64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 missing       1\n   2 \u2502 Wizo          3\n   3 \u2502 MAGIC!        3\n   4 \u2502 Macaco        1\n   5 \u2502 SOYOU         1\n31438\u00d72 DataFrame\n   Row \u2502 artists          count\n       \u2502 String?          UInt64\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n     1 \u2502 The Beatles         279\n     2 \u2502 George Jones        271\n     3 \u2502 Stevie Wonder       236\n     4 \u2502 Linkin Park         224\n     5 \u2502 Ella Fitzgerald     222\n     6 \u2502 Prateek Kuhad       217\n     7 \u2502 Feid                202\n   \u22ee   \u2502        \u22ee           \u22ee\n 31432 \u2502 Leonard               1\n 31433 \u2502 marcos g              1\n 31434 \u2502 BLVKSHP               1\n 31435 \u2502 Memtrix               1\n 31436 \u2502 SOYOU                 1\n 31437 \u2502 Macaco                1\n 31438 \u2502 missing               1\n               31424 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/","title":"Writing Functions with TidierDB Chains","text":"

On this page, we'll briefly explore how to use TidierDB macros and $ witth @eval to bulid a function

For a more indepth explanation, please check out the TidierData page on interpolation

using TidierDB, DataFrames;\n\ndb = connect(duckdb());\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\ncopy_to(db, df, \"dfm\");\ndf_mem = db_table(db, \"dfm\");\n

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#interpolation","title":"Interpolation","text":"

Variables are interpoated using @eval and $. Place @eval before you begin the chain or call a TidierDb macro Why Use @eval? In Julia, macros like @filter are expanded at parse time, before runtime variables like vals are available. By using @eval, we force the expression to be evaluated at runtime, allowing us to interpolate the variable into the macro.

num = [3];\ncolumn = :id;\n@eval @chain t(df_mem) begin\n        @filter(value in $num)\n        @select($column)\n        @collect\n    end\n
2\u00d71 DataFrame RowidString1AC2AH

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#function-set-up","title":"Function set up","text":"

Begin by defining your function as your normally would, but before @chain you need to use @eval. For the variables to be interpolated in need to be started with $

function test(vals, cols)\n    @eval @chain t(df_mem) begin\n        @filter(value in $vals)\n        @select($cols)\n        @collect\n    end\nend;\n\nvals = [1,  2,  3, 3];\ntest(vals, [:groups, :value, :percent])\n
6\u00d73 DataFrame RowgroupsvaluepercentStringInt64Float641bb10.12aa10.63aa20.24bb20.75bb30.36aa30.8

Now with a new variable

other_vals = [1];\ncols = [:value, :percent];\ntest(other_vals, cols)\n
2\u00d72 DataFrame RowvaluepercentInt64Float64110.1210.6

Defineing a new function

function gs(groups, aggs, new_name, threshold)\n    @eval @chain t(df_mem) begin\n        @group_by($groups)\n        @summarize($new_name = mean($aggs))\n        @filter($new_name > $threshold)\n        @collect\n    end\nend;\n\ngs(:groups, :percent, :mean_percent, .5)\n
1\u00d72 DataFrame Rowgroupsmean_percentStringFloat641aa0.6

Change the column and threshold

gs(:groups, :value, :mean_value, 2)\n
2\u00d72 DataFrame Rowgroupsmean_valueStringFloat641bb3.02aa3.0

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#write-pipeline-function-to-use-inside-of-chains","title":"Write pipeline function to use inside of chains","text":"

Lets say there is a particular sequence of macros that you want repeatedly use. Wrap this series into a function that accepts a t(query as its first argument and returns a SQLquery and you can easily resuse it.

function moving_aggs(table, start, stop, group, order, col)\n    qry = @eval @chain $table begin\n        @group_by $group\n        @window_frame $start $stop\n        @window_order $order\n        @mutate(across($col, (minimum, maximum, mean)))\n    end\n    return qry\nend;\n\n@chain t(df_mem) begin\n    moving_aggs(-2, 1, :groups, :percent, :value)\n    @filter value_mean > 2.75\n    @aside @show_query _\n    @collect\nend\n
6\u00d77 DataFrame Rowidgroupsvaluepercentvalue_minimumvalue_maximumvalue_meanStringStringInt64Float64Int64Int64Float641ABaa20.2243.02AHaa30.8153.253AJaa51.0153.04ACbb30.3153.05AGbb20.7253.56AIbb40.9253.66667

Filtering before the window functions

@chain t(df_mem) begin\n    @filter(value >=2 )\n    moving_aggs(-1, 1, :groups, :percent, :value)\n    @aside @show_query _\n    @collect\nend\n
8\u00d77 DataFrame Rowidgroupsvaluepercentvalue_minimumvalue_maximumvalue_meanStringStringInt64Float64Int64Int64Float641ABaa20.2243.02ADaa40.4243.03AHaa30.8354.04AJaa51.0354.05ACbb30.3354.06AEbb50.5253.333337AGbb20.7253.666678AIbb40.9243.0

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#interpolating-queries","title":"Interpolating Queries","text":"

To use a prior, uncollected TidierDB query in other TidierDB macros, interpolate the needed query without showing or collecting it

ok = @chain t(df_mem) @summarize(mean = mean(value));\n

The mean value represented in SQL from the above is 3

With @filter

@eval @chain t(df_mem) begin\n    @filter( value > $ok)\n    @collect\nend\n
4\u00d74 DataFrame RowidgroupsvaluepercentStringStringInt64Float641ADaa40.42AEbb50.53AIbb40.94AJaa51.0

With @mutate

@eval @chain t(df_mem) begin\n    @mutate(value2 =  value + $ok)\n    @collect\nend\n
10\u00d75 DataFrame Rowidgroupsvaluepercentvalue2StringStringInt64Float64Float641AAbb10.14.02ABaa20.25.03ACbb30.36.04ADaa40.47.05AEbb50.58.06AFaa10.64.07AGbb20.75.08AHaa30.86.09AIbb40.97.010AJaa51.08.0

With @summarize

@eval @chain t(df_mem) begin\n    @summarize(value =  mean(value) * $ok)\n    @collect\nend\n
1\u00d71 DataFrame RowvalueFloat6419.0

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/getting_started/","title":"Getting Started","text":"

To use TidierDB.jl, you will have to set up a connection. TidierDB.jl gives you access to duckdb via duckdb_open and duckdb_connect. However, to use MySql, ClickHouse, MSSQL, Postgres, or SQLite, you will have to load those packages in first.

If you plan to use TidierDB.jl with TidierData.jl or Tidier.jl, it is most convenenient to load the packages as follows:

using TidierData\nimport TidierDB as DB\n

Alternatively, using Tidier will import TidierDB in the above manner for you, where TidierDB functions and macros will be available as DB.@mutate() and so on, and the TidierData equivalent would be @mutate().

"},{"location":"examples/generated/UserGuide/getting_started/#connecting","title":"Connecting","text":"

To connect to a database, you can uset the connect function as shown below, or establish your own connection through the respecitve libraries.

For example Connecting to MySQL

conn = DB.connect(DB.mysql(); host=\"localhost\", user=\"root\", password=\"password\", db=\"mydb\")\n

versus connecting to DuckDB

conn = DB.connect(DB.duckdb())\n

"},{"location":"examples/generated/UserGuide/getting_started/#connect-to-a-local-database-file","title":"Connect to a local database file","text":"

You can also connect to an existing database by passing the database file path as a string.

db = DB.connect(DB.duckdb(), \"mydb.duckdb\")\n

You can also establish any DuckDB connection through an alternate method that you prefer, and use that as your connection as well.

"},{"location":"examples/generated/UserGuide/getting_started/#package-extensions","title":"Package Extensions","text":"

The following backends utilize package extensions. To use one of backends listed below, you will need to write using Library

  • ClickHouse: import ClickHouse
  • MySQL and MariaDB: using MySQL
  • MSSQL: using ODBC
  • Postgres: using LibPQ
  • SQLite: using SQLite
  • Athena: using AWS
  • Oracle: using ODBC
  • Google BigQuery: using GoogleCloud

"},{"location":"examples/generated/UserGuide/getting_started/#db_table","title":"db_table","text":"

What does db_table do?

db_table starts the underlying SQL query struct, in addition to pulling the table metadata and storing it there. Storing metadata is what enables a lazy interface that also supports tidy selection.

  • db_table has two required arguments: connection and table
  • table can be a table name on a database or a path/url to file to read. When passing db_table a path or url, the table is not copied into memory.

    • Of note, db_table only support direct file paths to a table. It does not support database file paths such as dbname.duckdb or dbname.sqlite. Such files must be used with connect first.
    • With DuckDB and ClickHouse, if you have a folder of multiple files to read, you can use * read in all files matching the pattern.
    • For example, the below would read all files that end in .csv in the given folder.
db_table(db, \"folder/path/*.csv\")\n

db_table also supports iceberg, delta, and S3 file paths via DuckDB.

"},{"location":"examples/generated/UserGuide/getting_started/#minimizing-compute-costs","title":"Minimizing Compute Costs","text":"

If you are working with a backend where compute cost is important, it will be important to minimize using db_table as this will requery for metadata each time. Compute costs are relevant to backends such as AWS, databricks and Snowflake.

To do this, save the results of db_table and use them with t. Using t pulls the relevant information (metadata, con, etc) from the mutable SQLquery struct, allowing you to repeatedly query and collect the table without requerying for the metadata each time

!Tip: t() is an alias for from_query This means after saving the results of db_table, use t(table) to refer to the table or prior query

table = DB.db_table(con, \"path\")\n@chain DB.t(table) begin\n    ## data wrangling here\nend\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/ibis_comp/","title":"TidierDB.jl vs Ibis","text":""},{"location":"examples/generated/UserGuide/ibis_comp/#comparing-tidierdb-vs-ibis","title":"Comparing TidierDB vs Ibis","text":"

TidierDB is a reimplementation of dbplyr from R, so the syntax is remarkably similar. But how does TidierDB compare to Python's Ibis? This page will perform a similar comparison to the Ibis Documentation comparing Ibis and dplyr

"},{"location":"examples/generated/UserGuide/ibis_comp/#set-up","title":"Set up","text":"

Ibis

import ibis\nimport ibis.selectors as s # allows for different styles of column selection\nfrom ibis import _ # eliminates need to type table name before each column vs typing cols as strings\nibis.options.interactive = True # automatically collects first 10 rows of table\n\ncon = ibis.connect(\"duckdb://\")\n

TidierDB

using TidierDB\ndb = connect(duckdb())\n

Of note, TidierDB does not yet have an \"interactive mode\" so each example result will be collected.

"},{"location":"examples/generated/UserGuide/ibis_comp/#loading-data","title":"Loading Data","text":"

With Ibis, there are specific functions to read in different file types

mtcars = con.read_csv(\"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\")\n

In TidierDB, there is only db_table, which determines the file type and generates the syntax appropriate for the backend in use.

mtcars = db_table(db, \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\");\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#previewing-the-data","title":"Previewing the data","text":"

TidierDB and Ibis use head/@head to preview the first rows of a dataset.

Ibis

mtcars.head(6)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502    21.0 \u2502     6 \u2502   160.0 \u2502   110 \u2502    3.90 \u2502   2.620 \u2502   16.46 \u2502     0 \u2502     1 \u2502     4 \u2502     4 \u2502\n\u2502 Mazda RX4 Wag     \u2502    21.0 \u2502     6 \u2502   160.0 \u2502   110 \u2502    3.90 \u2502   2.875 \u2502   17.02 \u2502     0 \u2502     1 \u2502     4 \u2502     4 \u2502\n\u2502 Datsun 710        \u2502    22.8 \u2502     4 \u2502   108.0 \u2502    93 \u2502    3.85 \u2502   2.320 \u2502   18.61 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Hornet 4 Drive    \u2502    21.4 \u2502     6 \u2502   258.0 \u2502   110 \u2502    3.08 \u2502   3.215 \u2502   19.44 \u2502     1 \u2502     0 \u2502     3 \u2502     1 \u2502\n\u2502 Hornet Sportabout \u2502    18.7 \u2502     8 \u2502   360.0 \u2502   175 \u2502    3.15 \u2502   3.440 \u2502   17.02 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Valiant           \u2502    18.1 \u2502     6 \u2502   225.0 \u2502   105 \u2502    2.76 \u2502   3.460 \u2502   20.22 \u2502     1 \u2502     0 \u2502     3 \u2502     1 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) @head(6) @collect\n
6\u00d712 DataFrame\n Row \u2502 model              mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?            Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4              21.0       6     160.0     110      3.9      2.62      16.46       0       1       4       4\n   2 \u2502 Mazda RX4 Wag          21.0       6     160.0     110      3.9      2.875     17.02       0       1       4       4\n   3 \u2502 Datsun 710             22.8       4     108.0      93      3.85     2.32      18.61       1       1       4       1\n   4 \u2502 Hornet 4 Drive         21.4       6     258.0     110      3.08     3.215     19.44       1       0       3       1\n   5 \u2502 Hornet Sportabout      18.7       8     360.0     175      3.15     3.44      17.02       0       0       3       2\n   6 \u2502 Valiant                18.1       6     225.0     105      2.76     3.46      20.22       1       0       3       1\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#filtering","title":"Filtering","text":"

The example below demonstrates how to filter using multiple criteria in both Ibis and TidierData Ibis

mtcars.filter(((_.mpg > 22) & (_.drat > 4) | (_.hp == 113)))\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model          \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string         \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Lotus Europa   \u2502    30.4 \u2502     4 \u2502    95.1 \u2502   113 \u2502    3.77 \u2502   1.513 \u2502   16.90 \u2502     1 \u2502     1 \u2502     5 \u2502     2 \u2502\n\u2502 Fiat 128       \u2502    32.4 \u2502     4 \u2502    78.7 \u2502    66 \u2502    4.08 \u2502   2.200 \u2502   19.47 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Honda Civic    \u2502    30.4 \u2502     4 \u2502    75.7 \u2502    52 \u2502    4.93 \u2502   1.615 \u2502   18.52 \u2502     1 \u2502     1 \u2502     4 \u2502     2 \u2502\n\u2502 Toyota Corolla \u2502    33.9 \u2502     4 \u2502    71.1 \u2502    65 \u2502    4.22 \u2502   1.835 \u2502   19.90 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Fiat X1-9      \u2502    27.3 \u2502     4 \u2502    79.0 \u2502    66 \u2502    4.08 \u2502   1.935 \u2502   18.90 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Porsche 914-2  \u2502    26.0 \u2502     4 \u2502   120.3 \u2502    91 \u2502    4.43 \u2502   2.140 \u2502   16.70 \u2502     0 \u2502     1 \u2502     5 \u2502     2 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) begin\n       @filter((mpg > 22 && drat > 4) || hp == 113)\n       @collect\nend\n
6\u00d712 DataFrame\n Row \u2502 model           mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?         Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Lotus Europa        30.4       4      95.1     113      3.77     1.513     16.9        1       1       5       2\n   2 \u2502 Fiat 128            32.4       4      78.7      66      4.08     2.2       19.47       1       1       4       1\n   3 \u2502 Honda Civic         30.4       4      75.7      52      4.93     1.615     18.52       1       1       4       2\n   4 \u2502 Toyota Corolla      33.9       4      71.1      65      4.22     1.835     19.9        1       1       4       1\n   5 \u2502 Fiat X1-9           27.3       4      79.0      66      4.08     1.935     18.9        1       1       4       1\n   6 \u2502 Porsche 914-2       26.0       4     120.3      91      4.43     2.14      16.7        0       1       5       2\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#creating-new-columns","title":"Creating new columns","text":"

Both TidierDB and Ibis use mutate/@mutate to add new columns

Ibis

(\n   mtcars\n        .mutate(kpg = _.mpg * 1.61)\n        .select(\"model\", \"kpg\")\n)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 kpg     \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502  33.810 \u2502\n\u2502 Mazda RX4 Wag     \u2502  33.810 \u2502\n\u2502 Datsun 710        \u2502  36.708 \u2502\n\u2502 Hornet 4 Drive    \u2502  34.454 \u2502\n\u2502 Hornet Sportabout \u2502  30.107 \u2502\n\u2502 Valiant           \u2502  29.141 \u2502\n\u2502 Duster 360        \u2502  23.023 \u2502\n\u2502 Merc 240D         \u2502  39.284 \u2502\n\u2502 Merc 230          \u2502  36.708 \u2502\n\u2502 Merc 280          \u2502  30.912 \u2502\n\u2502 \u2026                 \u2502       \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) begin\n       @mutate(kpg = mpg * 1.61)\n       @select(model, kpg)\n       @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 model              kpg\n     \u2502 String?            Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4            33.81\n   2 \u2502 Mazda RX4 Wag        33.81\n   3 \u2502 Datsun 710           36.708\n   4 \u2502 Hornet 4 Drive       34.454\n   5 \u2502 Hornet Sportabout    30.107\n   6 \u2502 Valiant              29.141\n  \u22ee  \u2502         \u22ee             \u22ee\n  27 \u2502 Porsche 914-2        41.86\n  28 \u2502 Lotus Europa         48.944\n  29 \u2502 Ford Pantera L       25.438\n  30 \u2502 Ferrari Dino         31.717\n  31 \u2502 Maserati Bora        24.15\n  32 \u2502 Volvo 142E           34.454\n                    20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#sorting-columns","title":"Sorting columns","text":"

Ibis uses order_by similar to SQLs ORDER BY

Ibis

mtcars.order_by(_.mpg)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model               \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string              \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Cadillac Fleetwood  \u2502    10.4 \u2502     8 \u2502   472.0 \u2502   205 \u2502    2.93 \u2502   5.250 \u2502   17.98 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Lincoln Continental \u2502    10.4 \u2502     8 \u2502   460.0 \u2502   215 \u2502    3.00 \u2502   5.424 \u2502   17.82 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Camaro Z28          \u2502    13.3 \u2502     8 \u2502   350.0 \u2502   245 \u2502    3.73 \u2502   3.840 \u2502   15.41 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Duster 360          \u2502    14.3 \u2502     8 \u2502   360.0 \u2502   245 \u2502    3.21 \u2502   3.570 \u2502   15.84 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Chrysler Imperial   \u2502    14.7 \u2502     8 \u2502   440.0 \u2502   230 \u2502    3.23 \u2502   5.345 \u2502   17.42 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Maserati Bora       \u2502    15.0 \u2502     8 \u2502   301.0 \u2502   335 \u2502    3.54 \u2502   3.570 \u2502   14.60 \u2502     0 \u2502     1 \u2502     5 \u2502     8 \u2502\n\u2502 Merc 450SLC         \u2502    15.2 \u2502     8 \u2502   275.8 \u2502   180 \u2502    3.07 \u2502   3.780 \u2502   18.00 \u2502     0 \u2502     0 \u2502     3 \u2502     3 \u2502\n\u2502 AMC Javelin         \u2502    15.2 \u2502     8 \u2502   304.0 \u2502   150 \u2502    3.15 \u2502   3.435 \u2502   17.30 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Dodge Challenger    \u2502    15.5 \u2502     8 \u2502   318.0 \u2502   150 \u2502    2.76 \u2502   3.520 \u2502   16.87 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Ford Pantera L      \u2502    15.8 \u2502     8 \u2502   351.0 \u2502   264 \u2502    4.22 \u2502   3.170 \u2502   14.50 \u2502     0 \u2502     1 \u2502     5 \u2502     4 \u2502\n\u2502 \u2026                   \u2502       \u2026 \u2502     \u2026 \u2502       \u2026 \u2502     \u2026 \u2502       \u2026 \u2502       \u2026 \u2502       \u2026 \u2502     \u2026 \u2502     \u2026 \u2502     \u2026 \u2502     \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

While TidierDB uses @arrange like TidierData.jl

TidierDB

@chain t(mtcars) @arrange(mpg) @collect\n
32\u00d712 DataFrame\n Row \u2502 model                mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?              Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Cadillac Fleetwood       10.4       8     472.0     205      2.93     5.25      17.98       0       0       3       4\n   2 \u2502 Lincoln Continental      10.4       8     460.0     215      3.0      5.424     17.82       0       0       3       4\n   3 \u2502 Camaro Z28               13.3       8     350.0     245      3.73     3.84      15.41       0       0       3       4\n   4 \u2502 Duster 360               14.3       8     360.0     245      3.21     3.57      15.84       0       0       3       4\n   5 \u2502 Chrysler Imperial        14.7       8     440.0     230      3.23     5.345     17.42       0       0       3       4\n   6 \u2502 Maserati Bora            15.0       8     301.0     335      3.54     3.57      14.6        0       1       5       8\n  \u22ee  \u2502          \u22ee              \u22ee        \u22ee        \u22ee        \u22ee        \u22ee         \u22ee         \u22ee        \u22ee       \u22ee       \u22ee       \u22ee\n  27 \u2502 Porsche 914-2            26.0       4     120.3      91      4.43     2.14      16.7        0       1       5       2\n  28 \u2502 Fiat X1-9                27.3       4      79.0      66      4.08     1.935     18.9        1       1       4       1\n  29 \u2502 Honda Civic              30.4       4      75.7      52      4.93     1.615     18.52       1       1       4       2\n  30 \u2502 Lotus Europa             30.4       4      95.1     113      3.77     1.513     16.9        1       1       5       2\n  31 \u2502 Fiat 128                 32.4       4      78.7      66      4.08     2.2       19.47       1       1       4       1\n  32 \u2502 Toyota Corolla           33.9       4      71.1      65      4.22     1.835     19.9        1       1       4       1\n                                                                                                              20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#selecting-columns","title":"Selecting columns","text":"

In Ibis, columns must be prefixed with the table name, or in this case _, or they can be given as a string. Finally to using helper functions like startswith requires importing selectors as above.

Ibis

mtcars.select(s.startswith(\"m\"), \"drat\", _.wt)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 mpg     \u2503 drat    \u2503 wt      \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502 float64 \u2502 float64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502    21.0 \u2502    3.90 \u2502   2.620 \u2502\n\u2502 Mazda RX4 Wag     \u2502    21.0 \u2502    3.90 \u2502   2.875 \u2502\n\u2502 Datsun 710        \u2502    22.8 \u2502    3.85 \u2502   2.320 \u2502\n\u2502 Hornet 4 Drive    \u2502    21.4 \u2502    3.08 \u2502   3.215 \u2502\n\u2502 Hornet Sportabout \u2502    18.7 \u2502    3.15 \u2502   3.440 \u2502\n\u2502 Valiant           \u2502    18.1 \u2502    2.76 \u2502   3.460 \u2502\n\u2502 Duster 360        \u2502    14.3 \u2502    3.21 \u2502   3.570 \u2502\n\u2502 Merc 240D         \u2502    24.4 \u2502    3.69 \u2502   3.190 \u2502\n\u2502 Merc 230          \u2502    22.8 \u2502    3.92 \u2502   3.150 \u2502\n\u2502 Merc 280          \u2502    19.2 \u2502    3.92 \u2502   3.440 \u2502\n\u2502 \u2026                 \u2502       \u2026 \u2502       \u2026 \u2502       \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB does not require names to be prefixed and, like TidierData, tidy column selection with starts_with, ends_with, and contains is supported at base. TidierDB also supports providing column names as strings, although this would only be needed in the setting of renaming a column with a space in it.

TidierDB

@chain t(mtcars) @select(starts_with(\"m\"), \"drat\", wt) @collect\n
32\u00d74 DataFrame\n Row \u2502 model              mpg       drat      wt\n     \u2502 String?            Float64?  Float64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4              21.0      3.9      2.62\n   2 \u2502 Mazda RX4 Wag          21.0      3.9      2.875\n   3 \u2502 Datsun 710             22.8      3.85     2.32\n   4 \u2502 Hornet 4 Drive         21.4      3.08     3.215\n   5 \u2502 Hornet Sportabout      18.7      3.15     3.44\n   6 \u2502 Valiant                18.1      2.76     3.46\n  \u22ee  \u2502         \u22ee             \u22ee         \u22ee         \u22ee\n  27 \u2502 Porsche 914-2          26.0      4.43     2.14\n  28 \u2502 Lotus Europa           30.4      3.77     1.513\n  29 \u2502 Ford Pantera L         15.8      4.22     3.17\n  30 \u2502 Ferrari Dino           19.7      3.62     2.77\n  31 \u2502 Maserati Bora          15.0      3.54     3.57\n  32 \u2502 Volvo 142E             21.4      4.11     2.78\n                                        20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#multi-step-queries-and-summarizing","title":"Multi step queries and summarizing","text":"

Aggregating data is done with aggregate in Ibis and @summarize in TidierDB. To group data, both utilze group_by/@group_by Ibis

mtcars.group_by(._cyl).aggregate(\n    total_hp=_.hp.sum(),\n    avg_hp=_.hp.mean()\n).filter(_.total_hp < 1000)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 cyl   \u2503 total_hp \u2503 avg_hp     \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 int64 \u2502 int64    \u2502 float64    \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502     6 \u2502      856 \u2502 122.285714 \u2502\n\u2502     4 \u2502      909 \u2502  82.636364 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

In TidierDB, @filter will automatically determine whether the criteria belong in a WHERE or HAVING SQL clause.

TidierDB

@chain t(mtcars) begin\n    @group_by(cyl)\n    @summarize(total_hp = sum(hp),\n               avg_hp = avg(hp))\n    @filter(total_hp < 1000)\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 cyl     total_hp  avg_hp\n     \u2502 Int64?  Int128?   Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      6       856  122.286\n   2 \u2502      4       909   82.6364\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#renaming-columns","title":"Renaming columns","text":"

Both tools use rename/@rename to rename columns

Ibis

mtcars.rename(make_model = \"model\").select(_.make_model)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 make_model        \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502\n\u2502 Mazda RX4 Wag     \u2502\n\u2502 Datsun 710        \u2502\n\u2502 Hornet 4 Drive    \u2502\n\u2502 Hornet Sportabout \u2502\n\u2502 Valiant           \u2502\n\u2502 Duster 360        \u2502\n\u2502 Merc 240D         \u2502\n\u2502 Merc 230          \u2502\n\u2502 Merc 280          \u2502\n\u2502 \u2026                 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) @rename(model_make = model) @select(model_make) @collect\n
32\u00d71 DataFrame\n Row \u2502 model_make\n     \u2502 String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4\n   2 \u2502 Mazda RX4 Wag\n   3 \u2502 Datsun 710\n   4 \u2502 Hornet 4 Drive\n   5 \u2502 Hornet Sportabout\n   6 \u2502 Valiant\n  \u22ee  \u2502         \u22ee\n  27 \u2502 Porsche 914-2\n  28 \u2502 Lotus Europa\n  29 \u2502 Ford Pantera L\n  30 \u2502 Ferrari Dino\n  31 \u2502 Maserati Bora\n  32 \u2502 Volvo 142E\n          20 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/key_differences/","title":"Key Differences from TidierData.jl","text":"

There are a few important syntax and behavior differences between TidierDB.jl and TidierData.jl outlined below.

"},{"location":"examples/generated/UserGuide/key_differences/#creating-a-database","title":"Creating a database","text":"

For these examples we will use DuckDB, the default backend, although SQLite, Postgres, MySQL, MariaDB, MSSQL, and ClickHouse are possible. If you have an existing DuckDB connection, then this step is not required. For these examples, we will create a data frame and copy it to an in-memory DuckDB database.

using DataFrames, TidierDB\n\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\n\ndb = connect(duckdb());\n\ncopy_to(db, df, \"df_mem\"); # copying over the data frame to an in-memory database\n

"},{"location":"examples/generated/UserGuide/key_differences/#row-ordering","title":"Row ordering","text":"

DuckDB benefits from aggressive parallelization of pipelines. This means that if you have multiple threads enabled in Julia, which you can check or set using Threads.nthreads(), DuckDB will use multiple threads. However, because many operations are multi-threaded, the resulting row order is inconsistent. If row order needs to be deterministic for your use case, make sure to apply an @arrange(column_name_1, column_name_2, etc...) prior to collecting the results.

"},{"location":"examples/generated/UserGuide/key_differences/#starting-a-chain","title":"Starting a chain","text":"

When using TidierDB, db_table(connection, :table_name) is used to start a chain.

"},{"location":"examples/generated/UserGuide/key_differences/#grouped-mutation","title":"Grouped mutation","text":"

In TidierDB, when performing @group_by then @mutate, the table will be ungrouped after applying all of the mutations in the clause to the grouped data. To perform subsequent grouped operations, the user would have to regroup the data. This is demonstrated below.

@chain db_table(db, :df_mem) begin\n    @group_by(groups)\n    @summarize(mean_percent = mean(percent))\n    @collect\n end\n
2\u00d72 DataFrame Rowgroupsmean_percentStringFloat641bb0.52aa0.6

Regrouping following @mutate

@chain db_table(db, :df_mem) begin\n    @group_by(groups)\n    @mutate(max = maximum(percent), min = minimum(percent))\n    @group_by(groups)\n    @summarise(mean_percent = mean(percent))\n    @collect\nend\n
2\u00d72 DataFrame Rowgroupsmean_percentStringFloat641aa0.62bb0.5

"},{"location":"examples/generated/UserGuide/key_differences/#differences-in-case_when","title":"Differences in case_when()","text":"

In TidierDB, after the clause is completed, the result for the new column should is separated by a comma , in contrast to TidierData.jl, where the result for the new column is separated by a => .

@chain db_table(db, :df_mem) begin\n    @mutate(new_col = case_when(percent > .5, \"Pass\",  # in TidierData, percent > .5 => \"Pass\",\n                                percent <= .5, \"Try Again\", # percent <= .5 => \"Try Again\"\n                                true, \"middle\"))\n    @collect\n end\n
10\u00d75 DataFrame Rowidgroupsvaluepercentnew_colStringStringInt64Float64String1AAbb10.1Try Again2ABaa20.2Try Again3ACbb30.3Try Again4ADaa40.4Try Again5AEbb50.5Try Again6AFaa10.6Pass7AGbb20.7Pass8AHaa30.8Pass9AIbb40.9Pass10AJaa51.0Pass

"},{"location":"examples/generated/UserGuide/key_differences/#joining-tables","title":"Joining Tables","text":"

When joining a table, the column from both tables will be present, in contrast to TidierData which will keep one column

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/outofmemex/","title":"Working With Larger than RAM Datasets","text":"

While using the DuckDB backend, TidierDB's lazy intferace enables querying datasets larger than your available RAM.

To illustrate this, we will recreate the Hugging Face x Polars example. The final table results are shown below and in this Hugging Face x DuckDB example

First we will load TidierDB, set up a local database and then set the URLs for the 2 training datasets from huggingface.co

using TidierDB\ndb = connect(duckdb())\n\nurls = [\"https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0000.parquet\",\n \"https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0001.parquet\"];\n

Here, we pass the vector of URLs to db_table, which will not copy them into memory. Since these datasets are so large, we will also set stream = true in @collect to stream the results. If we wanted to read all the files in the folder we could have replace the 0000 with * (wildcard) db_table(db, \"Path/to/folder/*.parquet\") Of note, reading these files from URLs is not as rapid as reading them from local files.

@chain db_table(db, urls) begin\n    @group_by(horoscope)\n    @summarise(count = n(), avg_blog_length = mean(length(text)))\n    @arrange(desc(count))\n    @aside @show_query _\n    @collect(stream = true)\nend\n

Placing @aside @show_query _ before @collect above lets us see the SQL query and collect it to a local DataFrame at the same time.

SELECT horoscope, COUNT(*) AS count, AVG(length(text)) AS avg_blog_length\n        FROM read_parquet(['https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0000.parquet', 'https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0001.parquet'])\n        GROUP BY horoscope\n        ORDER BY avg_blog_length DESC\n12\u00d73 DataFrame\n Row \u2502 horoscope    count   avg_blog_length\n     \u2502 String?      Int64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Aquarius      49568         1125.83\n   2 \u2502 Cancer        63512         1097.96\n   3 \u2502 Libra         60304         1060.61\n   4 \u2502 Capricorn     49402         1059.56\n   5 \u2502 Sagittarius   50431         1057.46\n   6 \u2502 Leo           58010         1049.6\n   7 \u2502 Taurus        61571         1022.69\n   8 \u2502 Gemini        52925         1020.26\n   9 \u2502 Scorpio       56495         1014.03\n  10 \u2502 Pisces        53812         1011.75\n  11 \u2502 Virgo         64629          996.684\n  12 \u2502 Aries         69134          918.081\n

To learn more about memory efficient queries on larger than RAM files, this blog from DuckDB will help maximize your local db

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/s3viaduckdb/","title":"S3 + DuckDB + TidierDB","text":"

TidierDB allows you leverage DuckDB's seamless database integration.

Using DuckDB, you can connect to an AWS or GoogleCloud Database to query directly without making any local copies.

You can also use DBInterface.execute to set up any DuckDB database connection you need and then use that db to query with TidierDB

using TidierDB\n\n#Connect to Google Cloud via DuckDB\n#google_db = connect(duckdb(), :gbq, access_key=\"string\", secret_key=\"string\")\n\n#Connect to AWS via DuckDB\naws_db = connect(duckdb(), :aws, aws_access_key_id= \"string\",\n                                aws_secret_access_key= \"string\",\n                                aws_region=\"us-east-1\")\ns3_csv_path = \"s3://path/to_data.csv\"\n\n@chain db_table(aws_db, s3_csv_path) begin\n    @filter(!starts_with(column1, \"M\"))\n    @group_by(cyl)\n    @summarize(mpg = mean(mpg))\n    @mutate(mpg_squared = mpg^2,\n               mpg_rounded = round(mpg),\n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))\n    @filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    @arrange(desc(mpg_rounded))\n    @collect\nend\n
2\u00d75 DataFrame\n Row \u2502 cyl     mpg       mpg_squared  mpg_rounded  mpg_efficiency\n     \u2502 Int64?  Float64?  Float64?     Float64?     String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4   27.3444      747.719         27.0  efficient\n   2 \u2502      6   19.7333      389.404         20.0  moderate\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/udfs_ex/","title":"Flexible Syntax and UDFs","text":"

TidierDB is unique in its statement parsing flexiblility. This means that using any built in SQL function or user defined functions (or UDFS) or is readily avaialable. To use any function built into a database in @mutate or in @summarize, simply correctly write the correctly, but replace ' with \". This also applies to any UDF. The example below will illustrate UDFs in the context of DuckDB.

# Set up the connection\nusing TidierDB  #rexports DuckDB\ndb = DuckDB.DB()\ncon = DuckDB.connect(db) # this will be important for UDFs\nmtcars_path = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\nmtcars = db_tbable(con, mtcars_path);\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#aggregate-function-in-summarize","title":"aggregate function in @summarize","text":"

Lets use the DuckDB kurtosis aggregate function

@chain t(mtcars) begin\n      @group_by cyl\n      @summarize(kurt = kurtosis(mpg))\n      @collect\nend\n3\u00d72 DataFrame\n Row \u2502 cyl     kurt\n     \u2502 Int64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4  -1.43411\n   2 \u2502      6  -1.82944\n   3 \u2502      8   0.330061\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#aggregate-functions-in-mutate","title":"aggregate functions in @mutate","text":"

To aggregate sql functions that are builtin to any database, but exist outside of the TidierDB parser, simply wrap the function call in agg()

@chain t(mtcars) begin\n    @group_by(cyl)\n    @mutate(kurt = agg(kurtosis(mpg)))\n    @select cyl mpg kurt\n    @collect\nend\n\n32\u00d73 DataFrame\n Row \u2502 cyl     mpg       kurt\n     \u2502 Int64?  Float64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      8      18.7   0.330061\n   2 \u2502      8      14.3   0.330061\n   3 \u2502      8      16.4   0.330061\n   4 \u2502      8      17.3   0.330061\n   5 \u2502      8      15.2   0.330061\n   6 \u2502      8      10.4   0.330061\n   7 \u2502      8      10.4   0.330061\n  \u22ee  \u2502   \u22ee        \u22ee          \u22ee\n  27 \u2502      6      21.0  -1.82944\n  28 \u2502      6      21.4  -1.82944\n  29 \u2502      6      18.1  -1.82944\n  30 \u2502      6      19.2  -1.82944\n  31 \u2502      6      17.8  -1.82944\n  32 \u2502      6      19.7  -1.82944\n                    19 rows omitted\nend\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#duckdb-function-chaining","title":"DuckDB function chaining","text":"

In DuckDB, functions can be chained together with .. TidierDB lets you leverage this.

@chain t(mtcars) begin\n    @mutate(model2 = model.upper().string_split(\" \").list_aggr(\"string_agg\",\".\").concat(\".\"))\n    @select model model2\n    @collect\nend\n32\u00d72 DataFrame\n Row \u2502 model              model2\n     \u2502 String?            String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4          MAZDA.RX4.\n   2 \u2502 Mazda RX4 Wag      MAZDA.RX4.WAG.\n   3 \u2502 Datsun 710         DATSUN.710.\n   4 \u2502 Hornet 4 Drive     HORNET.4.DRIVE.\n   5 \u2502 Hornet Sportabout  HORNET.SPORTABOUT.\n   6 \u2502 Valiant            VALIANT.\n   7 \u2502 Duster 360         DUSTER.360.\n  \u22ee  \u2502         \u22ee                  \u22ee\n  27 \u2502 Porsche 914-2      PORSCHE.914-2.\n  28 \u2502 Lotus Europa       LOTUS.EUROPA.\n  29 \u2502 Ford Pantera L     FORD.PANTERA.L.\n  30 \u2502 Ferrari Dino       FERRARI.DINO.\n  31 \u2502 Maserati Bora      MASERATI.BORA.\n  32 \u2502 Volvo 142E         VOLVO.142E.\n                              19 rows omitted\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#rowid-and-pseudocolumns","title":"rowid and pseudocolumns","text":"

When a table is not being read directly from a file, rowid is avaialable for use. In general, TidierDB should support all pseudocolumns.

copy_to(db, mtcars_path, \"mtcars\"); # copying table in for demostration purposes\n@chain db_table(con, :mtcars) begin\n      @filter(rowid == 4)\n      @select(model:hp)\n      @collect\nend\n1\u00d75 DataFrame\n Row \u2502 model              mpg       cyl     disp      hp\n     \u2502 String?            Float64?  Int64?  Float64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Hornet Sportabout      18.7       8     360.0     175\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#udf-sqlite-example","title":"UDF SQLite Example","text":"
using SQLite\nsql = connect(sqlite());\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\n\ncopy_to(db, sql, \"df_mem\");\nSQLite.@register sql function diff_of_squares(x, y)\n              x^2 - y^2\n              end;\n\n@chain db_table(sql, \"df_mem\") begin\n      @select(value, percent)\n      @mutate(plus3 = diff_of_squares(value, percent))\n      @collect\nend\n10\u00d73 DataFrame\n Row \u2502 value  percent  plus3\n     \u2502 Int64  Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1      0.1     0.99\n   2 \u2502     2      0.2     3.96\n   3 \u2502     3      0.3     8.91\n   4 \u2502     4      0.4    15.84\n   5 \u2502     5      0.5    24.75\n   6 \u2502     1      0.6     0.64\n   7 \u2502     2      0.7     3.51\n   8 \u2502     3      0.8     8.36\n   9 \u2502     4      0.9    15.19\n  10 \u2502     5      1.0    24.0\n
"},{"location":"examples/generated/UserGuide/udfs_ex/#how-to-create-udf-in-duckdb","title":"How to create UDF in DuckDB","text":"

Example coming soon..

This page was generated using Literate.jl.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#what-is-tidierdbjl","title":"What is TidierDB.jl?","text":"

TiderDB.jl is a 100% Julia implementation of the dbplyr R package, and similar to Python's ibis package.

The main goal of TidierDB.jl is to bring the syntax of Tidier.jl to multiple SQL backends, making it possible to analyze data directly on databases without needing to copy the entire database into memory.

"},{"location":"#currently-supported-backends-include","title":"Currently supported backends include:","text":"DuckDB (default) duckdb() ClickHouse clickhouse() SQLite sqlite() Postgres postgres() MySQL mysql() MariaDB mysql() MSSQL mssql() Athena athena() Snowflake snowflake() Databricks databricks() Google Big Query gbq() Oracle oracle()

Change the backend using set_sql_mode() - for example - set_sql_mode(databricks())

"},{"location":"#installation","title":"Installation","text":"

For the stable version:

] add TidierDB\n

TidierDB.jl currently supports:

Category Supported Macros and Functions Data Manipulation @arrange, @group_by, @filter, @select, @mutate (supports across), @summarize/@summarise (supports across), @distinct Joining @left_join, @right_join, @inner_join, @anti_join, @full_join, @semi_join, @union, @union_all Slice and Order @slice_min, @slice_max, @slice_sample, @order, @window_order, @window_frame Utility @show_query, @collect, @head, @count, show_tables, @create_view , drop_view Helper Functions across, desc, if_else, case_when, n, starts_with, ends_with, contains, as_float, as_integer, as_string, is_missing, missing_if, replace_missing TidierStrings.jl Functions str_detect, str_replace, str_replace_all, str_remove_all, str_remove TidierDates.jl Functions year, month, day, hour, min, second, floor_date, difftime, mdy, ymd, dmy Aggregate Functions mean, minimum, maximum, std, sum, cumsum, cor, cov, var, all aggregate sql fxns

@summarize supports any SQL aggregate function in addition to the list above. Simply write the function as written in SQL syntax and it will work. @mutate supports all builtin SQL functions as well.

When using the DuckDB backend, if db_table recieves a file path ( .parquet, .json, .csv, iceberg or delta), it does not copy it into memory. This allows for queries on files too big for memory. db_table also supports S3 bucket locations via DuckDB.

"},{"location":"#what-is-the-recommended-way-to-use-tidierdb","title":"What is the recommended way to use TidierDB?","text":"

Typically, you will want to use TidierDB alongside TidierData because there are certain functionality (such as pivoting) which are only supported in TidierData and can only be performed on data frames.

Our recommended path for using TidierDB is to import the package so that there are no namespace conflicts with TidierData. Once TidierDB is integrated with Tidier, then Tidier will automatically load the packages in this fashion.

First, let's develop and execute a query using TidierDB. Notice that all top-level macros and functions originating from TidierDB start with a DB prefix. Any functions defined within macros do not need to be prefixed within DB because they are actually pseudofunctions that are in actuality converted into SQL code.

Even though the code reads similarly to TidierData, note that no computational work actually occurs until you run DB.@collect(), which runs the SQL query and instantiates the result as a DataFrame.

using TidierData\nimport TidierDB as DB\n\ndb = DB.connect(DB.duckdb());\npath_or_name = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\n\n@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@collect\nend\n
2\u00d75 DataFrame\n Row \u2502 cyl     mpg       mpg_squared  mpg_rounded  mpg_efficiency \n     \u2502 Int64?  Float64?  Float64?     Float64?     String?        \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4   27.3444      747.719         27.0  efficient\n   2 \u2502      6   19.7333      389.404         20.0  moderate\n

"},{"location":"#what-if-we-wanted-to-pivot-the-result","title":"What if we wanted to pivot the result?","text":"

We cannot do this using TidierDB. However, we can call @pivot_longer() from TidierData after the result of the query has been instantiated as a DataFrame, like this:

@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@collect\n    @pivot_longer(everything(), names_to = \"variable\", values_to = \"value\")\nend\n
10\u00d72 DataFrame\n Row \u2502 variable        value     \n     \u2502 String          Any       \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 cyl             4\n   2 \u2502 cyl             6\n   3 \u2502 mpg             27.3444\n   4 \u2502 mpg             19.7333\n   5 \u2502 mpg_squared     747.719\n   6 \u2502 mpg_squared     389.404\n   7 \u2502 mpg_rounded     27.0\n   8 \u2502 mpg_rounded     20.0\n   9 \u2502 mpg_efficiency  efficient\n  10 \u2502 mpg_efficiency  moderate\n

"},{"location":"#what-sql-query-does-tidierdb-generate-for-a-given-piece-of-julia-code","title":"What SQL query does TidierDB generate for a given piece of Julia code?","text":"

We can replace DB.collect() with DB.@show_query to reveal the underlying SQL query being generated by TidierDB. To handle complex queries, TidierDB makes heavy use of Common Table Expressions (CTE), which are a useful tool to organize long queries.

@chain DB.db_table(db, path_or_name) begin\n    DB.@filter(!starts_with(model, \"M\"))\n    DB.@group_by(cyl)\n    DB.@summarize(mpg = mean(mpg))\n    DB.@mutate(mpg_squared = mpg^2, \n               mpg_rounded = round(mpg), \n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))            \n    DB.@filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    DB.@arrange(desc(mpg_rounded))\n    DB.@show_query\nend\n
WITH cte_1 AS (\nSELECT *\n        FROM mtcars\n        WHERE NOT (starts_with(model, 'M'))),\ncte_2 AS (\nSELECT cyl, AVG(mpg) AS mpg\n        FROM cte_1\n        GROUP BY cyl),\ncte_3 AS (\nSELECT  cyl, mpg, POWER(mpg, 2) AS mpg_squared, ROUND(mpg) AS mpg_rounded, CASE WHEN mpg >= POWER(cyl, 2) THEN 'efficient' WHEN mpg < 15.2 THEN 'inefficient' ELSE 'moderate' END AS mpg_efficiency\n        FROM cte_2 ),\ncte_4 AS (\nSELECT *\n        FROM cte_3\n        WHERE mpg_efficiency in ('moderate', 'efficient'))  \nSELECT *\n        FROM cte_4  \n        ORDER BY mpg_rounded DESC\n

"},{"location":"#tidierdb-is-already-quite-fully-featured-supporting-advanced-tidierdata-functions-like-across-for-multi-column-selection","title":"TidierDB is already quite fully-featured, supporting advanced TidierData functions like across() for multi-column selection.","text":"
@chain DB.db_table(db, path_or_name) begin\n    DB.@group_by(cyl)\n    DB.@summarize(across((starts_with(\"a\"), ends_with(\"s\")), (mean, sum)))\n    DB.@collect\nend\n
3\u00d75 DataFrame\n Row \u2502 cyl     am_mean   vs_mean   am_sum   vs_sum  \n     \u2502 Int64?  Float64?  Float64?  Int128?  Int128? \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4  0.727273  0.909091        8       10\n   2 \u2502      6  0.428571  0.571429        3        4\n   3 \u2502      8  0.142857  0.0             2        0\n

Bang bang !! interpolation for columns and values is also supported.

There are a few subtle but important differences from Tidier.jl outlined here.

"},{"location":"#missing-a-function-or-backend","title":"Missing a function or backend?","text":"

You can use any existing SQL function within @mutate with the correct SQL syntax and it should just work.

But if you run into problems please open an issue, and we will be happy to take a look!

"},{"location":"reference/","title":"Reference","text":""},{"location":"reference/#index","title":"Index","text":"
  • TidierDB.connect
  • TidierDB.copy_to
  • TidierDB.db_table
  • TidierDB.show_tables
  • TidierDB.warnings
  • TidierDB.@anti_join
  • TidierDB.@arrange
  • TidierDB.@collect
  • TidierDB.@count
  • TidierDB.@create_view
  • TidierDB.@distinct
  • TidierDB.@filter
  • TidierDB.@full_join
  • TidierDB.@group_by
  • TidierDB.@head
  • TidierDB.@inner_join
  • TidierDB.@left_join
  • TidierDB.@mutate
  • TidierDB.@rename
  • TidierDB.@right_join
  • TidierDB.@select
  • TidierDB.@semi_join
  • TidierDB.@slice_max
  • TidierDB.@slice_min
  • TidierDB.@slice_sample
  • TidierDB.@summarise
  • TidierDB.@summarize
  • TidierDB.@union
  • TidierDB.@union_all
  • TidierDB.@window_frame
  • TidierDB.@window_order
"},{"location":"reference/#reference-exported-functions","title":"Reference - Exported functions","text":"

# TidierDB.connect \u2014 Method.

connect(backend; kwargs...)\n

This function establishes a database connection based on the specified backend and connection parameters and sets the SQL mode

Arguments

  • backend: type specifying the database backend to connect to. Supported backends are:

    • duckdb(), sqlite()(SQLite), mssql(), mysql()(for MariaDB and MySQL), clickhouse(), postgres()
    • kwargs: Keyword arguments specifying the connection parameters for the selected backend. The required parameters vary depending on the backend:

    • MySQL:

      • host: The host name or IP address of the MySQL server. Default is \"localhost\".
      • user: The username for authentication. Default is an empty string.
      • password: The password for authentication.
      • db: The name of the database to connect to (optional).
      • port: The port number of the MySQL server (optional).

Returns

  • A database connection object based on the selected backend.

Examples

# Connect to MySQL\n# conn = connect(mysql(); host=\"localhost\", user=\"root\", password=\"password\", db=\"mydb\")\n# Connect to PostgreSQL using LibPQ\n# conn = connect(postgres(); host=\"localhost\", dbname=\"mydb\", user=\"postgres\", password=\"password\")\n# Connect to ClickHouse\n# conn = connect(clickhouse(); host=\"localhost\", port=9000, database=\"mydb\", user=\"default\", password=\"\")\n# Connect to SQLite\n# conn = connect(sqlite())\n# Connect to Google Big Query\n# conn = connect(gbq(), \"json_user_key_path\", \"location\")\n# Connect to Snowflake\n# conn = connect(snowflake(), \"ac_id\", \"token\", \"Database_name\", \"Schema_name\", \"warehouse_name\")\n# Connect to Microsoft SQL Server\n# conn = connect(mssql(), \"DRIVER={ODBC Driver 18 for SQL Server};SERVER=host,1433;UID=sa;PWD=YourPassword;Encrypt=no;TrustServerCertificate=yes\")\n# Connect to DuckDB\n# connect to Google Cloud via DuckDB\n# google_db = connect(duckdb(), :gbq, access_key=\"string\", secret_key=\"string\")\n# Connect to AWS via DuckDB\n# aws_db = connect2(duckdb(), :aws, aws_access_key_id=get(ENV, \"AWS_ACCESS_KEY_ID\", \"access_key\"), aws_secret_access_key=get(ENV, \"AWS_SECRET_ACCESS_KEY\", \"secret_access key\"), aws_region=get(ENV, \"AWS_DEFAULT_REGION\", \"us-east-1\"))\n# Connect to MotherDuck\n# connect(duckdb(), \"\"md://...\"\") for first connection, vs connect(duckdb(), \"md:\") for reconnection\n# Connect to exisiting database file\n# connect(duckdb(), \"path/to/database.duckdb\")\n# Open an in-memory database\njulia> db = connect(duckdb())\nDuckDB.Connection(\":memory:\")\n

source

# TidierDB.copy_to \u2014 Method.

   copy_to(conn, df_or_path, \"name\")\n

Allows user to copy a df to the database connection. Currently supports DuckDB, SQLite, MySql

Arguments

-conn: the database connection -df: dataframe to be copied or path to serve as source. With DuckDB, path supports .csv, .json, .parquet to be used without copying intermediary df. -name: name as string for the database to be used

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"test\");\n

source

# TidierDB.db_table \u2014 Function.

db_table(database, table_name, athena_params, delta = false, iceberg = false)\n

db_table starts the underlying SQL query struct, adding the metadata and table. If paths are passed directly to db*table instead of a name it will not copy it to memory, but rather ready directly from the file. db*tableonly supports direct file paths to a table. It does not support database file paths such asdbname.duckdbordbname.sqlite. Such files must be used withconnect first

Arguments

  • database: The Database or connection object
  • table_name: tablename as a string (name, local path, or URL). - CSV/TSV - Parquet - Json - Iceberg - Delta - S3 tables from AWS or Google Cloud

    • DuckDB and ClickHouse support vectors of paths and URLs.
    • DuckDB and ClickHouse also support use of * wildcards to read all files of a type in a location such as:
    • db_table(db, \"Path/to/testing_files/*.parquet\")
    • delta: must be true to read delta files
    • iceberg: must be true to read iceberg finalize_ctes

Example

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> db_table(db, \"df_mem\")\nTidierDB.SQLQuery(\"\", \"df_mem\", \"\", \"\", \"\", \"\", \"\", \"\", false, false, 4\u00d74 DataFrame\n Row \u2502 name     type     current_selxn  table_name \n     \u2502 String?  String?  Int64          String     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 id       VARCHAR              1  df_mem\n   2 \u2502 groups   VARCHAR              1  df_mem\n   3 \u2502 value    BIGINT               1  df_mem\n   4 \u2502 percent  DOUBLE               1  df_mem, false, DuckDB.Connection(\":memory:\"), TidierDB.CTE[], 0, nothing)\n

source

# TidierDB.show_tables \u2014 Method.

show_tables(con; GBQ_datasetname)\n

Shows tables available in database. currently supports DuckDB, databricks, Snowflake, GBQ, SQLite, LibPQ

Arguments

  • con : connection to backend
  • GBQ_datasetname : string of dataset name

Examples

julia> db = connect(duckdb());\n\njulia> show_tables(db);\n

source

# TidierDB.warnings \u2014 Method.

warnings(show::Bool)\n

Sets the global warning flag to the specified boolean value.

Arguments

  • flag::Bool: A boolean value to set the warning flag. If true, warnings will be enabled; if false, warnings will be disabled.

Default Behavior

By default, the warning flag is set to false, meaning that warnings are disabled unless explicitly enabled by setting this function with true.

Example

julia> warnings(true);\n

source

# TidierDB.@anti_join \u2014 Macro.

@anti_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an anti join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n        @anti_join(\"df_join\", id = id2)\n        @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AB      aa          2      0.2\n   2 \u2502 AD      aa          4      0.4\n   3 \u2502 AF      aa          1      0.6\n   4 \u2502 AH      aa          3      0.8\n   5 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@arrange \u2014 Macro.

@arrange(sql_query, columns...)\n

Order SQL table rows based on specified column(s).

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to order the rows by. Can include multiple columns for nested sorting. Wrap column name with desc() for descending order.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @arrange(value, desc(percent))\n         @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AF      aa          1      0.6\n   2 \u2502 AA      bb          1      0.1\n   3 \u2502 AG      bb          2      0.7\n   4 \u2502 AB      aa          2      0.2\n   5 \u2502 AH      aa          3      0.8\n   6 \u2502 AC      bb          3      0.3\n   7 \u2502 AI      bb          4      0.9\n   8 \u2502 AD      aa          4      0.4\n   9 \u2502 AJ      aa          5      1.0\n  10 \u2502 AE      bb          5      0.5\n

source

# TidierDB.@collect \u2014 Macro.

@collect(sql_query, stream = false)\n

db_table starts the underlying SQL query struct, adding the metadata and table.

Arguments

  • sql_query: The SQL query to operate on.
  • stream: optional streaming for query/execution of results when using duck db. Defaults to false

Example

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @collect db_table(db, \"df_mem\")\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AB      aa          2      0.2\n   3 \u2502 AC      bb          3      0.3\n   4 \u2502 AD      aa          4      0.4\n   5 \u2502 AE      bb          5      0.5\n   6 \u2502 AF      aa          1      0.6\n   7 \u2502 AG      bb          2      0.7\n   8 \u2502 AH      aa          3      0.8\n   9 \u2502 AI      bb          4      0.9\n  10 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@count \u2014 Macro.

@count(sql_query, columns...)\n

Count the number of rows grouped by specified column(s).

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to group by before counting. If no columns are specified, counts all rows in the query.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @count(groups)\n         @arrange(groups)\n         @collect\n       end\n2\u00d72 DataFrame\n Row \u2502 groups  count \n     \u2502 String  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          5\n   2 \u2502 bb          5\n

source

# TidierDB.@create_view \u2014 Macro.

@view(sql_query, name, replace = true)\n

Create a view from a SQL query. Currently supports DuckDB, MySQL, GBQ, Postgres

Arguments

  • sql_query: The SQL query to create a view from.
  • name: The name of the view to create.
  • replace: defaults to true if view should be replaced

Examples

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> copy_to(db, df, \"df1\");\n\njulia> @chain db_table(db, \"df1\") @create_view(viewer);\n\njulia> db_table(db, \"viewer\")\nTidierDB.SQLQuery(\"\", \"viewer\", \"\", \"\", \"\", \"\", \"\", \"\", false, false, 2\u00d74 DataFrame\n Row \u2502 name    type    current_selxn  table_name \n     \u2502 String  String  Int64          String     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 id      BIGINT              1  viewer\n   2 \u2502 value   BIGINT              1  viewer, false, DuckDB.DB(\":memory:\"), TidierDB.CTE[], 0, nothing, \"\", \"\", 0)\n

source

# TidierDB.@distinct \u2014 Macro.

@distinct(sql_query, columns...)\n

Select distinct rows based on specified column(s). Distinct works differently in TidierData vs SQL and therefore TidierDB. Distinct will also select only the only columns it is given (or all if given none)

Arguments

sql_query: The SQL query to operate on. columns: Columns to determine uniqueness. If no columns are specified, all columns are used to identify distinct rows.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @distinct(value)\n         @arrange(value)\n         @collect\n       end\n5\u00d71 DataFrame\n Row \u2502 value \n     \u2502 Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1\n   2 \u2502     2\n   3 \u2502     3\n   4 \u2502     4\n   5 \u2502     5\n\njulia> @chain db_table(db, :df_mem) begin\n         @distinct\n         @arrange(id)\n         @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AB      aa          2      0.2\n   3 \u2502 AC      bb          3      0.3\n   4 \u2502 AD      aa          4      0.4\n   5 \u2502 AE      bb          5      0.5\n   6 \u2502 AF      aa          1      0.6\n   7 \u2502 AG      bb          2      0.7\n   8 \u2502 AH      aa          3      0.8\n   9 \u2502 AI      bb          4      0.9\n  10 \u2502 AJ      aa          5      1.0\n

source

# TidierDB.@filter \u2014 Macro.

@filter(sql_query, conditions...)\n

Filter rows in a SQL table based on specified conditions.

Arguments

  • sql_query: The SQL query to filter rows from.
  • conditions: Expressions specifying the conditions that rows must satisfy to be included in the output. Rows for which the expression evaluates to true will be included in the result. Multiple conditions can be combined using logical operators (&&, ||). It will automatically detect whether the conditions belong in WHERE vs HAVING.

                 Temporarily, it is best to use begin and end when filtering multiple conditions. (ex 2 below)\n

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @filter(percent > .5)\n         @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AF      aa          1      0.6\n   2 \u2502 AG      bb          2      0.7\n   3 \u2502 AH      aa          3      0.8\n   4 \u2502 AI      bb          4      0.9\n   5 \u2502 AJ      aa          5      1.0\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(mean = mean(percent))\n         @filter begin \n           groups == \"bb\" || # logical operators can still be used like this\n           mean > .5\n         end\n         @arrange(groups)\n         @collect\n       end\n2\u00d72 DataFrame\n Row \u2502 groups  mean    \n     \u2502 String  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          0.6\n   2 \u2502 bb          0.5\n

source

# TidierDB.@full_join \u2014 Macro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an full join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @full_join((@chain db_table(db, \"df_join\") @filter(score > 70)), id)\n         #@aside @show_query _\n         @collect\n       end\n11\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id_1     category  score   \n     \u2502 String?  String?  Int64?   Float64?   String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA       X              88\n   2 \u2502 AC       bb             3        0.3  AC       Y              92\n   3 \u2502 AE       bb             5        0.5  AE       X              77\n   4 \u2502 AG       bb             2        0.7  AG       Y              83\n   5 \u2502 AI       bb             4        0.9  AI       X              95\n   6 \u2502 AB       aa             2        0.2  missing  missing   missing \n   7 \u2502 AD       aa             4        0.4  missing  missing   missing \n   8 \u2502 AF       aa             1        0.6  missing  missing   missing \n   9 \u2502 AH       aa             3        0.8  missing  missing   missing \n  10 \u2502 AJ       aa             5        1.0  missing  missing   missing \n  11 \u2502 missing  missing  missing  missing    AM       X              74\n

source

# TidierDB.@group_by \u2014 Macro.

@group_by(sql_query, columns...)\n

Group SQL table rows by specified column(s). If grouping is performed as a terminal operation without a subsequent mutatation or summarization (as in the example below), then the resulting data frame will be ungrouped when @collect is applied.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions specifying the columns to group by. Columns can be specified by name.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @arrange(groups)\n         @collect\n       end\n2\u00d71 DataFrame\n Row \u2502 groups \n     \u2502 String \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa\n   2 \u2502 bb\n

source

# TidierDB.@head \u2014 Macro.

@head(sql_query, value)\n

Limit SQL table number of rows returned based on specified value. LIMIT in SQL

Arguments

  • sql_query: The SQL query to operate on.
  • value: Number to limit how many rows are returned. If left empty, it will default to 6 rows

Examples

julia> db = connect(duckdb());\n\njulia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> copy_to(db, df, \"df_mem\");                     \n\njulia> @chain db_table(db, :df_mem) begin\n        @head(1) ## supports expressions ie `3-2` would return the same df below\n        @collect\n       end\n1\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n

source

# TidierDB.@inner_join \u2014 Macro.

@inner_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an inner join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @inner_join(\"df_join\", \"id\" = id2)\n         @collect\n       end\n5\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2     category  score \n     \u2502 String  String  Int64  Float64  String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA      X            88\n   2 \u2502 AC      bb          3      0.3  AC      Y            92\n   3 \u2502 AE      bb          5      0.5  AE      X            77\n   4 \u2502 AG      bb          2      0.7  AG      Y            83\n   5 \u2502 AI      bb          4      0.9  AI      X            95\n

source

# TidierDB.@left_join \u2014 Macro.

@left_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform a left join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, \"df_mem\") begin\n         @left_join(\"df_join\", \"id\" = \"id2\" )\n         @collect\n       end\n10\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2      category  score   \n     \u2502 String  String  Int64  Float64  String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA       X              88\n   2 \u2502 AC      bb          3      0.3  AC       Y              92\n   3 \u2502 AE      bb          5      0.5  AE       X              77\n   4 \u2502 AG      bb          2      0.7  AG       Y              83\n   5 \u2502 AI      bb          4      0.9  AI       X              95\n   6 \u2502 AB      aa          2      0.2  missing  missing   missing \n   7 \u2502 AD      aa          4      0.4  missing  missing   missing \n   8 \u2502 AF      aa          1      0.6  missing  missing   missing \n   9 \u2502 AH      aa          3      0.8  missing  missing   missing \n  10 \u2502 AJ      aa          5      1.0  missing  missing   missing \n\njulia> query = @chain db_table(db, \"df_join\") begin\n                  @filter(score > 85) # only show scores above 85 in joining table\n                end;\n\njulia> @chain db_table(db, \"df_mem\") begin\n         @left_join(t(query), id = id2)\n         @collect\n       end\n10\u00d77 DataFrame\n Row \u2502 id      groups  value  percent  id2      category  score   \n     \u2502 String  String  Int64  Float64  String?  String?   Int64?  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1  AA       X              88\n   2 \u2502 AC      bb          3      0.3  AC       Y              92\n   3 \u2502 AI      bb          4      0.9  AI       X              95\n   4 \u2502 AB      aa          2      0.2  missing  missing   missing \n   5 \u2502 AD      aa          4      0.4  missing  missing   missing \n   6 \u2502 AE      bb          5      0.5  missing  missing   missing \n   7 \u2502 AF      aa          1      0.6  missing  missing   missing \n   8 \u2502 AG      bb          2      0.7  missing  missing   missing \n   9 \u2502 AH      aa          3      0.8  missing  missing   missing \n  10 \u2502 AJ      aa          5      1.0  missing  missing   missing \n

source

# TidierDB.@mutate \u2014 Macro.

@mutate(sql_query, exprs...; _by)\n

Mutate SQL table rows by adding new columns or modifying existing ones.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions for mutating the table. New columns can be added or existing columns modified using column_name = expression syntax, where expression can involve existing columns.
  • _by: optional argument that supports single column names, or vectors of columns to allow for grouping for the transformation in the macro call

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @mutate(value = value * 4, new_col = percent^2)\n         @collect\n       end\n10\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  new_col \n     \u2502 String  String  Int64  Float64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          4      0.1     0.01\n   2 \u2502 AB      aa          8      0.2     0.04\n   3 \u2502 AC      bb         12      0.3     0.09\n   4 \u2502 AD      aa         16      0.4     0.16\n   5 \u2502 AE      bb         20      0.5     0.25\n   6 \u2502 AF      aa          4      0.6     0.36\n   7 \u2502 AG      bb          8      0.7     0.49\n   8 \u2502 AH      aa         12      0.8     0.64\n   9 \u2502 AI      bb         16      0.9     0.81\n  10 \u2502 AJ      aa         20      1.0     1.0\n\njulia> @chain db_table(db, :df_mem) begin\n         @mutate(max = maximum(percent), sum = sum(percent), _by = groups)\n         @collect\n       end\n10\u00d76 DataFrame\n Row \u2502 id      groups  value  percent  max      sum     \n     \u2502 String  String  Int64  Float64  Float64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AB      aa          2      0.2      1.0      3.0\n   2 \u2502 AD      aa          4      0.4      1.0      3.0\n   3 \u2502 AF      aa          1      0.6      1.0      3.0\n   4 \u2502 AH      aa          3      0.8      1.0      3.0\n   5 \u2502 AJ      aa          5      1.0      1.0      3.0\n   6 \u2502 AA      bb          1      0.1      0.9      2.5\n   7 \u2502 AC      bb          3      0.3      0.9      2.5\n   8 \u2502 AE      bb          5      0.5      0.9      2.5\n   9 \u2502 AG      bb          2      0.7      0.9      2.5\n  10 \u2502 AI      bb          4      0.9      0.9      2.5\n

source

# TidierDB.@rename \u2014 Macro.

@rename(sql_query, renamings...)\n

Rename one or more columns in a SQL query.

Arguments

-sql_query: The SQL query to operate on. -renamings: One or more pairs of old and new column names, specified as new name = old name

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n       @rename(new_name = percent)\n       @collect\n       end\n10\u00d74 DataFrame\n Row \u2502 id      groups  value  new_name \n     \u2502 String  String  Int64  Float64  \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1       0.1\n   2 \u2502 AB      aa          2       0.2\n   3 \u2502 AC      bb          3       0.3\n   4 \u2502 AD      aa          4       0.4\n   5 \u2502 AE      bb          5       0.5\n   6 \u2502 AF      aa          1       0.6\n   7 \u2502 AG      bb          2       0.7\n   8 \u2502 AH      aa          3       0.8\n   9 \u2502 AI      bb          4       0.9\n  10 \u2502 AJ      aa          5       1.0\n

source

# TidierDB.@right_join \u2014 Macro.

@right_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform a right join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @right_join(\"df_join\", id = id2)\n         @collect\n       end\n7\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id2     category  score \n     \u2502 String?  String?  Int64?   Float64?   String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA      X            88\n   2 \u2502 AC       bb             3        0.3  AC      Y            92\n   3 \u2502 AE       bb             5        0.5  AE      X            77\n   4 \u2502 AG       bb             2        0.7  AG      Y            83\n   5 \u2502 AI       bb             4        0.9  AI      X            95\n   6 \u2502 missing  missing  missing  missing    AK      Y            68\n   7 \u2502 missing  missing  missing  missing    AM      X            74\n\njulia> query = @chain db_table(db, \"df_join\") begin\n                  @filter(score >= 74) # only show scores above 85 in joining table\n                end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @right_join(t(query), id = id2)\n         @collect\n       end\n6\u00d77 DataFrame\n Row \u2502 id       groups   value    percent    id2     category  score \n     \u2502 String?  String?  Int64?   Float64?   String  String    Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA       bb             1        0.1  AA      X            88\n   2 \u2502 AC       bb             3        0.3  AC      Y            92\n   3 \u2502 AE       bb             5        0.5  AE      X            77\n   4 \u2502 AG       bb             2        0.7  AG      Y            83\n   5 \u2502 AI       bb             4        0.9  AI      X            95\n   6 \u2502 missing  missing  missing  missing    AM      X            74\n

source

# TidierDB.@select \u2014 Macro.

@select(sql_query, columns)\n

Select specified columns from a SQL table.

Arguments

  • sql_query: The SQL query to select columns from.
  • columns: Expressions specifying the columns to select. Columns can be specified by name, and new columns can be created with expressions using existing column values.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> df_mem = db_table(db, :df_mem);\n\njulia> @chain t(df_mem) begin\n         @select(groups:percent)\n         @collect\n       end\n10\u00d73 DataFrame\n Row \u2502 groups  value  percent \n     \u2502 String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 bb          1      0.1\n   2 \u2502 aa          2      0.2\n   3 \u2502 bb          3      0.3\n   4 \u2502 aa          4      0.4\n   5 \u2502 bb          5      0.5\n   6 \u2502 aa          1      0.6\n   7 \u2502 bb          2      0.7\n   8 \u2502 aa          3      0.8\n   9 \u2502 bb          4      0.9\n  10 \u2502 aa          5      1.0\n\njulia> @chain t(df_mem) begin\n         @select(contains(\"e\"))\n         @collect\n       end\n10\u00d72 DataFrame\n Row \u2502 value  percent \n     \u2502 Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1      0.1\n   2 \u2502     2      0.2\n   3 \u2502     3      0.3\n   4 \u2502     4      0.4\n   5 \u2502     5      0.5\n   6 \u2502     1      0.6\n   7 \u2502     2      0.7\n   8 \u2502     3      0.8\n   9 \u2502     4      0.9\n  10 \u2502     5      1.0\n

source

# TidierDB.@semi_join \u2014 Macro.

@semi_join(sql_query, join_table, orignal_table_col = new_table_col)\n

Perform an semi join between two SQL queries based on a specified condition. This syntax here is slightly different than TidierData.jl, however, because SQL does not drop the joining column, for the metadata storage, it is preferrable for the names to be different

Arguments

  • sql_query: The primary SQL query to operate on.
  • join_table: The secondary SQL table to join with the primary query table.
  • orignal_table_col: Column from the original table that matches for join. Accepts cols as bare column names or strings
  • new_table_col: Column from the new table that matches for join. Accepts cols as bare column names or strings

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> df2 = DataFrame(id2 = [\"AA\", \"AC\", \"AE\", \"AG\", \"AI\", \"AK\", \"AM\"],\n                category = [\"X\", \"Y\", \"X\", \"Y\", \"X\", \"Y\", \"X\"],\n                score = [88, 92, 77, 83, 95, 68, 74]);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> copy_to(db, df2, \"df_join\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @semi_join(\"df_join\", id = id2)\n         @collect\n       end\n5\u00d74 DataFrame\n Row \u2502 id      groups  value  percent \n     \u2502 String  String  Int64  Float64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1\n   2 \u2502 AC      bb          3      0.3\n   3 \u2502 AE      bb          5      0.5\n   4 \u2502 AG      bb          2      0.7\n   5 \u2502 AI      bb          4      0.9\n

source

# TidierDB.@slice_max \u2014 Macro.

@slice_max(sql_query, column, n = 1)\n

Select rows with the largest values in specified column. This will always return ties.

Arguments

  • sql_query: The SQL query to operate on.
  • column: Column to identify the smallest values.
  • n: The number of rows to select with the largest values for each specified column. Default is 1, which selects the row with the smallest value.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_max(value, n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @slice_max(value)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  rank_col \n     \u2502 String  String  Int64  Float64  Int64    \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AE      bb          5      0.5         1\n   2 \u2502 AJ      aa          5      1.0         1\n

source

# TidierDB.@slice_min \u2014 Macro.

@slice_min(sql_query, column, n = 1)\n

Select rows with the smallest values in specified column. This will always return ties.

Arguments

  • sql_query: The SQL query to operate on.
  • column: Column to identify the smallest values.
  • n: The number of rows to select with the smallest values for each specified column. Default is 1, which selects the row with the smallest value.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_min(value, n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n         @slice_min(value)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 id      groups  value  percent  rank_col \n     \u2502 String  String  Int64  Float64  Int64    \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 AA      bb          1      0.1         1\n   2 \u2502 AF      aa          1      0.6         1\n

source

# TidierDB.@slice_sample \u2014 Macro.

@slice_sample(sql_query, n)\n

Randomly select a specified number of rows from a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • n: The number of rows to randomly select.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @slice_sample(n = 2)\n         @collect\n       end;\n\njulia> @chain db_table(db, :df_mem) begin\n       @slice_sample()\n       @collect\n       end;\n

source

# TidierDB.@summarise \u2014 Macro.

   @summarise(sql_query, exprs...)\n

Aggregate and summarize specified columns of a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions defining the aggregation and summarization operations. These can specify simple aggregations like mean, sum, and count, or more complex expressions involving existing column values.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(across((value:percent), (mean, sum)))\n         @arrange(groups)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 groups  value_mean  percent_mean  value_sum  percent_sum \n     \u2502 String  Float64     Float64       Int128     Float64     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa             3.0           0.6         15          3.0\n   2 \u2502 bb             3.0           0.5         15          2.5\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(test = sum(percent), n = n())\n         @arrange(groups)\n         @collect\n       end\n2\u00d73 DataFrame\n Row \u2502 groups  test     n     \n     \u2502 String  Float64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          3.0      5\n   2 \u2502 bb          2.5      5\n

source

# TidierDB.@summarize \u2014 Macro.

   @summarize(sql_query, exprs...; _by)\n

Aggregate and summarize specified columns of a SQL table.

Arguments

  • sql_query: The SQL query to operate on.
  • exprs: Expressions defining the aggregation and summarization operations. These can specify simple aggregations like mean, sum, and count, or more complex expressions involving existing column values.
  • _by: optional argument that supports single column names, or vectors of columns to allow for grouping for the aggregatation in the macro call

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(across((ends_with(\"e\"), starts_with(\"p\")), (mean, sum)))\n         @arrange(groups)\n         @collect\n       end\n2\u00d75 DataFrame\n Row \u2502 groups  value_mean  percent_mean  value_sum  percent_sum \n     \u2502 String  Float64     Float64       Int128     Float64     \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa             3.0           0.6         15          3.0\n   2 \u2502 bb             3.0           0.5         15          2.5\n\njulia> @chain db_table(db, :df_mem) begin\n         @group_by(groups)\n         @summarise(test = sum(percent), n = n())\n         @arrange(groups)\n         @collect\n       end\n2\u00d73 DataFrame\n Row \u2502 groups  test     n     \n     \u2502 String  Float64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          3.0      5\n   2 \u2502 bb          2.5      5\n\njulia> @chain db_table(db, :df_mem) begin\n                @summarise(test = sum(percent), n = n(), _by = groups)\n                @arrange(groups)\n                @collect\n              end\n2\u00d73 DataFrame\n Row \u2502 groups  test     n     \n     \u2502 String  Float64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 aa          3.0      5\n   2 \u2502 bb          2.5      5\n

source

# TidierDB.@union \u2014 Macro.

@union(sql_query1, sql_query2)\n

Combine two SQL queries using the UNION operator.

Arguments

  • sql_query1: The first SQL query to combine.
  • sql_query2: The second SQL query to combine.

Returns

  • A lazy query of all distinct rows in the second query bound to the first

Examples

julia> db = connect(duckdb());\n\njulia> df1 = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> df2 = DataFrame(id = [4, 5, 6], value = [40, 50, 60]);\n\njulia> copy_to(db, df1, \"df1\");\n\njulia> copy_to(db, df2, \"df2\");\n\njulia> df1_table = db_table(db, \"df1\");\n\njulia> df2_table = db_table(db, \"df2\");\n\njulia> @chain t(df1_table) @union(df2_table) @collect\n6\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     4     40\n   5 \u2502     5     50\n   6 \u2502     6     60\n\njulia> query = @chain t(df2_table) @filter(value == 50);\n\njulia> @chain t(df1_table) begin \n        @union(t(query))\n        @collect\n       end\n4\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     5     50\n\njulia> @chain t(df1_table) begin \n        @union(t(df1_table))\n        @collect\n       end\n3\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n

source

# TidierDB.@window_frame \u2014 Macro.

@window_frame(sql_query, args...)\n

Define the window frame for window functions in a SQL query, specifying the range of rows to include in the calculation relative to the current row.

Arguments

  • sqlquery::SQLQuery: The SQLQuery instance to which the window frame will be applied.
  • args...: A variable number of arguments specifying the frame boundaries. These can be:

    • from: The starting point of the frame. Can be a positive or negative integer, 0 or empty. When empty, it will use UNBOUNDED
    • to: The ending point of the frame. Can be a positive or negative integer, 0 or empty. When empty, it will use UNBOUNDED
    • if only one integer is provided without specifying to or from it will default to from, and to will be UNBOUNDED.
    • if no arguments are given, both will be UNBOUNDED

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> df_mem = db_table(db, :df_mem);\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame(3)\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame(-3, 3)\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n       # @window_frame(to = -3)\n        @mutate(avg = mean(percent))\n        #@show_query\n        @collect\n       end;\n\njulia> @chain t(df_mem) begin\n        @group_by groups\n        @window_frame()\n        @mutate(avg = mean(percent))\n        #@show_query\n       end;\n

source

# TidierDB.@window_order \u2014 Macro.

   @window_order(sql_query, columns...)\n

Specify the order of rows for window functions within a SQL query.

Arguments

  • sql_query: The SQL query to operate on.
  • columns: Columns to order the rows by for the window function. Can include multiple columns for nested sorting. Prepend a column name with - for descending order.

Examples

julia> df = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9], \n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10], \n                        value = repeat(1:5, 2), \n                        percent = 0.1:0.1:1.0);\n\njulia> db = connect(duckdb());\n\njulia> copy_to(db, df, \"df_mem\");\n\njulia> @chain db_table(db, :df_mem) begin\n        @group_by groups\n        @window_frame(3)\n        @window_order(desc(percent))\n        @mutate(avg = mean(value))\n       #@show_query \n       end;\n

source

"},{"location":"reference/#reference-internal-functions","title":"Reference - Internal functions","text":"

# TidierDB.@union_all \u2014 Macro.

@union(sql_query1, sql_query2)\n

Combine two SQL queries using the UNION ALL operator.

Arguments

  • sql_query1: The first SQL query to combine.
  • sql_query2: The second SQL query to combine.

Returns

  • A lazy query of all rows in the second query bound to the first

Examples

julia> db = connect(duckdb());\n\njulia> df1 = DataFrame(id = [1, 2, 3], value = [10, 20, 30]);\n\njulia> copy_to(db, df1, \"df1\");\n\njulia> df1_table = db_table(db, \"df1\");\n\njulia> @chain t(df1_table) @union_all(df1_table) @collect\n6\u00d72 DataFrame\n Row \u2502 id     value \n     \u2502 Int64  Int64 \n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1     10\n   2 \u2502     2     20\n   3 \u2502     3     30\n   4 \u2502     1     10\n   5 \u2502     2     20\n   6 \u2502     3     30\n

source

"},{"location":"examples/generated/UserGuide/Snowflake/","title":"Using Snowflake","text":"

Establishing a connection with the Snowflake SQL Rest API requires a OAuth token specific to the Role the user will use to query tables with.

"},{"location":"examples/generated/UserGuide/Snowflake/#connecting","title":"Connecting","text":"

Connection is established with the connect function as shown below. Connection requires 5 items as strings

  • Account Identifier
  • OAuth token
  • Database Name
  • Schema Name
  • Compute Warehouse name

Two things to note:

  • Your OAuth Token may frequently expire, which may require you to rerun your connection line.
  • Since each time db_table runs, it runs a query to pull the metadata, you may choose to use run db_table and save the results, and use these results withfrom_query()

    • This will reduce the number of queries to your database
    • Allow you to build a a SQL query and @show_query even if the OAuthtoken has expired. To @collect you will have to reconnect and rerun dbtable if your OAuth token has expired
set_sql_mode(snowflake())\nac_id = \"string_id\"\ntoken = \"OAuth_token_string\"\ncon = connect(:snowflake, ac_id, token, \"DEMODB\", \"PUBLIC\", \"COMPUTE_WH\")\n# After connection is established, a you may begin querying.\nstable_table_metadata = db_table(con, \"MTCARS\")\n@chain from_query(stable_table_metadata) begin\n   @select(WT)\n   @mutate(TEST = WT *2)\n   #@aside @show_query _\n   @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 WT       TEST\n     \u2502 Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502   2.62     5.24\n   2 \u2502   2.875    5.75\n   3 \u2502   2.32     4.64\n   4 \u2502   3.215    6.43\n  \u22ee  \u2502    \u22ee        \u22ee\n  29 \u2502   3.17     6.34\n  30 \u2502   2.77     5.54\n  31 \u2502   3.57     7.14\n  32 \u2502   2.78     5.56\n         24 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/athena/","title":"Using Athena","text":"

To use the Athena AWS backend with TidierDB, set up and a small syntax difference are covered here.

"},{"location":"examples/generated/UserGuide/athena/#connecting","title":"Connecting","text":"

Connection is established through AWS.jl as shwon below.

using TidierDB, AWS\nset_sql_mode(athena())\n# Replace your credentials as needed below\naws_access_key_id = get(ENV,\"AWS_ACCESS_KEY_ID\",\"key\")\naws_secret_access_key = get(ENV, \"AWS_SECRET_ACCESS_KEY\",\"secret_key\")\naws_region = get(ENV,\"AWS_DEFAULT_REGION\",\"region\")\n\nconst AWS_GLOBAL_CONFIG = Ref{AWS.AWSConfig}()\ncreds = AWSCredentials(aws_access_key_id, aws_secret_access_key)\n\nAWS_GLOBAL_CONFIG[] = AWS.global_aws_config(region=aws_region, creds=creds)\n\ncatalog = \"AwsDataCatalog\"\nworkgroup = \"primary\"\ndb = \"demodb\"\nall_results = true\nresults_per_increment = 10\nout_loc = \"s3://location/\"\n\nathena_params = Dict(\n    \"ResultConfiguration\" => Dict(\n        \"OutputLocation\" => out_loc\n    ),\n    \"QueryExecutionContext\" => Dict(\n        \"Database\" => db,\n        \"Catalog\" => catalog\n    ),\n    \"Workgroup\" => workgroup\n)\n

"},{"location":"examples/generated/UserGuide/athena/#db_table-differences","title":"db_table differences","text":"

There are two differences for db_table which are seen in the query below

  1. The table needs to be passed as a string in the format database.table, ie \"demodb.table_name
  2. db_table requires a third argument: the athena_params from above.

"},{"location":"examples/generated/UserGuide/athena/#leveraging-from_query-with-athena-to-reduce-number-of-queries","title":"Leveraging from_query with Athena to reduce number of queries","text":"

Throughout TidierDB, each time db_table is called, it queries the databases to get the metadata. Consider how AWS Athena logs queries, a user may want to reduce the number of queries. This can be done saving the results of db_table, and then using from_query with those results for furthe queries as shown below.

mtcars = db_table(AWS_GLOBAL_CONFIG[], \"demodb.mtcars\", athena_params)\n@chain from_query(mtcars) begin\n    @filter(cyl > 4)\n    @group_by(cyl)\n    @summarize(mpg = mean(mpg))\n   #@show_query\n    @collect\nend\n
2\u00d72 DataFrame\n Row \u2502 cyl    mpg\n     \u2502 Int64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     6  19.7429\n   2 \u2502     8  15.1\n

I would like to acknowledge the work of Manu Francis and this blog post, which helped guide this process

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/databricks/","title":"Using Databricks","text":"

Establishing a connection with the Databricks SQL Rest API requires a token.

"},{"location":"examples/generated/UserGuide/databricks/#connecting","title":"Connecting","text":"

Connection is established with the connect function as shown below. Connection requires 5 items as strings

  • Account Instance : how to find your instance
  • OAuth token : how to generate your token
  • Database Name
  • Schema Name
  • warehouse_id

One thing to note, Since each time db_table runs, it runs a query to pull the metadata, you may choose to use run db_table and save the results, and use these results with from_query(). This will reduce the number of queries to your database and is illustrated below.

set_sql_mode(databricks())\ninstance_id = \"string_id\"\ntoken \"string_token\"\nwarehouse_id = \"e673cd4f387f964a\"\ncon = connect(:databricks, instance_id, token, \"DEMODB\", \"PUBLIC\", warehouse_id)\n# After connection is established, a you may begin querying.\nstable_table_metadata = db_table(con, \"mtcars\")\n@chain from_query(stable_table_metadata) begin\n   @select(wt)\n   @mutate(test = wt *2)\n   #@aside @show_query _\n   @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 wt       test\n     \u2502 Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502   2.62     5.24\n   2 \u2502   2.875    5.75\n   3 \u2502   2.32     4.64\n   4 \u2502   3.215    6.43\n  \u22ee  \u2502    \u22ee        \u22ee\n  29 \u2502   3.17     6.34\n  30 \u2502   2.77     5.54\n  31 \u2502   3.57     7.14\n  32 \u2502   2.78     5.56\n         24 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/","title":"Reproduce a duckplyr example","text":"

In this example, we will reproduce a DuckDB and duckplyr blog post example to demonstrate TidierDB's v0.5.0 capability.

The example by Hannes that is being reproduced is exploring Open Data from the New Zealand government that is ~ 1GB.

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#set-up","title":"Set up","text":"

First we will set up the local duckdb database and pull in the metadata for the files. Notice we are not reading this data into memory, only the paths and and column, and table names. To follow along, copy the set up code below after downloading the data, but add the directory to the local data.

import TidierDB as DB\ndb = DB.connect(DB.duckdb())\n\ndir = \"/Downloads/nzcensus/\"\ndata   = dir * \"Data8277.csv\"\nage    = dir * \"DimenLookupAge8277.csv\"\narea   = dir * \"DimenLookupArea8277.csv\"\nethnic = dir * \"DimenLookupEthnic8277.csv\"\nsex    = dir * \"DimenLookupSex8277.csv\"\nyear   = dir * \"DimenLookupYear8277.csv\"\n\ndata = DB.db_table(db, data);\nage = DB.db_table(db, age);\narea = DB.db_table(db, area);\nethnic = DB.db_table(db, ethnic);\nsex = DB.db_table(db, sex);\nyear = DB.db_table(db, year);\n

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#exploration","title":"Exploration","text":"

While this long chain could be broken up into multiple smaller chains, lets reproduce the duckplyr code from example and demonstrate how TidierDB also supports multiple joins after filtering, mutating, etc the joining tables. 6 different tables are being joined together through sequential inner joins.

@chain DB.t(data) begin\n  DB.@filter(str_detect(count, r\"^\\d+$\"))\n  DB.@mutate(count_ = as_integer(count))\n  DB.@filter(count_ > 0)\n  DB.@inner_join(\n    (@chain DB.t(age) begin\n    DB.@filter(str_detect(Description, r\"^\\d+ years$\"))\n    DB.@mutate(age_ = as_integer(str_remove(Code, \"years\"))) end),\n    Age = Code\n  )\n  DB.@inner_join((@chain DB.t(year) DB.@mutate(year_ = Description)), year = Code)\n  DB.@inner_join((@chain DB.t(area) begin\n    DB.@mutate(area_ = Description)\n    DB.@filter(!str_detect(area_, r\"^Total\"))\n  end)\n    , Area = Code)\n    DB.@inner_join((@chain DB.t(ethnic) begin\n      DB.@mutate(ethnic_ = Description)\n      DB.@filter(!str_detect( ethnic_, r\"^Total\",)) end), Ethnic = Code)\n  DB.@inner_join((@chain DB.t(sex) begin\n    DB.@mutate(sex_ = Description)\n    DB.@filter(!str_detect( sex_, r\"^Total\"))\n  end)\n   , Sex = Code)\n  DB.@inner_join((@chain DB.t(year) DB.@mutate(year_ = Description)), Year = Code)\n  @aside DB.@show_query _\n  DB.@create_view(joined_up)\nend;\n\n@chain DB.db_table(db, \"joined_up\") begin\n  DB.@filter begin\n    age_ >= 20\n    age_ <= 40\n    str_detect(area_, r\"^Auckland\")\n    year_ == \"2018\"\n    ethnic_ != \"European\"\n    end\n  DB.@group_by sex_\n  DB.@summarise(group_count = sum(count_))\n  DB.@collect\nend\n

"},{"location":"examples/generated/UserGuide/duckplyr_reprex/#results","title":"Results","text":"

When we collect this to a local dataframe, we can see that the results match the duckplyr/DuckDB example.

2\u00d72 DataFrame\n Row \u2502 sex_    group_count\n     \u2502 String  Int128\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Female       398556\n   2 \u2502 Male         397326\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/ex_joining/","title":"Joining Tables","text":"

This page will illustrate how to join different tables in TidierDB. The examples will use the mtcars dataset and a synthetic dataset called mt2 hosted on a personal MotherDuck instance. Examples will cover how to join tables with different schemas in different databases, and how to write queries on tables and then join them together, and how to do this by levaraging views.

"},{"location":"examples/generated/UserGuide/ex_joining/#setup","title":"Setup","text":"
using TidierDB\ndb = connect(duckdb(), \"md:\")\n\nmtcars = db_table(db, \"my_db.mtcars\")\nmt2 = db_table(db, \"ducks_db.mt2\")\n
"},{"location":"examples/generated/UserGuide/ex_joining/#wrangle-tables-and-self-join","title":"Wrangle tables and self join","text":"
query = @chain t(mtcars) begin\n    @group_by cyl\n    @summarize begin\n        across(mpg, (mean, minimum, maximum))\n        num_cars = n()\n        end\n    @mutate begin\n        efficiency = case_when(\n            mpg_mean >= 25, \"High\",\n            mpg_mean >= 15, \"Moderate\",\n            \"Low\" )\n      end\nend;\n\nquery2 = @chain t(mtcars) @filter(mpg>20) @mutate(mpg = mpg *4);\n\n@chain t(query) begin\n    @left_join(t(query2), cyl, cyl)\n    @group_by(efficiency)\n    @summarize(avg_mean = mean(mpg))\n    @mutate(mean = avg_mean / 4 )\n    @aside @show_query _\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n
"},{"location":"examples/generated/UserGuide/ex_joining/#different-schemas","title":"Different schemas","text":"

To connect to a table in a different schema, prefix it with a dot. For example, \"schemaname.tablename\". In this query, we are also filtering out cars that contain \"M\" in the name from the mt2 table before joining.

other_db = @chain db_table(db, \"ducks_db.mt2\") @filter(!str_detect(car, \"M\"))\n@chain t(mtcars) begin\n    @left_join(t(other_db), car, model)\n    @select(car, model)\n    @head(5)\n    @collect\nend\n
5\u00d72 DataFrame\n Row \u2502 car                model\n     \u2502 String             String\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Datsun 710         Datsun 710\n   2 \u2502 Hornet 4 Drive     Hornet 4 Drive\n   3 \u2502 Hornet Sportabout  Hornet Sportabout\n   4 \u2502 Valiant            Valiant\n   5 \u2502 Duster 360         Duster 360\n

To join directly to the table, you can use the @left_join macro with the table name as a string.

@chain t(mtcars) begin\n    @left_join(\"ducks_db.mt2\", car, model)\n    @select(car, model)\n    @head(5)\n    @collect\nend\n
5\u00d72 DataFrame\n Row \u2502 car                model\n     \u2502 String             String\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4          Mazda RX4\n   2 \u2502 Mazda RX4 Wag      Mazda RX4 Wag\n   3 \u2502 Datsun 710         Datsun 710\n   4 \u2502 Hornet 4 Drive     Hornet 4 Drive\n   5 \u2502 Hornet Sportabout  Hornet Sportabout\n

"},{"location":"examples/generated/UserGuide/ex_joining/#using-a-view","title":"Using a View","text":"

You can also use @create_view to create views and then join them. This is an alternate reuse complex queries.

# notice, this is not begin saved, bc a view is created in the database at the end of the chain\n@chain t(mtcars) begin\n       @group_by cyl\n       @summarize begin\n            across(mpg, (mean, minimum, maximum))\n            num_cars = n()\n        end\n       @mutate begin\n           efficiency = case_when(\n           mpg_mean >= 25, \"High\",\n           mpg_mean >= 15, \"Moderate\",\n              \"Low\" )\n        end\n       #create a view in the database\n       @create_view(viewer)\nend;\n\n# access the view like as if it was any other table\n@chain db_table(db, \"viewer\") begin\n    @left_join(t(query2), cyl, cyl)\n    @group_by(efficiency)\n    @summarize(avg_mean = mean(mpg))\n    @mutate(mean = avg_mean / 4 )\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/from_queryex/","title":"Reusing a Query (and Views)","text":"

While using TidierDB, you may need to generate part of a query and reuse it multiple times. There are two ways to do this

  1. from_query(query) or its alias t(query)
  2. @create_view(name)

"},{"location":"examples/generated/UserGuide/from_queryex/#setup","title":"Setup","text":"
import TidierDB as DB\ncon = DB.connect(duckdb())\nmtcars_path = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\nmtcars = DB.db_table(con, mtcars_path)\n

Start a query to analyze fuel efficiency by number of cylinders. However, to further build on this query later, end the chain without using @show_query or @collect

query = DB.@chain DB.t(mtcars) begin\n    DB.@group_by cyl\n    DB.@summarize begin\n        across(mpg, (mean, minimum, maximum))\n        num_cars = n()\n        end\n    DB.@mutate begin\n        efficiency = case_when(\n            mpg_mean >= 25, \"High\",\n            mpg_mean >= 15, \"Moderate\",\n            \"Low\" )\n       end\nend;\n

"},{"location":"examples/generated/UserGuide/from_queryex/#from_query-or-tquery","title":"from_query() or t(query)","text":"

Now, from_query, or t() a convienece wrapper, will allow you to reuse the query to calculate the average horsepower for each efficiency category

DB.@chain DB.t(query) begin\n   DB.@left_join(DB.t(mtcars), cyl = cyl)\n   DB.@group_by(efficiency)\n   DB.@summarize(avg_hp = mean(hp))\n   DB.@collect\nend\n
2\u00d72 DataFrame\n Row \u2502 efficiency  avg_hp\n     \u2502 String?     Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Moderate    180.238\n   2 \u2502 High         82.6364\n

"},{"location":"examples/generated/UserGuide/from_queryex/#create_view","title":"@create_view","text":"

This can also be done with @create_view.

query2 = @chain t(mtcars) @filter(mpg>20) @mutate(mpg = mpg *4);\nDB.@chain  DB.db_table(db, \"mtcars\") begin\n           DB.@group_by cyl\n           DB.@summarize begin\n               across(mpg, (mean, minimum, maximum))\n               num_cars = n()\n               end\n           DB.@mutate begin\n               efficiency = case_when(\n                   mpg_mean >= 25, \"High\",\n                   mpg_mean >= 15, \"Moderate\",\n                   \"Low\" )\n             end\n       DB.@create_view(viewer)\n       end;\n\n\nDB.@chain DB.db_table(db, \"viewer\") begin\n           DB.@left_join(DB.t(query2), cyl = cyl)\n           DB.@group_by(efficiency)\n           DB.@summarize(avg_mean = mean(mpg))\n           DB.@mutate(mean = avg_mean / 4 )\n           @aside DB.@show_query _\n           DB.@collect\nend\n2\u00d73 DataFrame\n Row \u2502 efficiency  avg_mean  mean\n     \u2502 String      Float64   Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 High        106.655   26.6636\n   2 \u2502 Moderate     84.5333  21.1333\n

"},{"location":"examples/generated/UserGuide/from_queryex/#preview-or-save-an-intermediate-table","title":"Preview or save an intermediate table","text":"

While querying a dataset, you may wish to see an intermediate table, or even save it. You can use @aside and from_query(_), illustrated below, to do just that. While we opted to print the results in this simple example below, we could have saved them by using name = DB.@chain...

import ClickHouse;\nconn = conn = DB.connect(DB.clickhouse(); host=\"localhost\", port=19000, database=\"default\", user=\"default\", password=\"\")\npath = \"https://huggingface.co/datasets/maharshipandya/spotify-tracks-dataset/resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet\"\nDB.@chain DB.db_table(conn, path) begin\n   DB.@count(cyl)\n   @aside println(DB.@chain DB.from_query(_) DB.@head(5) DB.@collect)\n   DB.@arrange(desc(count))\n   DB.@collect\nend\n
5\u00d72 DataFrame\n Row \u2502 artists  count\n     \u2502 String?  UInt64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 missing       1\n   2 \u2502 Wizo          3\n   3 \u2502 MAGIC!        3\n   4 \u2502 Macaco        1\n   5 \u2502 SOYOU         1\n31438\u00d72 DataFrame\n   Row \u2502 artists          count\n       \u2502 String?          UInt64\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n     1 \u2502 The Beatles         279\n     2 \u2502 George Jones        271\n     3 \u2502 Stevie Wonder       236\n     4 \u2502 Linkin Park         224\n     5 \u2502 Ella Fitzgerald     222\n     6 \u2502 Prateek Kuhad       217\n     7 \u2502 Feid                202\n   \u22ee   \u2502        \u22ee           \u22ee\n 31432 \u2502 Leonard               1\n 31433 \u2502 marcos g              1\n 31434 \u2502 BLVKSHP               1\n 31435 \u2502 Memtrix               1\n 31436 \u2502 SOYOU                 1\n 31437 \u2502 Macaco                1\n 31438 \u2502 missing               1\n               31424 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/","title":"Writing Functions with TidierDB Chains","text":"

On this page, we'll briefly explore how to use TidierDB macros and $ witth @eval to bulid a function

For a more indepth explanation, please check out the TidierData page on interpolation

using TidierDB, DataFrames;\n\ndb = connect(duckdb());\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\ncopy_to(db, df, \"dfm\");\ndf_mem = db_table(db, \"dfm\");\n

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#interpolation","title":"Interpolation","text":"

Variables are interpoated using @eval and $. Place @eval before you begin the chain or call a TidierDb macro Why Use @eval? In Julia, macros like @filter are expanded at parse time, before runtime variables like vals are available. By using @eval, we force the expression to be evaluated at runtime, allowing us to interpolate the variable into the macro.

num = [3];\ncolumn = :id;\n@eval @chain t(df_mem) begin\n        @filter(value in $num)\n        @select($column)\n        @collect\n    end\n
2\u00d71 DataFrame RowidString1AC2AH

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#function-set-up","title":"Function set up","text":"

Begin by defining your function as your normally would, but before @chain you need to use @eval. For the variables to be interpolated in need to be started with $

function test(vals, cols)\n    @eval @chain t(df_mem) begin\n        @filter(value in $vals)\n        @select($cols)\n        @collect\n    end\nend;\n\nvals = [1,  2,  3, 3];\ntest(vals, [:groups, :value, :percent])\n
6\u00d73 DataFrame RowgroupsvaluepercentStringInt64Float641bb10.12aa10.63aa20.24bb20.75bb30.36aa30.8

Now with a new variable

other_vals = [1];\ncols = [:value, :percent];\ntest(other_vals, cols)\n
2\u00d72 DataFrame RowvaluepercentInt64Float64110.1210.6

Defineing a new function

function gs(groups, aggs, new_name, threshold)\n    @eval @chain t(df_mem) begin\n        @group_by($groups)\n        @summarize($new_name = mean($aggs))\n        @filter($new_name > $threshold)\n        @collect\n    end\nend;\n\ngs(:groups, :percent, :mean_percent, .5)\n
1\u00d72 DataFrame Rowgroupsmean_percentStringFloat641aa0.6

Change the column and threshold

gs(:groups, :value, :mean_value, 2)\n
2\u00d72 DataFrame Rowgroupsmean_valueStringFloat641bb3.02aa3.0

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#write-pipeline-function-to-use-inside-of-chains","title":"Write pipeline function to use inside of chains","text":"

Lets say there is a particular sequence of macros that you want repeatedly use. Wrap this series into a function that accepts a t(query as its first argument and returns a SQLquery and you can easily resuse it.

function moving_aggs(table, start, stop, group, order, col)\n    qry = @eval @chain $table begin\n        @group_by $group\n        @window_frame $start $stop\n        @window_order $order\n        @mutate(across($col, (minimum, maximum, mean)))\n    end\n    return qry\nend;\n\n@chain t(df_mem) begin\n    moving_aggs(-2, 1, :groups, :percent, :value)\n    @filter value_mean > 2.75\n    @aside @show_query _\n    @collect\nend\n
6\u00d77 DataFrame Rowidgroupsvaluepercentvalue_minimumvalue_maximumvalue_meanStringStringInt64Float64Int64Int64Float641ABaa20.2243.02AHaa30.8153.253AJaa51.0153.04ACbb30.3153.05AGbb20.7253.56AIbb40.9253.66667

Filtering before the window functions

@chain t(df_mem) begin\n    @filter(value >=2 )\n    moving_aggs(-1, 1, :groups, :percent, :value)\n    @aside @show_query _\n    @collect\nend\n
8\u00d77 DataFrame Rowidgroupsvaluepercentvalue_minimumvalue_maximumvalue_meanStringStringInt64Float64Int64Int64Float641ABaa20.2243.02ADaa40.4243.03AHaa30.8354.04AJaa51.0354.05ACbb30.3354.06AEbb50.5253.333337AGbb20.7253.666678AIbb40.9243.0

"},{"location":"examples/generated/UserGuide/functions_pass_to_DB/#interpolating-queries","title":"Interpolating Queries","text":"

To use a prior, uncollected TidierDB query in other TidierDB macros, interpolate the needed query without showing or collecting it

ok = @chain t(df_mem) @summarize(mean = mean(value));\n

The mean value represented in SQL from the above is 3

With @filter

@eval @chain t(df_mem) begin\n    @filter( value > $ok)\n    @collect\nend\n
4\u00d74 DataFrame RowidgroupsvaluepercentStringStringInt64Float641ADaa40.42AEbb50.53AIbb40.94AJaa51.0

With @mutate

@eval @chain t(df_mem) begin\n    @mutate(value2 =  value + $ok)\n    @collect\nend\n
10\u00d75 DataFrame Rowidgroupsvaluepercentvalue2StringStringInt64Float64Float641AAbb10.14.02ABaa20.25.03ACbb30.36.04ADaa40.47.05AEbb50.58.06AFaa10.64.07AGbb20.75.08AHaa30.86.09AIbb40.97.010AJaa51.08.0

With @summarize

@eval @chain t(df_mem) begin\n    @summarize(value =  mean(value) * $ok)\n    @collect\nend\n
1\u00d71 DataFrame RowvalueFloat6419.0

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/getting_started/","title":"Getting Started","text":"

To use TidierDB.jl, you will have to set up a connection. TidierDB.jl gives you access to duckdb via duckdb_open and duckdb_connect. However, to use MySql, ClickHouse, MSSQL, Postgres, or SQLite, you will have to load those packages in first.

If you plan to use TidierDB.jl with TidierData.jl or Tidier.jl, it is most convenenient to load the packages as follows:

using TidierData\nimport TidierDB as DB\n

Alternatively, using Tidier will import TidierDB in the above manner for you, where TidierDB functions and macros will be available as DB.@mutate() and so on, and the TidierData equivalent would be @mutate().

"},{"location":"examples/generated/UserGuide/getting_started/#connecting","title":"Connecting","text":"

To connect to a database, you can uset the connect function as shown below, or establish your own connection through the respecitve libraries.

For example Connecting to MySQL

conn = DB.connect(DB.mysql(); host=\"localhost\", user=\"root\", password=\"password\", db=\"mydb\")\n

versus connecting to DuckDB

conn = DB.connect(DB.duckdb())\n

"},{"location":"examples/generated/UserGuide/getting_started/#connect-to-a-local-database-file","title":"Connect to a local database file","text":"

You can also connect to an existing database by passing the database file path as a string.

db = DB.connect(DB.duckdb(), \"mydb.duckdb\")\n

You can also establish any DuckDB connection through an alternate method that you prefer, and use that as your connection as well.

"},{"location":"examples/generated/UserGuide/getting_started/#package-extensions","title":"Package Extensions","text":"

The following backends utilize package extensions. To use one of backends listed below, you will need to write using Library

  • ClickHouse: import ClickHouse
  • MySQL and MariaDB: using MySQL
  • MSSQL: using ODBC
  • Postgres: using LibPQ
  • SQLite: using SQLite
  • Athena: using AWS
  • Oracle: using ODBC
  • Google BigQuery: using GoogleCloud

"},{"location":"examples/generated/UserGuide/getting_started/#db_table","title":"db_table","text":"

What does db_table do?

db_table starts the underlying SQL query struct, in addition to pulling the table metadata and storing it there. Storing metadata is what enables a lazy interface that also supports tidy selection.

  • db_table has two required arguments: connection and table
  • table can be a table name on a database or a path/url to file to read. When passing db_table a path or url, the table is not copied into memory.

    • Of note, db_table only support direct file paths to a table. It does not support database file paths such as dbname.duckdb or dbname.sqlite. Such files must be used with connect first.
    • With DuckDB and ClickHouse, if you have a folder of multiple files to read, you can use * read in all files matching the pattern.
    • For example, the below would read all files that end in .csv in the given folder.
db_table(db, \"folder/path/*.csv\")\n

db_table also supports iceberg, delta, and S3 file paths via DuckDB.

"},{"location":"examples/generated/UserGuide/getting_started/#minimizing-compute-costs","title":"Minimizing Compute Costs","text":"

If you are working with a backend where compute cost is important, it will be important to minimize using db_table as this will requery for metadata each time. Compute costs are relevant to backends such as AWS, databricks and Snowflake.

To do this, save the results of db_table and use them with t. Using t pulls the relevant information (metadata, con, etc) from the mutable SQLquery struct, allowing you to repeatedly query and collect the table without requerying for the metadata each time

!Tip: t() is an alias for from_query This means after saving the results of db_table, use t(table) to refer to the table or prior query

table = DB.db_table(con, \"path\")\n@chain DB.t(table) begin\n    ## data wrangling here\nend\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/ibis_comp/","title":"TidierDB.jl vs Ibis","text":""},{"location":"examples/generated/UserGuide/ibis_comp/#comparing-tidierdb-vs-ibis","title":"Comparing TidierDB vs Ibis","text":"

TidierDB is a reimplementation of dbplyr from R, so the syntax is remarkably similar. But how does TidierDB compare to Python's Ibis? This page will perform a similar comparison to the Ibis Documentation comparing Ibis and dplyr

"},{"location":"examples/generated/UserGuide/ibis_comp/#set-up","title":"Set up","text":"

Ibis

import ibis\nimport ibis.selectors as s # allows for different styles of column selection\nfrom ibis import _ # eliminates need to type table name before each column vs typing cols as strings\nibis.options.interactive = True # automatically collects first 10 rows of table\n\ncon = ibis.connect(\"duckdb://\")\n

TidierDB

using TidierDB\ndb = connect(duckdb())\n

Of note, TidierDB does not yet have an \"interactive mode\" so each example result will be collected.

"},{"location":"examples/generated/UserGuide/ibis_comp/#loading-data","title":"Loading Data","text":"

With Ibis, there are specific functions to read in different file types

mtcars = con.read_csv(\"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\")\n

In TidierDB, there is only db_table, which determines the file type and generates the syntax appropriate for the backend in use.

mtcars = db_table(db, \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\");\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#previewing-the-data","title":"Previewing the data","text":"

TidierDB and Ibis use head/@head to preview the first rows of a dataset.

Ibis

mtcars.head(6)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502    21.0 \u2502     6 \u2502   160.0 \u2502   110 \u2502    3.90 \u2502   2.620 \u2502   16.46 \u2502     0 \u2502     1 \u2502     4 \u2502     4 \u2502\n\u2502 Mazda RX4 Wag     \u2502    21.0 \u2502     6 \u2502   160.0 \u2502   110 \u2502    3.90 \u2502   2.875 \u2502   17.02 \u2502     0 \u2502     1 \u2502     4 \u2502     4 \u2502\n\u2502 Datsun 710        \u2502    22.8 \u2502     4 \u2502   108.0 \u2502    93 \u2502    3.85 \u2502   2.320 \u2502   18.61 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Hornet 4 Drive    \u2502    21.4 \u2502     6 \u2502   258.0 \u2502   110 \u2502    3.08 \u2502   3.215 \u2502   19.44 \u2502     1 \u2502     0 \u2502     3 \u2502     1 \u2502\n\u2502 Hornet Sportabout \u2502    18.7 \u2502     8 \u2502   360.0 \u2502   175 \u2502    3.15 \u2502   3.440 \u2502   17.02 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Valiant           \u2502    18.1 \u2502     6 \u2502   225.0 \u2502   105 \u2502    2.76 \u2502   3.460 \u2502   20.22 \u2502     1 \u2502     0 \u2502     3 \u2502     1 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) @head(6) @collect\n
6\u00d712 DataFrame\n Row \u2502 model              mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?            Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4              21.0       6     160.0     110      3.9      2.62      16.46       0       1       4       4\n   2 \u2502 Mazda RX4 Wag          21.0       6     160.0     110      3.9      2.875     17.02       0       1       4       4\n   3 \u2502 Datsun 710             22.8       4     108.0      93      3.85     2.32      18.61       1       1       4       1\n   4 \u2502 Hornet 4 Drive         21.4       6     258.0     110      3.08     3.215     19.44       1       0       3       1\n   5 \u2502 Hornet Sportabout      18.7       8     360.0     175      3.15     3.44      17.02       0       0       3       2\n   6 \u2502 Valiant                18.1       6     225.0     105      2.76     3.46      20.22       1       0       3       1\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#filtering","title":"Filtering","text":"

The example below demonstrates how to filter using multiple criteria in both Ibis and TidierData Ibis

mtcars.filter(((_.mpg > 22) & (_.drat > 4) | (_.hp == 113)))\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model          \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string         \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Lotus Europa   \u2502    30.4 \u2502     4 \u2502    95.1 \u2502   113 \u2502    3.77 \u2502   1.513 \u2502   16.90 \u2502     1 \u2502     1 \u2502     5 \u2502     2 \u2502\n\u2502 Fiat 128       \u2502    32.4 \u2502     4 \u2502    78.7 \u2502    66 \u2502    4.08 \u2502   2.200 \u2502   19.47 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Honda Civic    \u2502    30.4 \u2502     4 \u2502    75.7 \u2502    52 \u2502    4.93 \u2502   1.615 \u2502   18.52 \u2502     1 \u2502     1 \u2502     4 \u2502     2 \u2502\n\u2502 Toyota Corolla \u2502    33.9 \u2502     4 \u2502    71.1 \u2502    65 \u2502    4.22 \u2502   1.835 \u2502   19.90 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Fiat X1-9      \u2502    27.3 \u2502     4 \u2502    79.0 \u2502    66 \u2502    4.08 \u2502   1.935 \u2502   18.90 \u2502     1 \u2502     1 \u2502     4 \u2502     1 \u2502\n\u2502 Porsche 914-2  \u2502    26.0 \u2502     4 \u2502   120.3 \u2502    91 \u2502    4.43 \u2502   2.140 \u2502   16.70 \u2502     0 \u2502     1 \u2502     5 \u2502     2 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) begin\n       @filter((mpg > 22 && drat > 4) || hp == 113)\n       @collect\nend\n
6\u00d712 DataFrame\n Row \u2502 model           mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?         Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Lotus Europa        30.4       4      95.1     113      3.77     1.513     16.9        1       1       5       2\n   2 \u2502 Fiat 128            32.4       4      78.7      66      4.08     2.2       19.47       1       1       4       1\n   3 \u2502 Honda Civic         30.4       4      75.7      52      4.93     1.615     18.52       1       1       4       2\n   4 \u2502 Toyota Corolla      33.9       4      71.1      65      4.22     1.835     19.9        1       1       4       1\n   5 \u2502 Fiat X1-9           27.3       4      79.0      66      4.08     1.935     18.9        1       1       4       1\n   6 \u2502 Porsche 914-2       26.0       4     120.3      91      4.43     2.14      16.7        0       1       5       2\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#creating-new-columns","title":"Creating new columns","text":"

Both TidierDB and Ibis use mutate/@mutate to add new columns

Ibis

(\n   mtcars\n        .mutate(kpg = _.mpg * 1.61)\n        .select(\"model\", \"kpg\")\n)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 kpg     \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502  33.810 \u2502\n\u2502 Mazda RX4 Wag     \u2502  33.810 \u2502\n\u2502 Datsun 710        \u2502  36.708 \u2502\n\u2502 Hornet 4 Drive    \u2502  34.454 \u2502\n\u2502 Hornet Sportabout \u2502  30.107 \u2502\n\u2502 Valiant           \u2502  29.141 \u2502\n\u2502 Duster 360        \u2502  23.023 \u2502\n\u2502 Merc 240D         \u2502  39.284 \u2502\n\u2502 Merc 230          \u2502  36.708 \u2502\n\u2502 Merc 280          \u2502  30.912 \u2502\n\u2502 \u2026                 \u2502       \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) begin\n       @mutate(kpg = mpg * 1.61)\n       @select(model, kpg)\n       @collect\nend\n
32\u00d72 DataFrame\n Row \u2502 model              kpg\n     \u2502 String?            Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4            33.81\n   2 \u2502 Mazda RX4 Wag        33.81\n   3 \u2502 Datsun 710           36.708\n   4 \u2502 Hornet 4 Drive       34.454\n   5 \u2502 Hornet Sportabout    30.107\n   6 \u2502 Valiant              29.141\n  \u22ee  \u2502         \u22ee             \u22ee\n  27 \u2502 Porsche 914-2        41.86\n  28 \u2502 Lotus Europa         48.944\n  29 \u2502 Ford Pantera L       25.438\n  30 \u2502 Ferrari Dino         31.717\n  31 \u2502 Maserati Bora        24.15\n  32 \u2502 Volvo 142E           34.454\n                    20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#sorting-columns","title":"Sorting columns","text":"

Ibis uses order_by similar to SQLs ORDER BY

Ibis

mtcars.order_by(_.mpg)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model               \u2503 mpg     \u2503 cyl   \u2503 disp    \u2503 hp    \u2503 drat    \u2503 wt      \u2503 qsec    \u2503 vs    \u2503 am    \u2503 gear  \u2503 carb  \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string              \u2502 float64 \u2502 int64 \u2502 float64 \u2502 int64 \u2502 float64 \u2502 float64 \u2502 float64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502 int64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Cadillac Fleetwood  \u2502    10.4 \u2502     8 \u2502   472.0 \u2502   205 \u2502    2.93 \u2502   5.250 \u2502   17.98 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Lincoln Continental \u2502    10.4 \u2502     8 \u2502   460.0 \u2502   215 \u2502    3.00 \u2502   5.424 \u2502   17.82 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Camaro Z28          \u2502    13.3 \u2502     8 \u2502   350.0 \u2502   245 \u2502    3.73 \u2502   3.840 \u2502   15.41 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Duster 360          \u2502    14.3 \u2502     8 \u2502   360.0 \u2502   245 \u2502    3.21 \u2502   3.570 \u2502   15.84 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Chrysler Imperial   \u2502    14.7 \u2502     8 \u2502   440.0 \u2502   230 \u2502    3.23 \u2502   5.345 \u2502   17.42 \u2502     0 \u2502     0 \u2502     3 \u2502     4 \u2502\n\u2502 Maserati Bora       \u2502    15.0 \u2502     8 \u2502   301.0 \u2502   335 \u2502    3.54 \u2502   3.570 \u2502   14.60 \u2502     0 \u2502     1 \u2502     5 \u2502     8 \u2502\n\u2502 Merc 450SLC         \u2502    15.2 \u2502     8 \u2502   275.8 \u2502   180 \u2502    3.07 \u2502   3.780 \u2502   18.00 \u2502     0 \u2502     0 \u2502     3 \u2502     3 \u2502\n\u2502 AMC Javelin         \u2502    15.2 \u2502     8 \u2502   304.0 \u2502   150 \u2502    3.15 \u2502   3.435 \u2502   17.30 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Dodge Challenger    \u2502    15.5 \u2502     8 \u2502   318.0 \u2502   150 \u2502    2.76 \u2502   3.520 \u2502   16.87 \u2502     0 \u2502     0 \u2502     3 \u2502     2 \u2502\n\u2502 Ford Pantera L      \u2502    15.8 \u2502     8 \u2502   351.0 \u2502   264 \u2502    4.22 \u2502   3.170 \u2502   14.50 \u2502     0 \u2502     1 \u2502     5 \u2502     4 \u2502\n\u2502 \u2026                   \u2502       \u2026 \u2502     \u2026 \u2502       \u2026 \u2502     \u2026 \u2502       \u2026 \u2502       \u2026 \u2502       \u2026 \u2502     \u2026 \u2502     \u2026 \u2502     \u2026 \u2502     \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

While TidierDB uses @arrange like TidierData.jl

TidierDB

@chain t(mtcars) @arrange(mpg) @collect\n
32\u00d712 DataFrame\n Row \u2502 model                mpg       cyl     disp      hp      drat      wt        qsec      vs      am      gear    carb\n     \u2502 String?              Float64?  Int64?  Float64?  Int64?  Float64?  Float64?  Float64?  Int64?  Int64?  Int64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Cadillac Fleetwood       10.4       8     472.0     205      2.93     5.25      17.98       0       0       3       4\n   2 \u2502 Lincoln Continental      10.4       8     460.0     215      3.0      5.424     17.82       0       0       3       4\n   3 \u2502 Camaro Z28               13.3       8     350.0     245      3.73     3.84      15.41       0       0       3       4\n   4 \u2502 Duster 360               14.3       8     360.0     245      3.21     3.57      15.84       0       0       3       4\n   5 \u2502 Chrysler Imperial        14.7       8     440.0     230      3.23     5.345     17.42       0       0       3       4\n   6 \u2502 Maserati Bora            15.0       8     301.0     335      3.54     3.57      14.6        0       1       5       8\n  \u22ee  \u2502          \u22ee              \u22ee        \u22ee        \u22ee        \u22ee        \u22ee         \u22ee         \u22ee        \u22ee       \u22ee       \u22ee       \u22ee\n  27 \u2502 Porsche 914-2            26.0       4     120.3      91      4.43     2.14      16.7        0       1       5       2\n  28 \u2502 Fiat X1-9                27.3       4      79.0      66      4.08     1.935     18.9        1       1       4       1\n  29 \u2502 Honda Civic              30.4       4      75.7      52      4.93     1.615     18.52       1       1       4       2\n  30 \u2502 Lotus Europa             30.4       4      95.1     113      3.77     1.513     16.9        1       1       5       2\n  31 \u2502 Fiat 128                 32.4       4      78.7      66      4.08     2.2       19.47       1       1       4       1\n  32 \u2502 Toyota Corolla           33.9       4      71.1      65      4.22     1.835     19.9        1       1       4       1\n                                                                                                              20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#selecting-columns","title":"Selecting columns","text":"

In Ibis, columns must be prefixed with the table name, or in this case _, or they can be given as a string. Finally to using helper functions like startswith requires importing selectors as above.

Ibis

mtcars.select(s.startswith(\"m\"), \"drat\", _.wt)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 model             \u2503 mpg     \u2503 drat    \u2503 wt      \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502 float64 \u2502 float64 \u2502 float64 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502    21.0 \u2502    3.90 \u2502   2.620 \u2502\n\u2502 Mazda RX4 Wag     \u2502    21.0 \u2502    3.90 \u2502   2.875 \u2502\n\u2502 Datsun 710        \u2502    22.8 \u2502    3.85 \u2502   2.320 \u2502\n\u2502 Hornet 4 Drive    \u2502    21.4 \u2502    3.08 \u2502   3.215 \u2502\n\u2502 Hornet Sportabout \u2502    18.7 \u2502    3.15 \u2502   3.440 \u2502\n\u2502 Valiant           \u2502    18.1 \u2502    2.76 \u2502   3.460 \u2502\n\u2502 Duster 360        \u2502    14.3 \u2502    3.21 \u2502   3.570 \u2502\n\u2502 Merc 240D         \u2502    24.4 \u2502    3.69 \u2502   3.190 \u2502\n\u2502 Merc 230          \u2502    22.8 \u2502    3.92 \u2502   3.150 \u2502\n\u2502 Merc 280          \u2502    19.2 \u2502    3.92 \u2502   3.440 \u2502\n\u2502 \u2026                 \u2502       \u2026 \u2502       \u2026 \u2502       \u2026 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB does not require names to be prefixed and, like TidierData, tidy column selection with starts_with, ends_with, and contains is supported at base. TidierDB also supports providing column names as strings, although this would only be needed in the setting of renaming a column with a space in it.

TidierDB

@chain t(mtcars) @select(starts_with(\"m\"), \"drat\", wt) @collect\n
32\u00d74 DataFrame\n Row \u2502 model              mpg       drat      wt\n     \u2502 String?            Float64?  Float64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4              21.0      3.9      2.62\n   2 \u2502 Mazda RX4 Wag          21.0      3.9      2.875\n   3 \u2502 Datsun 710             22.8      3.85     2.32\n   4 \u2502 Hornet 4 Drive         21.4      3.08     3.215\n   5 \u2502 Hornet Sportabout      18.7      3.15     3.44\n   6 \u2502 Valiant                18.1      2.76     3.46\n  \u22ee  \u2502         \u22ee             \u22ee         \u22ee         \u22ee\n  27 \u2502 Porsche 914-2          26.0      4.43     2.14\n  28 \u2502 Lotus Europa           30.4      3.77     1.513\n  29 \u2502 Ford Pantera L         15.8      4.22     3.17\n  30 \u2502 Ferrari Dino           19.7      3.62     2.77\n  31 \u2502 Maserati Bora          15.0      3.54     3.57\n  32 \u2502 Volvo 142E             21.4      4.11     2.78\n                                        20 rows omitted\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#multi-step-queries-and-summarizing","title":"Multi step queries and summarizing","text":"

Aggregating data is done with aggregate in Ibis and @summarize in TidierDB. To group data, both utilze group_by/@group_by Ibis

mtcars.group_by(._cyl).aggregate(\n    total_hp=_.hp.sum(),\n    avg_hp=_.hp.mean()\n).filter(_.total_hp < 1000)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 cyl   \u2503 total_hp \u2503 avg_hp     \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 int64 \u2502 int64    \u2502 float64    \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502     6 \u2502      856 \u2502 122.285714 \u2502\n\u2502     4 \u2502      909 \u2502  82.636364 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

In TidierDB, @filter will automatically determine whether the criteria belong in a WHERE or HAVING SQL clause.

TidierDB

@chain t(mtcars) begin\n    @group_by(cyl)\n    @summarize(total_hp = sum(hp),\n               avg_hp = avg(hp))\n    @filter(total_hp < 1000)\n    @collect\nend\n
2\u00d73 DataFrame\n Row \u2502 cyl     total_hp  avg_hp\n     \u2502 Int64?  Int128?   Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      6       856  122.286\n   2 \u2502      4       909   82.6364\n

"},{"location":"examples/generated/UserGuide/ibis_comp/#renaming-columns","title":"Renaming columns","text":"

Both tools use rename/@rename to rename columns

Ibis

mtcars.rename(make_model = \"model\").select(_.make_model)\n
\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 make_model        \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 string            \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Mazda RX4         \u2502\n\u2502 Mazda RX4 Wag     \u2502\n\u2502 Datsun 710        \u2502\n\u2502 Hornet 4 Drive    \u2502\n\u2502 Hornet Sportabout \u2502\n\u2502 Valiant           \u2502\n\u2502 Duster 360        \u2502\n\u2502 Merc 240D         \u2502\n\u2502 Merc 230          \u2502\n\u2502 Merc 280          \u2502\n\u2502 \u2026                 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n

TidierDB

@chain t(mtcars) @rename(model_make = model) @select(model_make) @collect\n
32\u00d71 DataFrame\n Row \u2502 model_make\n     \u2502 String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4\n   2 \u2502 Mazda RX4 Wag\n   3 \u2502 Datsun 710\n   4 \u2502 Hornet 4 Drive\n   5 \u2502 Hornet Sportabout\n   6 \u2502 Valiant\n  \u22ee  \u2502         \u22ee\n  27 \u2502 Porsche 914-2\n  28 \u2502 Lotus Europa\n  29 \u2502 Ford Pantera L\n  30 \u2502 Ferrari Dino\n  31 \u2502 Maserati Bora\n  32 \u2502 Volvo 142E\n          20 rows omitted\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/key_differences/","title":"Key Differences from TidierData.jl","text":"

There are a few important syntax and behavior differences between TidierDB.jl and TidierData.jl outlined below.

"},{"location":"examples/generated/UserGuide/key_differences/#creating-a-database","title":"Creating a database","text":"

For these examples we will use DuckDB, the default backend, although SQLite, Postgres, MySQL, MariaDB, MSSQL, and ClickHouse are possible. If you have an existing DuckDB connection, then this step is not required. For these examples, we will create a data frame and copy it to an in-memory DuckDB database.

using DataFrames, TidierDB\n\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\n\ndb = connect(duckdb());\n\ncopy_to(db, df, \"df_mem\"); # copying over the data frame to an in-memory database\n

"},{"location":"examples/generated/UserGuide/key_differences/#row-ordering","title":"Row ordering","text":"

DuckDB benefits from aggressive parallelization of pipelines. This means that if you have multiple threads enabled in Julia, which you can check or set using Threads.nthreads(), DuckDB will use multiple threads. However, because many operations are multi-threaded, the resulting row order is inconsistent. If row order needs to be deterministic for your use case, make sure to apply an @arrange(column_name_1, column_name_2, etc...) prior to collecting the results.

"},{"location":"examples/generated/UserGuide/key_differences/#starting-a-chain","title":"Starting a chain","text":"

When using TidierDB, db_table(connection, :table_name) is used to start a chain.

"},{"location":"examples/generated/UserGuide/key_differences/#grouped-mutation","title":"Grouped mutation","text":"

In TidierDB, when performing @group_by then @mutate, the table will be ungrouped after applying all of the mutations in the clause to the grouped data. To perform subsequent grouped operations, the user would have to regroup the data. This is demonstrated below.

@chain db_table(db, :df_mem) begin\n    @group_by(groups)\n    @summarize(mean_percent = mean(percent))\n    @collect\n end\n
2\u00d72 DataFrame Rowgroupsmean_percentStringFloat641bb0.52aa0.6

Regrouping following @mutate

@chain db_table(db, :df_mem) begin\n    @group_by(groups)\n    @mutate(max = maximum(percent), min = minimum(percent))\n    @group_by(groups)\n    @summarise(mean_percent = mean(percent))\n    @collect\nend\n
2\u00d72 DataFrame Rowgroupsmean_percentStringFloat641aa0.62bb0.5

"},{"location":"examples/generated/UserGuide/key_differences/#differences-in-case_when","title":"Differences in case_when()","text":"

In TidierDB, after the clause is completed, the result for the new column should is separated by a comma , in contrast to TidierData.jl, where the result for the new column is separated by a => .

@chain db_table(db, :df_mem) begin\n    @mutate(new_col = case_when(percent > .5, \"Pass\",  # in TidierData, percent > .5 => \"Pass\",\n                                percent <= .5, \"Try Again\", # percent <= .5 => \"Try Again\"\n                                true, \"middle\"))\n    @collect\n end\n
10\u00d75 DataFrame Rowidgroupsvaluepercentnew_colStringStringInt64Float64String1AAbb10.1Try Again2ABaa20.2Try Again3ACbb30.3Try Again4ADaa40.4Try Again5AEbb50.5Try Again6AFaa10.6Pass7AGbb20.7Pass8AHaa30.8Pass9AIbb40.9Pass10AJaa51.0Pass

"},{"location":"examples/generated/UserGuide/key_differences/#joining-tables","title":"Joining Tables","text":"

When joining a table, the column from both tables will be present, in contrast to TidierData which will keep one column

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/outofmemex/","title":"Working With Larger than RAM Datasets","text":"

While using the DuckDB backend, TidierDB's lazy intferace enables querying datasets larger than your available RAM.

To illustrate this, we will recreate the Hugging Face x Polars example. The final table results are shown below and in this Hugging Face x DuckDB example

First we will load TidierDB, set up a local database and then set the URLs for the 2 training datasets from huggingface.co

using TidierDB\ndb = connect(duckdb())\n\nurls = [\"https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0000.parquet\",\n \"https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0001.parquet\"];\n

Here, we pass the vector of URLs to db_table, which will not copy them into memory. Since these datasets are so large, we will also set stream = true in @collect to stream the results. If we wanted to read all the files in the folder we could have replace the 0000 with * (wildcard) db_table(db, \"Path/to/folder/*.parquet\") Of note, reading these files from URLs is not as rapid as reading them from local files.

@chain db_table(db, urls) begin\n    @group_by(horoscope)\n    @summarise(count = n(), avg_blog_length = mean(length(text)))\n    @arrange(desc(count))\n    @aside @show_query _\n    @collect(stream = true)\nend\n

Placing @aside @show_query _ before @collect above lets us see the SQL query and collect it to a local DataFrame at the same time.

SELECT horoscope, COUNT(*) AS count, AVG(length(text)) AS avg_blog_length\n        FROM read_parquet(['https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0000.parquet', 'https://huggingface.co/datasets/blog_authorship_corpus/resolve/refs%2Fconvert%2Fparquet/blog_authorship_corpus/train/0001.parquet'])\n        GROUP BY horoscope\n        ORDER BY avg_blog_length DESC\n12\u00d73 DataFrame\n Row \u2502 horoscope    count   avg_blog_length\n     \u2502 String?      Int64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Aquarius      49568         1125.83\n   2 \u2502 Cancer        63512         1097.96\n   3 \u2502 Libra         60304         1060.61\n   4 \u2502 Capricorn     49402         1059.56\n   5 \u2502 Sagittarius   50431         1057.46\n   6 \u2502 Leo           58010         1049.6\n   7 \u2502 Taurus        61571         1022.69\n   8 \u2502 Gemini        52925         1020.26\n   9 \u2502 Scorpio       56495         1014.03\n  10 \u2502 Pisces        53812         1011.75\n  11 \u2502 Virgo         64629          996.684\n  12 \u2502 Aries         69134          918.081\n

To learn more about memory efficient queries on larger than RAM files, this blog from DuckDB will help maximize your local db

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/s3viaduckdb/","title":"S3 + DuckDB + TidierDB","text":"

TidierDB allows you leverage DuckDB's seamless database integration.

Using DuckDB, you can connect to an AWS or GoogleCloud Database to query directly without making any local copies.

You can also use DBInterface.execute to set up any DuckDB database connection you need and then use that db to query with TidierDB

using TidierDB\n\n#Connect to Google Cloud via DuckDB\n#google_db = connect(duckdb(), :gbq, access_key=\"string\", secret_key=\"string\")\n\n#Connect to AWS via DuckDB\naws_db = connect(duckdb(), :aws, aws_access_key_id= \"string\",\n                                aws_secret_access_key= \"string\",\n                                aws_region=\"us-east-1\")\ns3_csv_path = \"s3://path/to_data.csv\"\n\n@chain db_table(aws_db, s3_csv_path) begin\n    @filter(!starts_with(column1, \"M\"))\n    @group_by(cyl)\n    @summarize(mpg = mean(mpg))\n    @mutate(mpg_squared = mpg^2,\n               mpg_rounded = round(mpg),\n               mpg_efficiency = case_when(\n                                 mpg >= cyl^2 , \"efficient\",\n                                 mpg < 15.2 , \"inefficient\",\n                                 \"moderate\"))\n    @filter(mpg_efficiency in (\"moderate\", \"efficient\"))\n    @arrange(desc(mpg_rounded))\n    @collect\nend\n
2\u00d75 DataFrame\n Row \u2502 cyl     mpg       mpg_squared  mpg_rounded  mpg_efficiency\n     \u2502 Int64?  Float64?  Float64?     Float64?     String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4   27.3444      747.719         27.0  efficient\n   2 \u2502      6   19.7333      389.404         20.0  moderate\n

This page was generated using Literate.jl.

"},{"location":"examples/generated/UserGuide/udfs_ex/","title":"Flexible Syntax and UDFs","text":"

TidierDB is unique in its statement parsing flexiblility. This means that using any built in SQL function or user defined functions (or UDFS) or is readily avaialable. To use any function built into a database in @mutate or in @summarize, simply correctly write the correctly, but replace ' with \". This also applies to any UDF. The example below will illustrate UDFs in the context of DuckDB.

# Set up the connection\nusing TidierDB  #rexports DuckDB\ndb = DuckDB.DB()\ncon = DuckDB.connect(db) # this will be important for UDFs\nmtcars_path = \"https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv\"\nmtcars = db_tbable(con, mtcars_path);\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#aggregate-function-in-summarize","title":"aggregate function in @summarize","text":"

Lets use the DuckDB kurtosis aggregate function

@chain t(mtcars) begin\n      @group_by cyl\n      @summarize(kurt = kurtosis(mpg))\n      @collect\nend\n3\u00d72 DataFrame\n Row \u2502 cyl     kurt\n     \u2502 Int64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      4  -1.43411\n   2 \u2502      6  -1.82944\n   3 \u2502      8   0.330061\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#aggregate-functions-in-mutate","title":"aggregate functions in @mutate","text":"

To aggregate sql functions that are builtin to any database, but exist outside of the TidierDB parser, simply wrap the function call in agg()

@chain t(mtcars) begin\n    @group_by(cyl)\n    @mutate(kurt = agg(kurtosis(mpg)))\n    @select cyl mpg kurt\n    @collect\nend\n\n32\u00d73 DataFrame\n Row \u2502 cyl     mpg       kurt\n     \u2502 Int64?  Float64?  Float64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502      8      18.7   0.330061\n   2 \u2502      8      14.3   0.330061\n   3 \u2502      8      16.4   0.330061\n   4 \u2502      8      17.3   0.330061\n   5 \u2502      8      15.2   0.330061\n   6 \u2502      8      10.4   0.330061\n   7 \u2502      8      10.4   0.330061\n  \u22ee  \u2502   \u22ee        \u22ee          \u22ee\n  27 \u2502      6      21.0  -1.82944\n  28 \u2502      6      21.4  -1.82944\n  29 \u2502      6      18.1  -1.82944\n  30 \u2502      6      19.2  -1.82944\n  31 \u2502      6      17.8  -1.82944\n  32 \u2502      6      19.7  -1.82944\n                    19 rows omitted\nend\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#duckdb-function-chaining","title":"DuckDB function chaining","text":"

In DuckDB, functions can be chained together with .. TidierDB lets you leverage this.

@chain t(mtcars) begin\n    @mutate(model2 = model.upper().string_split(\" \").list_aggr(\"string_agg\",\".\").concat(\".\"))\n    @select model model2\n    @collect\nend\n32\u00d72 DataFrame\n Row \u2502 model              model2\n     \u2502 String?            String?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Mazda RX4          MAZDA.RX4.\n   2 \u2502 Mazda RX4 Wag      MAZDA.RX4.WAG.\n   3 \u2502 Datsun 710         DATSUN.710.\n   4 \u2502 Hornet 4 Drive     HORNET.4.DRIVE.\n   5 \u2502 Hornet Sportabout  HORNET.SPORTABOUT.\n   6 \u2502 Valiant            VALIANT.\n   7 \u2502 Duster 360         DUSTER.360.\n  \u22ee  \u2502         \u22ee                  \u22ee\n  27 \u2502 Porsche 914-2      PORSCHE.914-2.\n  28 \u2502 Lotus Europa       LOTUS.EUROPA.\n  29 \u2502 Ford Pantera L     FORD.PANTERA.L.\n  30 \u2502 Ferrari Dino       FERRARI.DINO.\n  31 \u2502 Maserati Bora      MASERATI.BORA.\n  32 \u2502 Volvo 142E         VOLVO.142E.\n                              19 rows omitted\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#rowid-and-pseudocolumns","title":"rowid and pseudocolumns","text":"

When a table is not being read directly from a file, rowid is avaialable for use. In general, TidierDB should support all pseudocolumns.

copy_to(db, mtcars_path, \"mtcars\"); # copying table in for demostration purposes\n@chain db_table(con, :mtcars) begin\n      @filter(rowid == 4)\n      @select(model:hp)\n      @collect\nend\n1\u00d75 DataFrame\n Row \u2502 model              mpg       cyl     disp      hp\n     \u2502 String?            Float64?  Int64?  Float64?  Int64?\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502 Hornet Sportabout      18.7       8     360.0     175\n

"},{"location":"examples/generated/UserGuide/udfs_ex/#udf-sqlite-example","title":"UDF SQLite Example","text":"
using SQLite\nsql = connect(sqlite());\ndf = DataFrame(id = [string('A' + i \u00f7 26, 'A' + i % 26) for i in 0:9],\n                        groups = [i % 2 == 0 ? \"aa\" : \"bb\" for i in 1:10],\n                        value = repeat(1:5, 2),\n                        percent = 0.1:0.1:1.0);\n\ncopy_to(db, sql, \"df_mem\");\nSQLite.@register sql function diff_of_squares(x, y)\n              x^2 - y^2\n              end;\n\n@chain db_table(sql, \"df_mem\") begin\n      @select(value, percent)\n      @mutate(plus3 = diff_of_squares(value, percent))\n      @collect\nend\n10\u00d73 DataFrame\n Row \u2502 value  percent  plus3\n     \u2502 Int64  Float64  Float64\n\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   1 \u2502     1      0.1     0.99\n   2 \u2502     2      0.2     3.96\n   3 \u2502     3      0.3     8.91\n   4 \u2502     4      0.4    15.84\n   5 \u2502     5      0.5    24.75\n   6 \u2502     1      0.6     0.64\n   7 \u2502     2      0.7     3.51\n   8 \u2502     3      0.8     8.36\n   9 \u2502     4      0.9    15.19\n  10 \u2502     5      1.0    24.0\n
"},{"location":"examples/generated/UserGuide/udfs_ex/#how-to-create-udf-in-duckdb","title":"How to create UDF in DuckDB","text":"

Example coming soon..

This page was generated using Literate.jl.

"}]} \ No newline at end of file diff --git a/latest/sitemap.xml.gz b/latest/sitemap.xml.gz index a72535d..482a3ee 100644 Binary files a/latest/sitemap.xml.gz and b/latest/sitemap.xml.gz differ