commit ec922a3bc83e5471d16ce73e30856613fb645986
parent cf0421cac8f28d093c9866c1988c7ef8e14b1d36
Author: Olivier Leroy <31417689+defuneste@users.noreply.github.com>
Date:   Wed,  3 Sep 2025 07:21:05 -0400
Chap02 cohort10 (#89)
* first try to slide chap03
* finish chap03
* Re-freeze.
* Tweaks. Started to add chats/videos but realized I should save that for a separate PR!
---------
Co-authored-by: Jon Harmon <jonthegeek@gmail.com>
Diffstat:
10 files changed, 2027 insertions(+), 1970 deletions(-)
diff --git a/_freeze/site_libs/revealjs/dist/theme/quarto-f77ede3f8df5db7b2f50cc1693ca0955.css b/_freeze/site_libs/revealjs/dist/theme/quarto-f77ede3f8df5db7b2f50cc1693ca0955.css
@@ -0,0 +1,8 @@
+@import"./fonts/source-sans-pro/source-sans-pro.css";:root{--r-background-color: #191919;--r-main-font: Source Sans Pro, Helvetica, sans-serif;--r-main-font-size: 36px;--r-main-color: #fff;--r-block-margin: 12px;--r-heading-margin: 0 0 12px 0;--r-heading-font: Source Sans Pro, Helvetica, sans-serif;--r-heading-color: #fff;--r-heading-line-height: 1.2;--r-heading-letter-spacing: normal;--r-heading-text-transform: none;--r-heading-text-shadow: none;--r-heading-font-weight: 600;--r-heading1-text-shadow: none;--r-heading1-size: 2em;--r-heading2-size: 1.6em;--r-heading3-size: 1.3em;--r-heading4-size: 1em;--r-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-link-color: #42affa;--r-link-color-dark: #068de9;--r-link-color-hover: #74c4fb;--r-selection-background-color: #bee4fd;--r-selection-color: #191919;--r-overlay-element-bg-color: 240, 240, 240;--r-overlay-element-fg-color: 0, 0, 0}.reveal-viewport{background:#191919;background-color:var(--r-background-color)}.reveal{font-family:var(--r-main-font);font-size:var(--r-main-font-size);font-weight:normal;color:var(--r-main-color)}.reveal ::selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal ::-moz-selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal .slides section,.reveal .slides section>section{line-height:1.3;font-weight:inherit}.reveal h1,.reveal h2,.reveal h3,.reveal h4,.reveal h5,.reveal h6{margin:var(--r-heading-margin);color:var(--r-heading-color);font-family:var(--r-heading-font);font-weight:var(--r-heading-font-weight);line-height:var(--r-heading-line-height);letter-spacing:var(--r-heading-letter-spacing);text-transform:var(--r-heading-text-transform);text-shadow:var(--r-heading-text-shadow);word-wrap:break-word}.reveal h1{font-size:var(--r-heading1-size)}.reveal h2{font-size:var(--r-heading2-size)}.reveal h3{font-size:var(--r-heading3-size)}.reveal h4{font-size:var(--r-heading4-size)}.reveal h1{text-shadow:var(--r-heading1-text-shadow)}.reveal p{margin:var(--r-block-margin) 0;line-height:1.3}.reveal h1:last-child,.reveal h2:last-child,.reveal h3:last-child,.reveal h4:last-child,.reveal h5:last-child,.reveal h6:last-child{margin-bottom:0}.reveal img,.reveal video,.reveal iframe{max-width:95%;max-height:95%}.reveal strong,.reveal b{font-weight:bold}.reveal em{font-style:italic}.reveal ol,.reveal dl,.reveal ul{display:inline-block;text-align:left;margin:0 0 0 1em}.reveal ol{list-style-type:decimal}.reveal ul{list-style-type:disc}.reveal ul ul{list-style-type:square}.reveal ul ul ul{list-style-type:circle}.reveal ul ul,.reveal ul ol,.reveal ol ol,.reveal ol ul{display:block;margin-left:40px}.reveal dt{font-weight:bold}.reveal dd{margin-left:40px}.reveal blockquote{display:block;position:relative;width:70%;margin:var(--r-block-margin) auto;padding:5px;font-style:italic;background:rgba(255,255,255,.05);box-shadow:0px 0px 2px rgba(0,0,0,.2)}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:inline-block}.reveal q{font-style:italic}.reveal pre{display:block;position:relative;width:90%;margin:var(--r-block-margin) auto;text-align:left;font-size:.55em;font-family:var(--r-code-font);line-height:1.2em;word-wrap:break-word;box-shadow:0px 5px 15px rgba(0,0,0,.15)}.reveal code{font-family:var(--r-code-font);text-transform:none;tab-size:2}.reveal pre code{display:block;padding:5px;overflow:auto;max-height:400px;word-wrap:normal}.reveal .code-wrapper{white-space:normal}.reveal .code-wrapper code{white-space:pre}.reveal table{margin:auto;border-collapse:collapse;border-spacing:0}.reveal table th{font-weight:bold}.reveal table th,.reveal table td{text-align:left;padding:.2em .5em .2em .5em;border-bottom:1px solid}.reveal table th[align=center],.reveal table td[align=center]{text-align:center}.reveal table th[align=right],.reveal table td[align=right]{text-align:right}.reveal table tbody tr:last-child th,.reveal table tbody tr:last-child td{border-bottom:none}.reveal sup{vertical-align:super;font-size:smaller}.reveal sub{vertical-align:sub;font-size:smaller}.reveal small{display:inline-block;font-size:.6em;line-height:1.2em;vertical-align:top}.reveal small *{vertical-align:top}.reveal img{margin:var(--r-block-margin) 0}.reveal a{color:var(--r-link-color);text-decoration:none;transition:color .15s ease}.reveal a:hover{color:var(--r-link-color-hover);text-shadow:none;border:none}.reveal .roll span:after{color:#fff;background:var(--r-link-color-dark)}.reveal .r-frame{border:4px solid var(--r-main-color);box-shadow:0 0 10px rgba(0,0,0,.15)}.reveal a .r-frame{transition:all .15s linear}.reveal a:hover .r-frame{border-color:var(--r-link-color);box-shadow:0 0 20px rgba(0,0,0,.55)}.reveal .controls{color:var(--r-link-color)}.reveal .progress{background:rgba(0,0,0,.2);color:var(--r-link-color)}@media print{.backgrounds{background-color:var(--r-background-color)}}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}tr.header>th>p:last-of-type{margin-bottom:0px}table,table.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-bottom:.5rem;text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:#fff}details>summary>p:only-child{display:inline}pre.sourceCode,code.sourceCode{position:relative}dd code:not(.sourceCode),p code:not(.sourceCode){white-space:pre-wrap}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x),.knitsql-table:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}a{text-underline-offset:3px}.callout pre.sourceCode{padding-left:0}div.ansi-escaped-output{font-family:monospace;display:block}/*!
+*
+* ansi colors from IPython notebook's
+*
+* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since
+* that seems to be what ansi_up emits
+*
+*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #191919;--quarto-body-color: #fff;--quarto-text-muted: white;--quarto-border-color: white;--quarto-border-width: 1px;--quarto-border-radius: 4px}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #191919;--mermaid-edge-color: #999;--mermaid-node-fg-color: #fff;--mermaid-fg-color: #fff;--mermaid-fg-color--lighter: white;--mermaid-fg-color--lightest: white;--mermaid-font-family: Source Sans Pro, Helvetica, sans-serif;--mermaid-label-bg-color: #191919;--mermaid-label-fg-color: #2a76dd;--mermaid-node-bg-color: rgba(42, 118, 221, 0.1);--mermaid-node-fg-color: #fff}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}pre.sourceCode:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(255, 255, 255)" viewBox="0 0 16 16"><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/></svg>');background-repeat:no-repeat;background-size:1rem 1rem}pre.sourceCode:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(255, 255, 255)" viewBox="0 0 16 16"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>')}pre.sourceCode:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(66, 175, 250)" viewBox="0 0 16 16"><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/></svg>')}pre.sourceCode:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(66, 175, 250)"  viewBox="0 0 16 16"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>')}.panel-tabset [role=tablist]{border-bottom:1px solid #fff;list-style:none;margin:0;padding:0;width:100%}.panel-tabset [role=tablist] *{-webkit-box-sizing:border-box;box-sizing:border-box}@media(min-width: 30em){.panel-tabset [role=tablist] li{display:inline-block}}.panel-tabset [role=tab]{border:1px solid rgba(0,0,0,0);border-top-color:#fff;display:block;padding:.5em 1em;text-decoration:none}@media(min-width: 30em){.panel-tabset [role=tab]{border-top-color:rgba(0,0,0,0);display:inline-block;margin-bottom:-1px}}.panel-tabset [role=tab][aria-selected=true]{background-color:#fff}@media(min-width: 30em){.panel-tabset [role=tab][aria-selected=true]{background-color:rgba(0,0,0,0);border:1px solid #fff;border-bottom-color:#191919}}@media(min-width: 30em){.panel-tabset [role=tab]:hover:not([aria-selected=true]){border:1px solid #fff}}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.reveal.center .slide aside,.reveal.center .slide div.aside{position:initial}section.has-light-background,section.has-light-background h1,section.has-light-background h2,section.has-light-background h3,section.has-light-background h4,section.has-light-background h5,section.has-light-background h6{color:#222}section.has-light-background a,section.has-light-background a:hover{color:#2a76dd}section.has-light-background code{color:#4758ab}section.has-dark-background,section.has-dark-background h1,section.has-dark-background h2,section.has-dark-background h3,section.has-dark-background h4,section.has-dark-background h5,section.has-dark-background h6{color:#fff}section.has-dark-background a,section.has-dark-background a:hover{color:#42affa}section.has-dark-background code{color:#ffa07a}#title-slide,div.reveal div.slides section.quarto-title-block{text-align:center}#title-slide .subtitle,div.reveal div.slides section.quarto-title-block .subtitle{margin-bottom:2.5rem}.reveal .slides{text-align:left}.reveal .title-slide h1{font-size:1.6em}.reveal[data-navigation-mode=linear] .title-slide h1{font-size:2em}.reveal div.sourceCode{border:1px solid #fff;border-radius:4px}.reveal pre{width:100%;box-shadow:none;background-color:#191919;border:none;margin:0;font-size:.55em;line-height:1.3;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal pre code{background-color:#191919;font-size:inherit;color:#fff;font-family:inherit}.reveal pre.sourceCode code{color:#fff;font-size:inherit;background-color:inherit;white-space:pre;font-family:inherit;padding:6px 9px;max-height:500px}.reveal .code-with-filename .code-with-filename-file pre{background-color:unset}.reveal code{color:var(--quarto-hl-fu-color);font-size:.875em;background-color:rgba(0,0,0,0);white-space:pre-wrap;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal .column-output-location{display:flex;align-items:stretch}.reveal .column-output-location .column:first-of-type div.sourceCode{height:100%;background-color:#191919}.reveal blockquote{display:block;position:relative;color:#fff;width:unset;margin:var(--r-block-margin) auto;padding:.625rem 1.75rem;border-left:.25rem solid #fff;font-style:normal;background:none;box-shadow:none}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:block}.reveal .slide aside,.reveal .slide div.aside{position:absolute;bottom:20px;font-size:0.7em;color:#fff}.reveal .slide sup{font-size:0.7em}.reveal .slide.scrollable aside,.reveal .slide.scrollable div.aside{position:relative;margin-top:1em}.reveal .slide aside .aside-footnotes{margin-bottom:0}.reveal .slide aside .aside-footnotes li:first-of-type{margin-top:0}.reveal .layout-sidebar{display:flex;width:100%;margin-top:.8em}.reveal .layout-sidebar .panel-sidebar{width:270px}.reveal .layout-sidebar-left .panel-sidebar{margin-right:calc(0.5em*2)}.reveal .layout-sidebar-right .panel-sidebar{margin-left:calc(0.5em*2)}.reveal .layout-sidebar .panel-fill,.reveal .layout-sidebar .panel-center,.reveal .layout-sidebar .panel-tabset{flex:1}.reveal .panel-input,.reveal .panel-sidebar{font-size:.5em;padding:.5em;border-style:solid;border-color:#fff;border-width:1px;border-radius:4px;background-color:rgba(233,236,239,.2)}.reveal .panel-sidebar :first-child,.reveal .panel-fill :first-child{margin-top:0}.reveal .panel-sidebar :last-child,.reveal .panel-fill :last-child{margin-bottom:0}.panel-input>div,.panel-input>div>div{vertical-align:middle;padding-right:1em}.reveal p,.reveal .slides section,.reveal .slides section>section{line-height:1.3}.reveal.smaller .slides section{font-size:0.7em}.reveal.smaller .slides section section{font-size:inherit}.reveal.smaller .slides h1{font-size:calc(2em/0.7)}.reveal.smaller .slides h2{font-size:calc(1.6em/0.7)}.reveal.smaller .slides h3{font-size:calc(1.3em/0.7)}.reveal .slides section.smaller{font-size:0.7em}.reveal .slides section.smaller h1{font-size:calc(2em/0.7)}.reveal .slides section.smaller h2{font-size:calc(1.6em/0.7)}.reveal .slides section.smaller h3{font-size:calc(1.3em/0.7)}.reveal .slides section div.callout{font-size:0.7em}.reveal .slides section div.callout h1{font-size:calc(2em/0.7)}.reveal .slides section div.callout h2{font-size:calc(1.6em/0.7)}.reveal .slides section div.callout h3{font-size:calc(1.3em/0.7)}.reveal .columns>.column>:not(ul,ol){margin-left:.25rem;margin-right:.25rem}.reveal .columns>.column:first-child>:not(ul,ol){margin-right:.5rem;margin-left:0}.reveal .columns>.column:last-child>:not(ul,ol){margin-right:0;margin-left:.5rem}.reveal .slide-number{color:#74c4fb;background-color:#191919}.reveal .footer{color:#fff}.reveal .footer a{color:#42affa}.reveal .footer.has-dark-background{color:#fff}.reveal .footer.has-dark-background a{color:#7bc6fa}.reveal .footer.has-light-background{color:#505050}.reveal .footer.has-light-background a{color:#6a9bdd}.reveal .slide-number{color:#fff}.reveal .slide-number.has-dark-background{color:#fff}.reveal .slide-number.has-light-background{color:#505050}.reveal .slide figure>figcaption,.reveal .slide img.stretch+p.caption,.reveal .slide img.r-stretch+p.caption{font-size:0.7em}@media screen and (min-width: 500px){.reveal .controls[data-controls-layout=edges] .navigate-left{left:.2em}.reveal .controls[data-controls-layout=edges] .navigate-right{right:.2em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.4em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:2.3em}}.tippy-box[data-theme~=light-border]{background-color:#191919;color:#fff;border-radius:4px;border:solid 1px #fff;font-size:.6em}.tippy-box[data-theme~=light-border] .tippy-arrow{color:#fff}.tippy-box[data-placement^=bottom]>.tippy-content{padding:7px 10px;z-index:1}.reveal .panel-tabset [role=tab]{padding:.25em .7em}.reveal .slide-menu-button .fa-bars::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(66, 175, 250)" class="bi bi-list" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/></svg>')}.reveal .slide-chalkboard-buttons .fa-easel2::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(66, 175, 250)" class="bi bi-easel2" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 0a.5.5 0 0 1 .447.276L8.81 1h4.69A1.5 1.5 0 0 1 15 2.5V11h.5a.5.5 0 0 1 0 1h-2.86l.845 3.379a.5.5 0 0 1-.97.242L12.11 14H3.89l-.405 1.621a.5.5 0 0 1-.97-.242L3.36 12H.5a.5.5 0 0 1 0-1H1V2.5A1.5 1.5 0 0 1 2.5 1h4.691l.362-.724A.5.5 0 0 1 8 0ZM2 11h12V2.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5V11Zm9.61 1H4.39l-.25 1h7.72l-.25-1Z"/></svg>')}.reveal .slide-chalkboard-buttons .fa-brush::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(66, 175, 250)" class="bi bi-brush" viewBox="0 0 16 16"><path d="M15.825.12a.5.5 0 0 1 .132.584c-1.53 3.43-4.743 8.17-7.095 10.64a6.067 6.067 0 0 1-2.373 1.534c-.018.227-.06.538-.16.868-.201.659-.667 1.479-1.708 1.74a8.118 8.118 0 0 1-3.078.132 3.659 3.659 0 0 1-.562-.135 1.382 1.382 0 0 1-.466-.247.714.714 0 0 1-.204-.288.622.622 0 0 1 .004-.443c.095-.245.316-.38.461-.452.394-.197.625-.453.867-.826.095-.144.184-.297.287-.472l.117-.198c.151-.255.326-.54.546-.848.528-.739 1.201-.925 1.746-.896.126.007.243.025.348.048.062-.172.142-.38.238-.608.261-.619.658-1.419 1.187-2.069 2.176-2.67 6.18-6.206 9.117-8.104a.5.5 0 0 1 .596.04zM4.705 11.912a1.23 1.23 0 0 0-.419-.1c-.246-.013-.573.05-.879.479-.197.275-.355.532-.5.777l-.105.177c-.106.181-.213.362-.32.528a3.39 3.39 0 0 1-.76.861c.69.112 1.736.111 2.657-.12.559-.139.843-.569.993-1.06a3.122 3.122 0 0 0 .126-.75l-.793-.792zm1.44.026c.12-.04.277-.1.458-.183a5.068 5.068 0 0 0 1.535-1.1c1.9-1.996 4.412-5.57 6.052-8.631-2.59 1.927-5.566 4.66-7.302 6.792-.442.543-.795 1.243-1.042 1.826-.121.288-.214.54-.275.72v.001l.575.575zm-4.973 3.04.007-.005a.031.031 0 0 1-.007.004zm3.582-3.043.002.001h-.002z"/></svg>')}/*! dark */.reveal div.callout.callout-style-default .callout-title{color:#222}.reveal ol[type=a]{list-style-type:lower-alpha}.reveal ol[type=a s]{list-style-type:lower-alpha}.reveal ol[type=A s]{list-style-type:upper-alpha}.reveal ol[type=i]{list-style-type:lower-roman}.reveal ol[type=i s]{list-style-type:lower-roman}.reveal ol[type=I s]{list-style-type:upper-roman}.reveal ol[type="1"]{list-style-type:decimal}.reveal ul.task-list{list-style:none}.reveal ul.task-list li input[type=checkbox]{width:2em;height:2em;margin:0 1em .5em -1.6em;vertical-align:middle}div.cell-output-display div.pagedtable-wrapper table.table{font-size:.6em}.reveal .code-annotation-container-hidden{display:none}.reveal code.sourceCode button.code-annotation-anchor,.reveal code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;font-size:.7em;line-height:1.2em;margin-top:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.reveal code.sourceCode button.code-annotation-anchor{cursor:pointer}.reveal code.sourceCode a.code-annotation-anchor{text-align:center;vertical-align:middle;text-decoration:none;cursor:default;height:1.2em;width:1.2em}.reveal code.sourceCode.fragment a.code-annotation-anchor{left:auto}.reveal #code-annotation-line-highlight-gutter{width:100%;border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2}.reveal #code-annotation-line-highlight{margin-left:-8em;width:calc(100% + 4em);border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2;margin-bottom:-2px}.reveal code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#191919;font-weight:bolder}.reveal pre.code-annotation-code{padding-top:0;padding-bottom:0}.reveal pre.code-annotation-code code{z-index:3;padding-left:0px}.reveal dl.code-annotation-container-grid{margin-left:.1em}.reveal dl.code-annotation-container-grid dt{margin-top:.65rem;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;border:solid #fff 1px;border-radius:50%;height:1.3em;width:1.3em;line-height:1.3em;font-size:.5em;text-align:center;vertical-align:middle;text-decoration:none}.reveal dl.code-annotation-container-grid dd{margin-left:.25em}.reveal .scrollable ol li:first-child:nth-last-child(n+10),.reveal .scrollable ol li:first-child:nth-last-child(n+10)~li{margin-left:1em}kbd{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:36px;color:#fff;background-color:#4a4b4b;border:1px solid;border-color:#fff;border-radius:5px;padding:.4rem .4rem}:root{--r-inline-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-block-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-inline-code-font-size: 0.875em;--r-block-code-font-size: 0.55em}.reveal a{font-weight:400;background-color:rgba(0,0,0,0);text-decoration:inherit}.reveal div.callout{margin-top:1rem;margin-bottom:1rem;border-radius:4px;overflow-wrap:break-word}.reveal div.callout.callout-style-simple,.reveal div.callout.callout-style-default{border-left:.3rem solid #acacac;border-right:solid 1px #fff;border-top:solid 1px #fff;border-bottom:solid 1px #fff}.reveal div.callout.callout-style-simple div.callout-body,.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-body,.reveal div.callout.callout-style-default div.callout-title{font-size:inherit;border-bottom:none;font-weight:600}.reveal div.callout.callout-style-simple div.callout-title p,.reveal div.callout.callout-style-default div.callout-title p{margin-top:.5em;margin-bottom:.5em}.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-title{display:flex;align-items:center}.reveal div.callout.callout-style-simple .callout-icon::before,.reveal div.callout.callout-style-default .callout-icon::before{height:1.25em;width:1.25em;background-size:1.25em 1.25em}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child,.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child{margin-bottom:var(--r-block-margin)}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode),.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode){padding-bottom:.5rem;margin-bottom:0}.reveal div.callout.callout-style-simple.callout-titled .callout-icon::before,.reveal div.callout.callout-style-default.callout-titled .callout-icon::before{margin-top:.25em;padding-right:.25em}.reveal div.callout.callout-style-simple.no-icon::before,.reveal div.callout.callout-style-default.no-icon::before{display:none !important}.reveal div.callout.callout-style-simple{padding:0em .5em;display:flex}.reveal div.callout.callout-style-simple.callout-titled .callout-body{margin-top:.2em}.reveal div.callout.callout-style-simple.callout-titled:not(.no-icon) .callout-content{padding-left:1.6em}.reveal div.callout.callout-style-simple.callout-titled .callout-content p{margin-top:0}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body{display:flex}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-icon::before{margin-top:var(--r-block-margin);padding-right:.5em}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>div.sourceCode:last-child{margin-bottom:1rem}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>:first-child{margin-top:var(--r-block-margin)}.reveal div.callout.callout-style-simple .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-simple div.callout-title{opacity:75%}.reveal div.callout.callout-style-simple div.callout-body{font-weight:400}.reveal div.callout.callout-style-default.callout-titled .callout-content p{margin-top:.7em}.reveal div.callout.callout-style-default .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-default div.callout-body{font-weight:400}.reveal div.callout.callout-style-default div.callout-title{opacity:85%;padding-left:.5em;padding-right:.5em}.reveal div.callout.callout-style-default div.callout-content{padding-left:.5em;padding-right:.5em}.reveal div.callout .callout-body-container{flex-grow:1}.reveal div.callout.callout-note{border-left-color:#0d6efd}.reveal div.callout.callout-note.callout-style-default .callout-title{background-color:#04214c}.reveal div.callout.callout-note .callout-icon::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill: %230c63e4" class="bi bi-info-circle" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/><path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/></svg>');}.reveal div.callout.callout-tip{border-left-color:#198754}.reveal div.callout.callout-tip.callout-style-default .callout-title{background-color:#082919}.reveal div.callout.callout-tip .callout-icon::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill: %23177a4c" class="bi bi-lightbulb" viewBox="0 0 16 16"><path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/></svg>');}.reveal div.callout.callout-warning{border-left-color:#ffc107}.reveal div.callout.callout-warning.callout-style-default .callout-title{background-color:#4d3a02}.reveal div.callout.callout-warning .callout-icon::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill: %23e6ae06" class="bi bi-exclamation-triangle" viewBox="0 0 16 16"><path d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.146.146 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.163.163 0 0 1-.054.06.116.116 0 0 1-.066.017H1.146a.115.115 0 0 1-.066-.017.163.163 0 0 1-.054-.06.176.176 0 0 1 .002-.183L7.884 2.073a.147.147 0 0 1 .054-.057zm1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566z"/><path d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995z"/></svg>');}.reveal div.callout.callout-caution{border-left-color:#fd7e14}.reveal div.callout.callout-caution.callout-style-default .callout-title{background-color:#4c2606}.reveal div.callout.callout-caution .callout-icon::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill: %23e47112" class="bi bi-cone-striped" viewBox="0 0 16 16"><path d="M9.97 4.88l.953 3.811C10.158 8.878 9.14 9 8 9c-1.14 0-2.159-.122-2.923-.309L6.03 4.88C6.635 4.957 7.3 5 8 5s1.365-.043 1.97-.12zm-.245-.978L8.97.88C8.718-.13 7.282-.13 7.03.88L6.274 3.9C6.8 3.965 7.382 4 8 4c.618 0 1.2-.036 1.725-.098zm4.396 8.613a.5.5 0 0 1 .037.96l-6 2a.5.5 0 0 1-.316 0l-6-2a.5.5 0 0 1 .037-.96l2.391-.598.565-2.257c.862.212 1.964.339 3.165.339s2.303-.127 3.165-.339l.565 2.257 2.391.598z"/></svg>');}.reveal div.callout.callout-important{border-left-color:#dc3545}.reveal div.callout.callout-important.callout-style-default .callout-title{background-color:#421015}.reveal div.callout.callout-important .callout-icon::before{background-image:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill: %23c6303e" class="bi bi-exclamation-circle" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/><path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/></svg>');}.reveal .quarto-title-block .quarto-title-authors{display:flex;justify-content:center}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author{padding-left:.5em;padding-right:.5em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:hover,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:visited,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:active{color:inherit;text-decoration:none}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-name{margin-bottom:.1rem}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-email{margin-top:0px;margin-bottom:.4em;font-size:.6em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-orcid img{margin-bottom:4px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation{font-size:.7em;margin-top:0px;margin-bottom:8px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation:first{margin-top:12px}.reveal .footer{font-size:.6em !important}.reveal .slide strong{font-weight:900;color:#a0e9ff}.reveal .slide h3{color:#a0e9ff}:root{--quarto-scss-export-highlight-font-color: #A0E9FF;--quarto-scss-export-body-bg: #191919;--quarto-scss-export-body-color: #fff;--quarto-scss-export-link-color: #42affa;--quarto-scss-export-input-panel-bg: rgba(233, 236, 239, 0.2);--quarto-scss-export-link-color-bg: transparent;--quarto-scss-export-text-muted: white;--quarto-scss-export-gray-200: #e9ecef;--quarto-scss-export-gray-100: #f8f9fa;--quarto-scss-export-gray-900: #212529;--quarto-scss-export-primary: #2a76dd;--quarto-scss-export-link-color-hover: #74c4fb;--quarto-scss-export-selection-bg: #bee4fd;--quarto-scss-export-selection-color: #191919;--quarto-scss-export-border-color: white;--quarto-scss-export-presentation-heading-color: #fff;--quarto-scss-export-presentation-list-bullet-color: #fff;--quarto-scss-export-code-block-bg: #191919;--quarto-scss-export-code-block-border-color: white;--quarto-scss-export-code-block-color: #fff;--quarto-scss-export-code-bg: transparent;--quarto-scss-export-tabset-border-color: white;--quarto-scss-export-table-border-color: white;--quarto-scss-export-input-panel-border-color: white;--quarto-scss-export-callout-color-note: #0d6efd;--quarto-scss-export-callout-color-tip: #198754;--quarto-scss-export-callout-color-important: #dc3545;--quarto-scss-export-callout-color-caution: #fd7e14;--quarto-scss-export-callout-color-warning: #ffc107;--quarto-scss-export-light-bg-text-color: #222;--quarto-scss-export-dark-bg-text-color: #fff;--quarto-scss-export-light-bg-link-color: #2a76dd;--quarto-scss-export-dark-bg-link-color: #42affa;--quarto-scss-export-light-bg-code-color: #4758ab;--quarto-scss-export-dark-bg-code-color: #ffa07a;--quarto-scss-export-kbd-color: #fff;--quarto-scss-export-kbd-bg: #f8f9fa;--quarto-scss-export-revealjs-heading-color: #fff;--quarto-scss-export-revealjs-list-bullet-color: #fff;--quarto-scss-export-backgroundColor: #191919;--quarto-scss-export-mainColor: #fff;--quarto-scss-export-headingColor: #fff;--quarto-scss-export-linkColor: #42affa;--quarto-scss-export-linkColorHover: #74c4fb;--quarto-scss-export-selectionBackgroundColor: #bee4fd;--quarto-scss-export-selectionColor: #191919;--quarto-scss-export-btn-code-copy-color: white;--quarto-scss-export-btn-code-copy-color-active: #42affa;--quarto-scss-export-secondary: #999;--quarto-scss-export-mermaid-bg-color: #191919;--quarto-scss-export-mermaid-edge-color: #999;--quarto-scss-export-mermaid-node-fg-color: #fff;--quarto-scss-export-mermaid-fg-color: #fff;--quarto-scss-export-mermaid-fg-color--lighter: white;--quarto-scss-export-mermaid-fg-color--lightest: white;--quarto-scss-export-mermaid-label-bg-color: #191919;--quarto-scss-export-mermaid-label-fg-color: #2a76dd;--quarto-scss-export-mermaid-node-bg-color: rgba(42, 118, 221, 0.1)}
+\ No newline at end of file
diff --git a/_freeze/slides/02/execute-results/html.json b/_freeze/slides/02/execute-results/html.json
@@ -1,8 +1,8 @@
 {
-  "hash": "387178242b89e67960838bbc8e8752bc",
+  "hash": "5d45e2f475170b2f4aeae3a9e7c745dc",
   "result": {
     "engine": "knitr",
-    "markdown": "---\nengine: knitr\ntitle: Names and values\n---\n\n## Learning objectives\n\n- Distinguish between an *object* and its *name*.\n- Identify when data are *copied* versus *modified*.\n- Trace and identify the memory used by R.\n\nThe `{lobstr}` package will help us throughout the chapter\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(lobstr)\n```\n:::\n\n\n\n## Syntactic names are easier to create and work with than non-syntactic names\n\n\n- Syntactic names: `my_variable`, `x`, `cpp11`, `.by`.\n  - Can't use names in `?Reserved`\n\n- Non-syntactic names need to be surrounded in backticks. \n\n## Names are *bound to* values with `<-`\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- c(1, 2, 3)\na\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bfbc968\"\n```\n\n\n:::\n:::\n\n\n## Many names can be bound to the same values\n\n\n::: {.cell}\n\n```{.r .cell-code}\nb <- a\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bfbc968\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bfbc968\"\n```\n\n\n:::\n:::\n\n\n## If shared values are modified, the object is copied to a new address\n\n\n::: {.cell}\n\n```{.r .cell-code}\nb[[1]] <- 5\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bfbc968\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226fda7278\"\n```\n\n\n:::\n:::\n\n\n## Memory addresses can differ even if objects seem the same\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- 1:10\nb <- a\nc <- 1:10\n\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x22271c9e7b0\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x22271c9e7b0\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(c)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x22271d77708\"\n```\n\n\n:::\n:::\n\n\n## Functions have a single address regardless of how they're referenced\n\n\n::: {.cell}\n\n```{.r .cell-code}\nobj_addr(mean)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226f891738\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(base::mean)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226f891738\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(get(\"mean\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226f891738\"\n```\n\n\n:::\n:::\n\n\n## Unlike most objects, environments keep the same memory address on modify\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd <- new.env()\nobj_addr(d)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226b7489d8\"\n```\n\n\n:::\n\n```{.r .cell-code}\ne <- d\ne[['a']] <- 1\nobj_addr(e)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226b7489d8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(d)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226b7489d8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nd[['a']]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Use `tracemem` to validate if values are copied or modified\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- runif(10)\ntracemem(x)\n#> [1] \"<000001F4185B4B08>\"\ny <- x\nx[[1]] <- 10\n#> tracemem[0x000001f4185b4b08 -> 0x000001f4185b4218]:\nuntracemem(x)\n```\n:::\n\n\n## `tracemem` shows internal C code minimizes copying\n\n\n::: {.cell}\n\n```{.r .cell-code}\ny <- as.list(x)\ntracemem(y)\n#> [1] \"<000001AD67FDCD38>\"\nmedians <- vapply(x, median, numeric(1))\nfor (i in 1:5) {\n  y[[i]] <- y[[i]] - medians[[i]]\n}\n#> tracemem[0x000001ad67fdcd38 -> 0x000001ad61982638]:\nuntracemem(y)\n```\n:::\n\n\n## A function's environment follows copy-on-modify rules\n\n:::: {.columns}\n\n::: {.column}\n\n::: {.cell}\n\n```{.r .cell-code}\nf <- function(a) {\n  a\n}\n\nx <- c(1, 2, 3)\nz <- f(x) # No change in value\n\nobj_addr(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bdb8738\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(z) # No address change \n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226bdb8738\"\n```\n\n\n:::\n:::\n\n:::\n\n::: {.column}\n\n:::\n\n::::\n\n::: notes\n- Diagrams will be explained more in chapter 7.\n- `a` points to same address as `x`.\n- If `a` modified inside function, `z` would have new address.\n:::\n\n\n## `ref()` shows the memory address of a list and its *elements*\n\n:::: {.columns}\n\n::: {.column}\n\n::: {.cell}\n\n```{.r .cell-code}\nl1 <- list(1, 2, 3)\nobj_addr(l1)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x2226c315db8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nl2 <- l1\nl2[[3]] <- 4\nref(l1, l2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x2226c315db8] <list> \n#> ├─[2:0x2226c730078] <dbl> \n#> ├─[3:0x2226c75cdc8] <dbl> \n#> └─[4:0x2226c75cc08] <dbl> \n#>  \n#> █ [5:0x2226c36cd68] <list> \n#> ├─[2:0x2226c730078] \n#> ├─[3:0x2226c75cdc8] \n#> └─[6:0x2226c75b318] <dbl>\n```\n\n\n:::\n:::\n\n:::\n\n::: {.column}\n{width=50%}\n:::\n\n::::\n\n## Since dataframes are lists of (column) vectors, mutating a column modifies only that column\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3))\nd2 <- d1\nd2[, 2] <- d2[, 2] * 2\nref(d1, d2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x2226cca93c8] <df[,2]> \n#> ├─x = [2:0x22272216148] <dbl> \n#> └─y = [3:0x222722160f8] <dbl> \n#>  \n#> █ [4:0x2226ce0cf48] <df[,2]> \n#> ├─x = [2:0x22272216148] \n#> └─y = [5:0x222727b0c38] <dbl>\n```\n\n\n:::\n:::\n\n\n## Since dataframes are lists of (column) vectors, mutating a row modifies the value\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3))\nd2 <- d1\nd2[1, ] <- d2[1, ] * 2\nref(d1, d2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x22272912588] <df[,2]> \n#> ├─x = [2:0x222730bd408] <dbl> \n#> └─y = [3:0x222730bd3b8] <dbl> \n#>  \n#> █ [4:0x22272b0e548] <df[,2]> \n#> ├─x = [5:0x222731501d8] <dbl> \n#> └─y = [6:0x22273150188] <dbl>\n```\n\n\n:::\n:::\n\n\n::: notes\n- Here \"mutate\" means \"change\", not `dplyr::mutate()`\n:::\n\n## Characters are unique due to the global string pool\n\n:::: {.columns}\n\n::: {.column}\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:4\nref(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1:0x22272f78460] <int>\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- 1:4\nref(y)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1:0x222730899d8] <int>\n```\n\n\n:::\n\n```{.r .cell-code}\nx <- c(\"a\", \"a\", \"b\")\nref(x, character = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x2227394f1d8] <chr> \n#> ├─[2:0x22267d9b118] <string: \"a\"> \n#> ├─[2:0x22267d9b118] \n#> └─[3:0x2226e17f3b8] <string: \"b\">\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- c(\"a\")\nref(y, character = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x2227397fce8] <chr> \n#> └─[2:0x22267d9b118] <string: \"a\">\n```\n\n\n:::\n:::\n\n:::\n\n::: {.column}\n\n:::\n\n::::\n\n::: notes\n- \"a\" is always at the same address.\n- Each member of character vector has its own address (kind of list-like).\n:::\n\n## Memory amount can also be measured, using `lobstr::obj_size`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nbanana <- \"bananas bananas bananas\"\nobj_addr(banana)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x22271bc25b8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_size(banana)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 136 B\n```\n\n\n:::\n:::\n\n\n## Alternative Representation or ALTREPs represent vector values efficiently\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:10\nobj_size(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 680 B\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- 1:10000\nobj_size(y)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 680 B\n```\n\n\n:::\n:::\n\n\n## We can measure memory & speed using `bench::mark()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmed <- function(d, medians) {\n  for (i in seq_along(medians)) {\n    d[[i]] <- d[[i]] - medians[[i]]\n  }\n}\nx <- data.frame(matrix(runif(5 * 1e4), ncol = 5))\nmedians <- vapply(x, median, numeric(1))\ny <- as.list(x)\n\nbench::mark(\n  \"data.frame\" = med(x, medians),\n  \"list\" = med(y, medians)\n)[, c(\"min\", \"median\", \"mem_alloc\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 2 × 3\n#>        min   median mem_alloc\n#>   <bch:tm> <bch:tm> <bch:byt>\n#> 1   52.7µs   66.6µs     491KB\n#> 2   22.8µs   33.7µs     391KB\n```\n\n\n:::\n:::\n\n\n::: notes\n- The thing to see: list version uses less RAM and is faster\n:::\n\n## The garbage collector `gc()` explicitly clears out unbound objects\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:3\nx <- 2:4 # \"1:3\" is orphaned\nrm(x) # \"2:4\" is orphaned\ngc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>           used (Mb) gc trigger (Mb) max used (Mb)\n#> Ncells  791104 42.3    1505464 80.5  1505464 80.5\n#> Vcells 1497631 11.5    8388608 64.0  8388482 64.0\n```\n\n\n:::\n\n```{.r .cell-code}\nlobstr::mem_used() # Wrapper around gc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 56.29 MB\n```\n\n\n:::\n:::\n\n\n::: aside\n`gc()` runs automatically, never *need* to call\n:::\n\n::: notes\n- `mem_used()` multiplies Ncells \"used\" by either 28 (32-bit architecture) or 56 (64-bit architecture)., and Vcells \"used\" by 8, adds them, and converts to Mb.\n:::",
+    "markdown": "---\nengine: knitr\ntitle: Names and values\n---\n\n## Learning objectives\n\n- Distinguish between an *object* and its *name*.\n- Identify when data are *copied* versus *modified*.\n- Trace and identify the memory used by R.\n\nThe `{lobstr}` package will help us throughout the chapter\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(lobstr)\n```\n:::\n\n\n\n## Syntactic names are easier to create and work with than non-syntactic names\n\n\n- Syntactic names: `my_variable`, `x`, `cpp11`, `.by`.\n  - Can't use names in `?Reserved`\n\n- Non-syntactic names need to be surrounded in backticks. \n\n## Names are *bound to* values with `<-`\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- c(1, 2, 3)\na\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dfae968\"\n```\n\n\n:::\n:::\n\n\n## Many names can be bound to the same values\n\n\n::: {.cell}\n\n```{.r .cell-code}\nb <- a\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dfae968\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dfae968\"\n```\n\n\n:::\n:::\n\n\n## If shared values are modified, the object is copied to a new address\n\n\n::: {.cell}\n\n```{.r .cell-code}\nb[[1]] <- 5\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dfae968\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x16249d8f228\"\n```\n\n\n:::\n:::\n\n\n## Memory addresses can differ even if objects seem the same\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- 1:10\nb <- a\nc <- 1:10\n\nobj_addr(a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624bd3f5e8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624bd3f5e8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(c)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624bd7a540\"\n```\n\n\n:::\n:::\n\n\n## Functions have a single address regardless of how they're referenced\n\n\n::: {.cell}\n\n```{.r .cell-code}\nobj_addr(mean)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x162498c1738\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(base::mean)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x162498c1738\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(get(\"mean\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x162498c1738\"\n```\n\n\n:::\n:::\n\n\n## Unlike most objects, environments keep the same memory address on modify\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd <- new.env()\nobj_addr(d)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624d749900\"\n```\n\n\n:::\n\n```{.r .cell-code}\ne <- d\ne[['a']] <- 1\nobj_addr(e)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624d749900\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(d)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624d749900\"\n```\n\n\n:::\n\n```{.r .cell-code}\nd[['a']]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Use `tracemem` to validate if values are copied or modified\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- runif(10)\ntracemem(x)\n#> [1] \"<000001F4185B4B08>\"\ny <- x\nx[[1]] <- 10\n#> tracemem[0x000001f4185b4b08 -> 0x000001f4185b4218]:\nuntracemem(x)\n```\n:::\n\n\n## `tracemem` shows internal C code minimizes copying\n\n\n::: {.cell}\n\n```{.r .cell-code}\ny <- as.list(x)\ntracemem(y)\n#> [1] \"<000001AD67FDCD38>\"\nmedians <- vapply(x, median, numeric(1))\nfor (i in 1:5) {\n  y[[i]] <- y[[i]] - medians[[i]]\n}\n#> tracemem[0x000001ad67fdcd38 -> 0x000001ad61982638]:\nuntracemem(y)\n```\n:::\n\n\n## A function's environment follows copy-on-modify rules\n\n:::: columns\n\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\nf <- function(a) {\n  a\n}\n\nx <- c(1, 2, 3)\nz <- f(x) # No change in value\n\nobj_addr(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dda26e8\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_addr(z) # No address change \n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624dda26e8\"\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n\n:::\n\n::::\n\n::: notes\n- Diagrams will be explained more in chapter 7.\n- `a` points to same address as `x`.\n- If `a` modified inside function, `z` would have new address.\n:::\n\n\n## `ref()` shows the memory address of a list and its *elements*\n\n:::: columns\n\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\nl1 <- list(1, 2, 3)\nobj_addr(l1)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624e315d68\"\n```\n\n\n:::\n\n```{.r .cell-code}\nl2 <- l1\nl2[[3]] <- 4\nref(l1, l2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x1624e315d68] <list> \n#> ├─[2:0x1624e77ebd0] <dbl> \n#> ├─[3:0x1624e77ea10] <dbl> \n#> └─[4:0x1624e77e850] <dbl> \n#>  \n#> █ [5:0x1624e34ed18] <list> \n#> ├─[2:0x1624e77ebd0] \n#> ├─[3:0x1624e77ea10] \n#> └─[6:0x1624e754e70] <dbl>\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n{width=50%}\n:::\n\n::::\n\n## Since dataframes are lists of (column) vectors, mutating a column modifies only that column\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3))\nd2 <- d1\nd2[, 2] <- d2[, 2] * 2\nref(d1, d2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x1624ec8d348] <df[,2]> \n#> ├─x = [2:0x1624f230148] <dbl> \n#> └─y = [3:0x1624f2300f8] <dbl> \n#>  \n#> █ [4:0x1624ee2aec8] <df[,2]> \n#> ├─x = [2:0x1624f230148] \n#> └─y = [5:0x1624f7aab98] <dbl>\n```\n\n\n:::\n:::\n\n\n## Since dataframes are lists of (column) vectors, mutating a row modifies the value\n\n\n::: {.cell}\n\n```{.r .cell-code}\nd1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3))\nd2 <- d1\nd2[1, ] <- d2[1, ] * 2\nref(d1, d2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x1624f91a508] <df[,2]> \n#> ├─x = [2:0x16250095318] <dbl> \n#> └─y = [3:0x162500952c8] <dbl> \n#>  \n#> █ [4:0x1624fb324c8] <df[,2]> \n#> ├─x = [5:0x162501700e8] <dbl> \n#> └─y = [6:0x16250170098] <dbl>\n```\n\n\n:::\n:::\n\n\n::: notes\n- Here \"mutate\" means \"change\", not `dplyr::mutate()`\n:::\n\n## Characters are unique due to the global string pool\n\n:::: columns\n\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:4\nref(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1:0x1624ff60cb0] <int>\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- 1:4\nref(y)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1:0x162500b4318] <int>\n```\n\n\n:::\n\n```{.r .cell-code}\nx <- c(\"a\", \"a\", \"b\")\nref(x, character = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x1625089a188] <chr> \n#> ├─[2:0x16241da3118] <string: \"a\"> \n#> ├─[2:0x16241da3118] \n#> └─[3:0x1624818b3b8] <string: \"b\">\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- c(\"a\")\nref(y, character = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> █ [1:0x16250892930] <chr> \n#> └─[2:0x16241da3118] <string: \"a\">\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n\n:::\n\n::::\n\n::: notes\n- \"a\" is always at the same address.\n- Each member of character vector has its own address (kind of list-like).\n:::\n\n## Memory amount can also be measured, using `lobstr::obj_size`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nbanana <- \"bananas bananas bananas\"\nobj_addr(banana)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"0x1624bb90318\"\n```\n\n\n:::\n\n```{.r .cell-code}\nobj_size(banana)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 136 B\n```\n\n\n:::\n:::\n\n\n## Alternative Representation or ALTREPs represent vector values efficiently\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:10\nobj_size(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 680 B\n```\n\n\n:::\n\n```{.r .cell-code}\ny <- 1:10000\nobj_size(y)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 680 B\n```\n\n\n:::\n:::\n\n\n## We can measure memory & speed using `bench::mark()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmed <- function(d, medians) {\n  for (i in seq_along(medians)) {\n    d[[i]] <- d[[i]] - medians[[i]]\n  }\n}\nx <- data.frame(matrix(runif(5 * 1e4), ncol = 5))\nmedians <- vapply(x, median, numeric(1))\ny <- as.list(x)\n\nbench::mark(\n  \"data.frame\" = med(x, medians),\n  \"list\" = med(y, medians)\n)[, c(\"min\", \"median\", \"mem_alloc\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 2 × 3\n#>        min   median mem_alloc\n#>   <bch:tm> <bch:tm> <bch:byt>\n#> 1   52.7µs   71.2µs     491KB\n#> 2   16.8µs   35.1µs     391KB\n```\n\n\n:::\n:::\n\n\n::: notes\n- The thing to see: list version uses less RAM and is faster\n:::\n\n## The garbage collector `gc()` explicitly clears out unbound objects\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:3\nx <- 2:4 # \"1:3\" is orphaned\nrm(x) # \"2:4\" is orphaned\ngc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>           used (Mb) gc trigger (Mb) max used (Mb)\n#> Ncells  791094 42.3    1505455 80.4  1505455 80.4\n#> Vcells 1497588 11.5    8388608 64.0  8388528 64.0\n```\n\n\n:::\n\n```{.r .cell-code}\nlobstr::mem_used() # Wrapper around gc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 56.29 MB\n```\n\n\n:::\n:::\n\n\n::: aside\n`gc()` runs automatically, never *need* to call\n:::\n\n::: notes\n- `mem_used()` multiplies Ncells \"used\" by either 28 (32-bit architecture) or 56 (64-bit architecture)., and Vcells \"used\" by 8, adds them, and converts to Mb.\n:::",
     "supporting": [
       "02_files"
     ],
diff --git a/_freeze/slides/03/execute-results/html.json b/_freeze/slides/03/execute-results/html.json
@@ -1,8 +1,8 @@
 {
-  "hash": "eb6c217758ae1a65dff62d4b190e7c4e",
+  "hash": "f9d9a682b3ccae551ce90320fcaa1d33",
   "result": {
     "engine": "knitr",
-    "markdown": "---\nengine: knitr\ntitle: Vectors\n---\n\n## Learning objectives:\n\n-   Learn about different types of vectors and their attributes\n-   Navigate through vector types and their value types\n-   Venture into factors and date-time objects\n-   Discuss the differences between data frames and tibbles\n-   Do not get absorbed by the `NA` and `NULL` black hole\n\n\n::: {.cell}\n\n:::\n\n\n\n<details>\n<summary>Session Info</summary>\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> R version 4.5.1 (2025-06-13 ucrt)\n#> Platform: x86_64-w64-mingw32/x64\n#> Running under: Windows 11 x64 (build 26100)\n#> \n#> Matrix products: default\n#>   LAPACK version 3.12.1\n#> \n#> locale:\n#> [1] LC_COLLATE=English_United States.utf8 \n#> [2] LC_CTYPE=English_United States.utf8   \n#> [3] LC_MONETARY=English_United States.utf8\n#> [4] LC_NUMERIC=C                          \n#> [5] LC_TIME=English_United States.utf8    \n#> \n#> time zone: America/Chicago\n#> tzcode source: internal\n#> \n#> attached base packages:\n#> [1] stats     graphics  grDevices utils     datasets  methods   base     \n#> \n#> other attached packages:\n#> [1] palmerpenguins_0.1.1 gt_1.0.0             dplyr_1.1.4         \n#> \n#> loaded via a namespace (and not attached):\n#>  [1] digest_0.6.37     R6_2.6.1          fastmap_1.2.0     tidyselect_1.2.1 \n#>  [5] xfun_0.52         magrittr_2.0.3    glue_1.8.0        tibble_3.3.0     \n#>  [9] knitr_1.50        pkgconfig_2.0.3   htmltools_0.5.8.1 rmarkdown_2.29   \n#> [13] generics_0.1.4    lifecycle_1.0.4   xml2_1.3.8        cli_3.6.5        \n#> [17] vctrs_0.6.5       compiler_4.5.1    tools_4.5.1       pillar_1.11.0    \n#> [21] evaluate_1.0.4    yaml_2.3.10       rlang_1.1.6       jsonlite_2.0.0   \n#> [25] keyring_1.4.1\n```\n\n\n:::\n:::\n\n</details>\n\n## Aperitif\n\n\n\n### Counting Penguins\n\nConsider this code to count the number of Gentoo penguins in the `penguins` data set. We see that there are 124 Gentoo penguins.\n\n\n::: {.cell}\n\n:::\n\n\n### In\n\nOne subtle error can arise in trying out `%in%` here instead.\n\n\n::: {.cell}\n\n:::\n\n\n\n\n### Fix: base R \n\n\n::: {.cell}\n\n:::\n\n\n### Fix: dplyr\n\n\n::: {.cell}\n\n:::\n\n\n### Motivation\n\n* What are the different types of vectors?\n* How does this affect accessing vectors?\n\n<details>\n<summary>Side Quest: Looking up the `%in%` operator</summary>\nIf you want to look up the manual pages for the `%in%` operator with the `?`, use backticks:\n\n\n::: {.cell}\n\n:::\n\n\nand we find that `%in%` is a wrapper for the `match()` function.\n\n</details>\n\n\n## Types of Vectors\n\n \n\nTwo main types:\n\n-   **Atomic**: Elements all the same type.\n-   **List**: Elements are different Types.\n\nClosely related but not technically a vector:\n\n-   **NULL**: Null elements. Often length zero.\n\n## Atomic Vectors\n\n### Types of atomic vectors\n\n \n\n-   **Logical**: True/False\n-   **Integer**: Numeric (discrete, no decimals)\n-   **Double**: Numeric (continuous, decimals)\n-   **Character**: String\n\n### Vectors of Length One\n\n**Scalars** are vectors that consist of a single value.\n\n#### Logicals\n\n\n::: {.cell}\n\n:::\n\n\n#### Doubles\n\n\n::: {.cell}\n\n:::\n\n\n#### Integers\n\nIntegers must be followed by L and cannot have fractional values\n\n\n::: {.cell}\n\n:::\n\n\n<details>\n<summary>Pop Quiz: Why \"L\" for integers?</summary>\nWickham notes that the use of `L` dates back to the **C** programming language and its \"long int\" type for memory allocation.\n</details>\n\n#### Strings\n\nStrings can use single or double quotes and special characters are escaped with \\\n\n\n::: {.cell}\n\n:::\n\n\n### Longer\n\nThere are several ways to make longer vectors:\n\n**1. With single values** inside c() for combine.\n\n\n::: {.cell}\n\n:::\n\n\n \n\n**2. With other vectors**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 4\n```\n\n\n:::\n:::\n\n\n<details>\n<summary>Side Quest: rlang</summary>\n\n`{rlang}` has [vector constructor functions too](https://rlang.r-lib.org/reference/vector-construction.html):\n\n-   `rlang::lgl(...)`\n-   `rlang::int(...)`\n-   `rlang::dbl(...)`\n-   `rlang::chr(...)`\n\nThey look to do both more and less than `c()`.\n\n-   More:\n    -   Enforce type\n    -   Splice lists\n    -   More types: `rlang::bytes()`, `rlang::cpl(...)`\n-   Less:\n    -   Stricter rules on names\n\nNote: currently has `questioning` lifecycle badge, since these constructors may get moved to `vctrs`\n\n</details>\n\n### Type and Length\n\nWe can determine the type of a vector with `typeof()` and its length with `length()`\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n<div id=\"gvfpwanldj\" style=\"padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;\">\n<style>#gvfpwanldj table {\n  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#gvfpwanldj thead, #gvfpwanldj tbody, #gvfpwanldj tfoot, #gvfpwanldj tr, #gvfpwanldj td, #gvfpwanldj th {\n  border-style: none;\n}\n\n#gvfpwanldj p {\n  margin: 0;\n  padding: 0;\n}\n\n#gvfpwanldj .gt_table {\n  display: table;\n  border-collapse: collapse;\n  line-height: normal;\n  margin-left: auto;\n  margin-right: auto;\n  color: #333333;\n  font-size: 16px;\n  font-weight: normal;\n  font-style: normal;\n  background-color: #FFFFFF;\n  width: auto;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #A8A8A8;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #A8A8A8;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_caption {\n  padding-top: 4px;\n  padding-bottom: 4px;\n}\n\n#gvfpwanldj .gt_title {\n  color: #333333;\n  font-size: 125%;\n  font-weight: initial;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-color: #FFFFFF;\n  border-bottom-width: 0;\n}\n\n#gvfpwanldj .gt_subtitle {\n  color: #333333;\n  font-size: 85%;\n  font-weight: initial;\n  padding-top: 3px;\n  padding-bottom: 5px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-color: #FFFFFF;\n  border-top-width: 0;\n}\n\n#gvfpwanldj .gt_heading {\n  background-color: #FFFFFF;\n  text-align: center;\n  border-bottom-color: #FFFFFF;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_bottom_border {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_col_headings {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_col_heading {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 6px;\n  padding-left: 5px;\n  padding-right: 5px;\n  overflow-x: hidden;\n}\n\n#gvfpwanldj .gt_column_spanner_outer {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  padding-top: 0;\n  padding-bottom: 0;\n  padding-left: 4px;\n  padding-right: 4px;\n}\n\n#gvfpwanldj .gt_column_spanner_outer:first-child {\n  padding-left: 0;\n}\n\n#gvfpwanldj .gt_column_spanner_outer:last-child {\n  padding-right: 0;\n}\n\n#gvfpwanldj .gt_column_spanner {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 5px;\n  overflow-x: hidden;\n  display: inline-block;\n  width: 100%;\n}\n\n#gvfpwanldj .gt_spanner_row {\n  border-bottom-style: hidden;\n}\n\n#gvfpwanldj .gt_group_heading {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  text-align: left;\n}\n\n#gvfpwanldj .gt_empty_group_heading {\n  padding: 0.5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: middle;\n}\n\n#gvfpwanldj .gt_from_md > :first-child {\n  margin-top: 0;\n}\n\n#gvfpwanldj .gt_from_md > :last-child {\n  margin-bottom: 0;\n}\n\n#gvfpwanldj .gt_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  margin: 10px;\n  border-top-style: solid;\n  border-top-width: 1px;\n  border-top-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  overflow-x: hidden;\n}\n\n#gvfpwanldj .gt_stub {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#gvfpwanldj .gt_stub_row_group {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n  vertical-align: top;\n}\n\n#gvfpwanldj .gt_row_group_first td {\n  border-top-width: 2px;\n}\n\n#gvfpwanldj .gt_row_group_first th {\n  border-top-width: 2px;\n}\n\n#gvfpwanldj .gt_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#gvfpwanldj .gt_first_summary_row {\n  border-top-style: solid;\n  border-top-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_first_summary_row.thick {\n  border-top-width: 2px;\n}\n\n#gvfpwanldj .gt_last_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_grand_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#gvfpwanldj .gt_first_grand_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-style: double;\n  border-top-width: 6px;\n  border-top-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_last_grand_summary_row_top {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: double;\n  border-bottom-width: 6px;\n  border-bottom-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_striped {\n  background-color: rgba(128, 128, 128, 0.05);\n}\n\n#gvfpwanldj .gt_table_body {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_footnotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_footnote {\n  margin: 0px;\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#gvfpwanldj .gt_sourcenotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#gvfpwanldj .gt_sourcenote {\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#gvfpwanldj .gt_left {\n  text-align: left;\n}\n\n#gvfpwanldj .gt_center {\n  text-align: center;\n}\n\n#gvfpwanldj .gt_right {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n\n#gvfpwanldj .gt_font_normal {\n  font-weight: normal;\n}\n\n#gvfpwanldj .gt_font_bold {\n  font-weight: bold;\n}\n\n#gvfpwanldj .gt_font_italic {\n  font-style: italic;\n}\n\n#gvfpwanldj .gt_super {\n  font-size: 65%;\n}\n\n#gvfpwanldj .gt_footnote_marks {\n  font-size: 75%;\n  vertical-align: 0.4em;\n  position: initial;\n}\n\n#gvfpwanldj .gt_asterisk {\n  font-size: 100%;\n  vertical-align: 0;\n}\n\n#gvfpwanldj .gt_indent_1 {\n  text-indent: 5px;\n}\n\n#gvfpwanldj .gt_indent_2 {\n  text-indent: 10px;\n}\n\n#gvfpwanldj .gt_indent_3 {\n  text-indent: 15px;\n}\n\n#gvfpwanldj .gt_indent_4 {\n  text-indent: 20px;\n}\n\n#gvfpwanldj .gt_indent_5 {\n  text-indent: 25px;\n}\n\n#gvfpwanldj .katex-display {\n  display: inline-flex !important;\n  margin-bottom: 0.75em !important;\n}\n\n#gvfpwanldj div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {\n  height: 0px !important;\n}\n</style>\n<table class=\"gt_table\" data-quarto-disable-processing=\"false\" data-quarto-bootstrap=\"false\">\n  <thead>\n    <tr class=\"gt_heading\">\n      <td colspan=\"4\" class=\"gt_heading gt_title gt_font_normal gt_bottom_border\" style>Types of Atomic Vectors<span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span></td>\n    </tr>\n    \n    <tr class=\"gt_col_headings\">\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_names\">name</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_values\">value</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_type\">typeof()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_length\">length()</th>\n    </tr>\n  </thead>\n  <tbody class=\"gt_table_body\">\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">lgl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">TRUE, FALSE</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">logical</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">2</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">int_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1L, 6L, 10L</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">integer</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">3</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">dbl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1, 2.5, 4.5</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">double</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">3</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">chr_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">'these are', 'some strings'</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">character</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">2</td></tr>\n  </tbody>\n  \n  <tfoot class=\"gt_footnotes\">\n    <tr>\n      <td class=\"gt_footnote\" colspan=\"4\"><span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span> Source: https://adv-r.hadley.nz/index.html</td>\n    </tr>\n  </tfoot>\n</table>\n</div>\n```\n\n:::\n:::\n\n\n<details>\n<summary>Side Quest: Penguins</summary>\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n\n</details>\n\n### Missing values\n\n#### Contagion\n\nFor most computations, an operation over values that includes a missing value yields a missing value (unless you're careful)\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] NA\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] NA\n```\n\n\n:::\n:::\n\n\n#### Exceptions\n\n\n::: {.cell}\n\n:::\n\n\n\n#### Innoculation\n\n\n::: {.cell}\n\n:::\n\n\nTo search for missing values use `is.na()`\n\n\n::: {.cell}\n\n:::\n\n\n\n::: {.cell}\n\n:::\n\n\n<details>\n<summary>Side Quest: NA Types</summary>\n\nEach type has its own NA type\n\n-   Logical: `NA`\n-   Integer: `NA_integer`\n-   Double: `NA_double`\n-   Character: `NA_character`\n\nThis may not matter in many contexts.\n\nBut this does matter for operations where types matter like `dplyr::if_else()`.\n</details>\n\n\n### Testing\n\n**What type of vector `is.*`() it?**\n\nTest data type:\n\n-   Logical: `is.logical()`\n-   Integer: `is.integer()`\n-   Double: `is.double()`\n-   Character: `is.character()`\n\n**What type of object is it?**\n\nDon't test objects with these tools:\n\n-   `is.vector()`\n-   `is.atomic()`\n-   `is.numeric()` \n\nThey don’t test if you have a vector, atomic vector, or numeric vector; you’ll need to carefully read the documentation to figure out what they actually do (preview: *attributes*)\n\n<details>\n<summary>Side Quest: rlang</summary>\n\nInstead, maybe, use `{rlang}`\n\n-   `rlang::is_vector`\n-   `rlang::is_atomic`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] FALSE\n```\n\n\n:::\n:::\n\n\nSee more [here](https://rlang.r-lib.org/reference/type-predicates.html)\n</details>\n\n\n### Coercion\n\n* R follows rules for coercion: character → double → integer → logical\n\n* R can coerce either automatically or explicitly\n\n#### **Automatic**\n\nTwo contexts for automatic coercion:\n\n1.  Combination\n2.  Mathematical\n\n##### Coercion by Combination:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  chr [1:2] \"TRUE\" \"TRUE\"\n```\n\n\n:::\n:::\n\n\n##### Coercion by Mathematical operations:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3\n```\n\n\n:::\n:::\n\n\n#### **Explicit**\n\n<!--\n\nUse `as.*()`\n\n-   Logical: `as.logical()`\n-   Integer: `as.integer()`\n-   Double: `as.double()`\n-   Character: `as.character()`\n\n-->\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n<div id=\"abudsgypeu\" style=\"padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;\">\n<style>#abudsgypeu table {\n  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#abudsgypeu thead, #abudsgypeu tbody, #abudsgypeu tfoot, #abudsgypeu tr, #abudsgypeu td, #abudsgypeu th {\n  border-style: none;\n}\n\n#abudsgypeu p {\n  margin: 0;\n  padding: 0;\n}\n\n#abudsgypeu .gt_table {\n  display: table;\n  border-collapse: collapse;\n  line-height: normal;\n  margin-left: auto;\n  margin-right: auto;\n  color: #333333;\n  font-size: 16px;\n  font-weight: normal;\n  font-style: normal;\n  background-color: #FFFFFF;\n  width: auto;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #A8A8A8;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #A8A8A8;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_caption {\n  padding-top: 4px;\n  padding-bottom: 4px;\n}\n\n#abudsgypeu .gt_title {\n  color: #333333;\n  font-size: 125%;\n  font-weight: initial;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-color: #FFFFFF;\n  border-bottom-width: 0;\n}\n\n#abudsgypeu .gt_subtitle {\n  color: #333333;\n  font-size: 85%;\n  font-weight: initial;\n  padding-top: 3px;\n  padding-bottom: 5px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-color: #FFFFFF;\n  border-top-width: 0;\n}\n\n#abudsgypeu .gt_heading {\n  background-color: #FFFFFF;\n  text-align: center;\n  border-bottom-color: #FFFFFF;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_bottom_border {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_col_headings {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_col_heading {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 6px;\n  padding-left: 5px;\n  padding-right: 5px;\n  overflow-x: hidden;\n}\n\n#abudsgypeu .gt_column_spanner_outer {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  padding-top: 0;\n  padding-bottom: 0;\n  padding-left: 4px;\n  padding-right: 4px;\n}\n\n#abudsgypeu .gt_column_spanner_outer:first-child {\n  padding-left: 0;\n}\n\n#abudsgypeu .gt_column_spanner_outer:last-child {\n  padding-right: 0;\n}\n\n#abudsgypeu .gt_column_spanner {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 5px;\n  overflow-x: hidden;\n  display: inline-block;\n  width: 100%;\n}\n\n#abudsgypeu .gt_spanner_row {\n  border-bottom-style: hidden;\n}\n\n#abudsgypeu .gt_group_heading {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  text-align: left;\n}\n\n#abudsgypeu .gt_empty_group_heading {\n  padding: 0.5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: middle;\n}\n\n#abudsgypeu .gt_from_md > :first-child {\n  margin-top: 0;\n}\n\n#abudsgypeu .gt_from_md > :last-child {\n  margin-bottom: 0;\n}\n\n#abudsgypeu .gt_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  margin: 10px;\n  border-top-style: solid;\n  border-top-width: 1px;\n  border-top-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  overflow-x: hidden;\n}\n\n#abudsgypeu .gt_stub {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#abudsgypeu .gt_stub_row_group {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n  vertical-align: top;\n}\n\n#abudsgypeu .gt_row_group_first td {\n  border-top-width: 2px;\n}\n\n#abudsgypeu .gt_row_group_first th {\n  border-top-width: 2px;\n}\n\n#abudsgypeu .gt_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#abudsgypeu .gt_first_summary_row {\n  border-top-style: solid;\n  border-top-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_first_summary_row.thick {\n  border-top-width: 2px;\n}\n\n#abudsgypeu .gt_last_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_grand_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#abudsgypeu .gt_first_grand_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-style: double;\n  border-top-width: 6px;\n  border-top-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_last_grand_summary_row_top {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: double;\n  border-bottom-width: 6px;\n  border-bottom-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_striped {\n  background-color: rgba(128, 128, 128, 0.05);\n}\n\n#abudsgypeu .gt_table_body {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_footnotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_footnote {\n  margin: 0px;\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#abudsgypeu .gt_sourcenotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#abudsgypeu .gt_sourcenote {\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#abudsgypeu .gt_left {\n  text-align: left;\n}\n\n#abudsgypeu .gt_center {\n  text-align: center;\n}\n\n#abudsgypeu .gt_right {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n\n#abudsgypeu .gt_font_normal {\n  font-weight: normal;\n}\n\n#abudsgypeu .gt_font_bold {\n  font-weight: bold;\n}\n\n#abudsgypeu .gt_font_italic {\n  font-style: italic;\n}\n\n#abudsgypeu .gt_super {\n  font-size: 65%;\n}\n\n#abudsgypeu .gt_footnote_marks {\n  font-size: 75%;\n  vertical-align: 0.4em;\n  position: initial;\n}\n\n#abudsgypeu .gt_asterisk {\n  font-size: 100%;\n  vertical-align: 0;\n}\n\n#abudsgypeu .gt_indent_1 {\n  text-indent: 5px;\n}\n\n#abudsgypeu .gt_indent_2 {\n  text-indent: 10px;\n}\n\n#abudsgypeu .gt_indent_3 {\n  text-indent: 15px;\n}\n\n#abudsgypeu .gt_indent_4 {\n  text-indent: 20px;\n}\n\n#abudsgypeu .gt_indent_5 {\n  text-indent: 25px;\n}\n\n#abudsgypeu .katex-display {\n  display: inline-flex !important;\n  margin-bottom: 0.75em !important;\n}\n\n#abudsgypeu div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {\n  height: 0px !important;\n}\n</style>\n<table class=\"gt_table\" data-quarto-disable-processing=\"false\" data-quarto-bootstrap=\"false\">\n  <thead>\n    <tr class=\"gt_heading\">\n      <td colspan=\"6\" class=\"gt_heading gt_title gt_font_normal gt_bottom_border\" style>Coercion of Atomic Vectors<span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span></td>\n    </tr>\n    \n    <tr class=\"gt_col_headings\">\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_names\">name</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_values\">value</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_logical\">as.logical()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_integer\">as.integer()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_double\">as.double()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_character\">as.character()</th>\n    </tr>\n  </thead>\n  <tbody class=\"gt_table_body\">\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">lgl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">TRUE, FALSE</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE FALSE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 0</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1 0</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'TRUE' 'FALSE'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">int_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1L, 6L, 10L</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE TRUE TRUE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 6 10</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1 6 10</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'1' '6' '10'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">dbl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1, 2.5, 4.5</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE TRUE TRUE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 2 4</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1.0 2.5 4.5</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'1' '2.5' '4.5'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">chr_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">'these are', 'some strings'</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">NA NA</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">NA_integer</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">NA_double</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'these are', 'some strings'</td></tr>\n  </tbody>\n  \n  <tfoot class=\"gt_footnotes\">\n    <tr>\n      <td class=\"gt_footnote\" colspan=\"6\"><span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span> Source: https://adv-r.hadley.nz/index.html</td>\n    </tr>\n  </tfoot>\n</table>\n</div>\n```\n\n:::\n:::\n\n\nBut note that coercion may fail in one of two ways, or both:\n\n-   With warning/error\n-   NAs\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1]  1  2 NA\n```\n\n\n:::\n:::\n\n\n### Exercises\n\n1. How do you create raw and complex scalars?\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n:::\n\n</details>\n\n2. Test your knowledge of the vector coercion rules by predicting the output of the following uses of c():\n\n\n::: {.cell}\n\n:::\n\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n:::\n\n</details>\n\n3. Why is `1 == \"1\"` true? Why is `-1 < FALSE` true? Why is `\"one\" < 2` false?\n\n<details><summary>Answer(s)</summary>\nThese comparisons are carried out by operator-functions (==, <), which coerce their arguments to a common type. In the examples above, these types will be character, double and character: 1 will be coerced to \"1\", FALSE is represented as 0 and 2 turns into \"2\" (and numbers precede letters in lexicographic order (may depend on locale)).\n\n</details>\n\n4. Why is the default missing value, NA, a logical vector? What’s special about logical vectors?\n\n<details><summary>Answer(s)</summary>\nThe presence of missing values shouldn’t affect the type of an object. Recall that there is a type-hierarchy for coercion from character → double → integer → logical. When combining `NA`s with other atomic types, the `NA`s will be coerced to integer (`NA_integer_`), double (`NA_real_`) or character (`NA_character_`) and not the other way round. If `NA` were a character and added to a set of other values all of these would be coerced to character as well.\n</details>\n\n5. Precisely what do `is.atomic()`, `is.numeric()`, and `is.vector()` test for?\n\n<details><summary>Answer(s)</summary>\nThe documentation states that:\n\n* `is.atomic()` tests if an object is an atomic vector (as defined in *Advanced R*) or is `NULL` (!).\n* `is.numeric()` tests if an object has type integer or double and is not of class `factor`, `Date`, `POSIXt` or `difftime`.\n* `is.vector()` tests if an object is a vector (as defined in *Advanced R*) or an expression and has no attributes, apart from names.\n\nAtomic vectors are defined in *Advanced R* as objects of type logical, integer, double, complex, character or raw. Vectors are defined as atomic vectors or lists.\n</details>\n\n\n\n## Attributes\n\nAttributes are name-value pairs that attach metadata to an object(vector).\n\n* **Name-value pairs**: attributes have a name and a value\n* **Metadata**: not data itself, but data about the data\n\n### How? \n\n#### Getting and Setting\n\nThree functions:\n\n1. retrieve and modify single attributes with `attr()`\n2. retrieve en masse with `attributes()`\n3. set en masse with `structure()`\n\n**Single attribute**\n\nUse `attr()`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"some attribute\"\n```\n\n\n:::\n:::\n\n\n**Multiple attributes**\n\nTo set multiple attributes, use `structure()` To get multiple attributes, use `attributes()`\n\n\n::: {.cell}\n\n:::\n\n\n\n\n::: {.cell}\n\n:::\n\n\n \n\n### Why\n\nThree particularly important attributes: \n\n1. **names** - a character vector giving each element a name\n2. **dimension** - (or dim) turns vectors into matrices and arrays \n3. **class** - powers the S3 object system (we'll learn more about this in chapter 13)\n\nMost attributes are lost by most operations.  Only two attributes are routinely preserved: names and dimension.\n\n#### Names\n\n~~Three~~ Four ways to name:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> A B C \n#> 1 2 3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n:::\n\n\n \n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n:::\n\n\n \n\n* You can remove names from a vector by using `x <- unname(x)` or `names(x) <- NULL`.\n* Thematically but not directly related: labelled class vectors with `haven::labelled()`\n\n\n#### Dimensions\n\nCreate matrices and arrays with `matrix()` and `array()`, or by using the assignment form of `dim()`:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> , , 1\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n#> \n#> , , 2\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    7    9   11\n#> [2,]    8   10   12\n#> \n#> , , 3\n#> \n#>      [,1] [,2] [,3]\n#> [1,]   13   15   17\n#> [2,]   14   16   18\n#> \n#> , , 4\n#> \n#>      [,1] [,2] [,3]\n#> [1,]   19   21   23\n#> [2,]   20   22   24\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> , , 1\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n#> \n#> , , 2\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    7    9   11\n#> [2,]    8   10   12\n#> \n#> , , 3\n#> \n#>      [,1] [,2] [,3]\n#> [1,]   13   15   17\n#> [2,]   14   16   18\n#> \n#> , , 4\n#> \n#>      [,1] [,2] [,3]\n#> [1,]   19   21   23\n#> [2,]   20   22   24\n```\n\n\n:::\n:::\n\n\n##### Functions for working with vectors, matrices and arrays:\n\nVector | Matrix\t| Array\n:----- | :---------- | :-----\n`names()` | `rownames()`, `colnames()` | `dimnames()`\n`length()` | `nrow()`, `ncol()` | `dim()`\n`c()` | `rbind()`, `cbind()` | `abind::abind()`\n— | `t()` | `aperm()`\n`is.null(dim(x))` | `is.matrix()` | `is.array()`\n\n* **Caution**: A vector without a `dim` attribute set is often thought of as 1-dimensional, but actually has `NULL` dimensions.\n* One dimension?\n\n\n::: {.cell}\n\n:::\n\n\n\n### Exercises\n\n1. How is `setNames()` implemented? How is `unname()` implemented? Read the source code.\n\n<details><summary>Answer(s)</summary>\n`setNames()` is implemented as:\n\n\n::: {.cell}\n\n:::\n\n\nBecause the data argument comes first, `setNames()` also works well with the magrittr-pipe operator. When no first argument is given, the result is a named vector (this is rather untypical as required arguments usually come first):\n\n\n::: {.cell}\n\n:::\n\n\n`unname()` is implemented in the following way:\n\n\n::: {.cell}\n\n:::\n\n\n`unname()` removes existing names (or dimnames) by setting them to `NULL`.\n</details>\n\n2. What does `dim()` return when applied to a 1-dimensional vector? When might you use `NROW()` or `NCOL()`?\n\n<details><summary>Answer(s)</summary>\n\n> dim() will return NULL when applied to a 1d vector.\n\nOne may want to use `NROW()` or `NCOL()` to handle atomic vectors, lists and NULL values in the same way as one column matrices or data frames. For these objects `nrow()` and `ncol()` return NULL:\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n3. How would you describe the following three objects? What makes them different from `1:5`?\n\n\n::: {.cell}\n\n:::\n\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n:::\n\n</details>\n\n\n4. An early draft used this code to illustrate `structure()`:\n\n\n::: {.cell}\n\n:::\n\n\nBut when you print that object you don’t see the comment attribute. Why? Is the attribute missing, or is there something else special about it?\n\n<details><summary>Answer(s)</summary>\nThe documentation states (see `?comment`):\n\n> Contrary to other attributes, the comment is not printed (by print or print.default).\n\nAlso, from `?attributes:`\n\n> Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set.\n\nWe can retrieve comment attributes by calling them explicitly:\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n\n\n## **Class** - S3 atomic vectors\n\n \n\nCredit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham\n\n**Having a class attribute turns an object into an S3 object.**\n\nWhat makes S3 atomic vectors different?\n\n1. behave differently from a regular vector when passed to a generic function \n2. often store additional information in other attributes\n\nFour important S3 vectors used in base R:\n\n1. **Factors** (categorical data)\n2. **Dates**\n3. **Date-times** (POSIXct)\n4. **Durations** (difftime)\n\n### Factors\n\nA factor is a vector used to store categorical data that can contain only predefined values.\n\nFactors are integer vectors with:\n\n-   Class: \"factor\"\n-   Attributes: \"levels\", or the set of allowed values\n\n\n::: {.cell}\n\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> colors\n#>  blue green   red \n#>     1     2     3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a_factor\n#>    red   blue  green yellow \n#>      3      1      2      0\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $levels\n#> [1] \"red\"    \"blue\"   \"green\"  \"yellow\"\n#> \n#> $class\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n\n#### Custom Order\n\nFactors can be ordered. This can be useful for models or visualizations where order matters.\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] high med  low  med  high low  med  high\n#> Levels: low < med < high\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> values\n#> high  low  med \n#>    3    2    3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> ordered_factor\n#>  low  med high \n#>    2    3    3\n```\n\n\n:::\n:::\n\n\n### Dates\n\nDates are:\n\n-   Double vectors\n-   With class \"Date\"\n-   No other attributes\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"Date\"\n```\n\n\n:::\n:::\n\n\nThe double component represents the number of days since since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time) `1970-01-01`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 31\n```\n\n\n:::\n:::\n\n\n### Date-times\n\nThere are 2 Date-time representations in base R:\n\n-   POSIXct, where \"ct\" denotes *calendar time*\n-   POSIXlt, where \"lt\" designates *local time*\n\n<!--\n\nJust for fun:\n\"How to pronounce 'POSIXct'?\"\nhttps://www.howtopronounce.com/posixct\n\n-->\n\nWe'll focus on POSIXct because:\n\n-   Simplest\n-   Built on an atomic (double) vector\n-   Most appropriate for use in a data frame\n\nLet's now build and deconstruct a Date-time\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"2025-08-04 15:39:49 EDT\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"POSIXct\" \"POSIXt\" \n#> \n#> $tzone\n#> [1] \"America/New_York\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"2025-08-04 21:39:49 CEST\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1708623296\n#> attr(,\"tzone\")\n#> [1] \"EST\"\n```\n\n\n:::\n:::\n\n\n\n### Durations\n\nDurations represent the amount of time between pairs of dates or date-times.\n\n-   Double vectors\n-   Class: \"difftime\"\n-   Attributes: \"units\", or the unit of duration (e.g., weeks, hours, minutes, seconds, etc.)\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> Time difference of 1 mins\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"difftime\"\n#> \n#> $units\n#> [1] \"mins\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> Time difference of 20273 days\n```\n\n\n:::\n:::\n\n\n\nSee also:\n\n-   [`lubridate::make_difftime()`](https://lubridate.tidyverse.org/reference/make_difftime.html)\n-   [`clock::date_time_build()`](https://clock.r-lib.org/reference/date_time_build.html)\n\n\n### Exercises\n\n1. What sort of object does `table()` return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables?\n\n<details><summary>Answer(s)</summary>\n\n`table()` returns a contingency table of its input variables. It is implemented as an integer vector with class table and dimensions (which makes it act like an array). Its attributes are dim (dimensions) and dimnames (one name for each input column). The dimensions correspond to the number of unique values (factor levels) in each input variable.\n\n\n::: {.cell}\n\n:::\n\n</details>\n\n2. What happens to a factor when you modify its levels?\n\n\n::: {.cell}\n\n:::\n\n\n<details><summary>Answer(s)</summary>\nThe underlying integer values stay the same, but the levels are changed, making it look like the data has changed.\n\n\n::: {.cell}\n\n:::\n\n</details>\n\n3. What does this code do? How do `f2` and `f3` differ from `f1`?\n\n\n::: {.cell}\n\n:::\n\n\n<details><summary>Answer(s)</summary>\nFor `f2` and `f3` either the order of the factor elements or its levels are being reversed. For `f1` both transformations are occurring.\n\n\n::: {.cell}\n\n:::\n\n</details>\n\n\n\n\n\n\n## Lists\n\n* sometimes called a generic vector or recursive vector\n* Recall ([section 2.3.3](https://adv-r.hadley.nz/names-values.html#list-references)): each element is really a *reference* to another object\n* an be composed of elements of different types (as opposed to atomic vectors which must be of only one type)\n\n### Constructing\n\nSimple lists:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1]  TRUE FALSE\n#> \n#> [[2]]\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20\n#> \n#> [[3]]\n#> [1] 1.2 2.3 3.4\n#> \n#> [[4]]\n#> [1] \"primo\"   \"secundo\" \"tercio\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : logi [1:2] TRUE FALSE\n#>  $ : int [1:20] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ : num [1:3] 1.2 2.3 3.4\n#>  $ : chr [1:3] \"primo\" \"secundo\" \"tercio\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1]  TRUE FALSE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1.2 2.3 3.4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] \"primo\"   \"secundo\" \"tercio\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] FALSE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 8\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"tercio\"\n```\n\n\n:::\n:::\n\n\nEven Simpler List\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 2.3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] \"primo\"\n```\n\n\n:::\n:::\n\n\nNested lists:\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 1\n#>  $ :List of 1\n#>   ..$ :List of 1\n#>   .. ..$ :List of 1\n#>   .. .. ..$ : num 1\n```\n\n\n:::\n:::\n\n\nLike JSON.\n\nCombined lists\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 2\n#>  $ :List of 2\n#>   ..$ : num 1\n#>   ..$ : num 2\n#>  $ :List of 2\n#>   ..$ : num 3\n#>   ..$ : num 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : num 1\n#>  $ : num 2\n#>  $ : num 3\n#>  $ : num 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : num 1\n#>  $ : num 2\n#>  $ : logi TRUE\n#>  $ : logi FALSE\n```\n\n\n:::\n:::\n\n\n### Testing\n\nCheck that is a list:\n\n-   `is.list()`\n-   \\`rlang::is_list()\\`\\`\n\nThe two do the same, except that the latter can check for the number of elements\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n:::\n\n\n### Coercion\n\nUse `as.list()`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1 2 3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1\n#> \n#> [[2]]\n#> [1] 2\n#> \n#> [[3]]\n#> [1] 3\n```\n\n\n:::\n:::\n\n\n### Matrices and arrays\n\nAlthough not often used, the dimension attribute can be added to create **list-matrices** or **list-arrays**.\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1]      [,2]\n#> [1,] integer,3 TRUE\n#> [2,] \"a\"       1\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n:::\n\n\n\n### Exercises\n\n1. List all the ways that a list differs from an atomic vector.\n\n<details><summary>Answer(s)</summary>\n\n* Atomic vectors are always homogeneous (all elements must be of the same type). Lists may be heterogeneous (the elements can be of different types) as described in the introduction of the vectors chapter.\n* Atomic vectors point to one address in memory, while lists contain a separate reference for each element. (This was described in the list sections of the vectors and the names and values chapters.)\n\n\n::: {.cell}\n\n:::\n\n\n\n* Subsetting with out-of-bounds and NA values leads to different output. For example, [ returns NA for atomics and NULL for lists. (This is described in more detail within the subsetting chapter.)\n\n\n::: {.cell}\n\n:::\n\n\n\n</details>\n\n2. Why do you need to use `unlist()` to convert a list to an atomic vector? Why doesn’t `as.vector()` work?\n\n<details><summary>Answer(s)</summary>\nA list is already a vector, though not an atomic one! Note that as.vector() and is.vector() use different definitions of “vector!”\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n3. Compare and contrast `c()` and `unlist()` when combining a date and date-time into a single vector.\n\n<details><summary>Answer(s)</summary>\nDate and date-time objects are both built upon doubles. While dates store the number of days since the reference date 1970-01-01 (also known as “the Epoch”) in days, date-time-objects (POSIXct) store the time difference to this date in seconds.\n\n\n::: {.cell}\n\n:::\n\n\nAs the c() generic only dispatches on its first argument, combining date and date-time objects via c() could lead to surprising results in older R versions (pre R 4.0.0):\n\n\n::: {.cell}\n\n:::\n\n\nIn the first statement above c.Date() is executed, which incorrectly treats the underlying double of dttm_ct (3600) as days instead of seconds. Conversely, when c.POSIXct() is called on a date, one day is counted as one second only.\n\nWe can highlight these mechanics by the following code:\n\n\n::: {.cell}\n\n:::\n\n\nAs of R 4.0.0 these issues have been resolved and both methods now convert their input first into POSIXct and Date, respectively.\n\n\n::: {.cell}\n\n:::\n\n\nHowever, as c() strips the time zone (and other attributes) of POSIXct objects, some caution is still recommended.\n\n\n::: {.cell}\n\n:::\n\n\nA package that deals with these kinds of problems in more depth and provides a structural solution for them is the {vctrs} package9 which is also used throughout the tidyverse.10\n\nLet’s look at unlist(), which operates on list input.\n\n\n::: {.cell}\n\n:::\n\n\nWe see again that dates and date-times are internally stored as doubles. Unfortunately, this is all we are left with, when unlist strips the attributes of the list.\n\nTo summarise: c() coerces types and strips time zones. Errors may have occurred in older R versions because of inappropriate method dispatch/immature methods. unlist() strips attributes.\n</details>\n\n\n\n## Data frames and tibbles\n\n \n\nCredit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham\n\n### Data frame\n\nA data frame is a:\n\n-   Named list of vectors (i.e., column names)\n-   Attributes:\n    -   (column) `names`\n    -   `row.names`\n    -   Class: \"data frame\"\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   col1  col2\n#> 1    1    un\n#> 2    2  deux\n#> 3    3 trois\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $names\n#> [1] \"col1\" \"col2\"\n#> \n#> $class\n#> [1] \"data.frame\"\n#> \n#> $row.names\n#> [1] 1 2 3\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"1\" \"2\" \"3\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\" \"col2\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\" \"col2\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2\n```\n\n\n:::\n:::\n\n\nUnlike other lists, the length of each vector must be the same (i.e. as many vector elements as rows in the data frame).\n\n### Tibble\n\nCreated to relieve some of the frustrations and pain points created by data frames, tibbles are data frames that are:\n\n-   Lazy (do less)\n-   Surly (complain more)\n\n#### Lazy\n\nTibbles do not:\n\n-   Coerce strings\n-   Transform non-syntactic names\n-   Recycle vectors of length greater than 1\n\n**Coerce strings**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  Factor w/ 4 levels \"bro\",\"don't\",..: 2 3 4 1\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  chr [1:4] \"don't\" \"factor\" \"me\" \"bro\"\n```\n\n\n:::\n:::\n\n\n**Transform non-syntactic names**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"X1\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"1\"\n```\n\n\n:::\n:::\n\n\n**Recycle vectors of length greater than 1**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in `tibble::tibble()`:\n#> ! Tibble columns must have compatible sizes.\n#> • Size 4: Existing data.\n#> • Size 2: Column `col2`.\n#> ℹ Only values of size one are recycled.\n```\n\n\n:::\n:::\n\n\n#### Surly\n\nTibbles do only what they're asked and complain if what they're asked doesn't make sense:\n\n-   Subsetting always yields a tibble\n-   Complains if cannot find column\n\n**Subsetting always yields a tibble**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [4 × 1] (S3: tbl_df/tbl/data.frame)\n#>  $ col1: num [1:4] 1 2 3 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n:::\n\n\n**Complains if cannot find column**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stderr}\n\n```\n#> Warning: Unknown or uninitialised column: `col`.\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n#### One more difference\n\n**`tibble()` allows you to refer to variables created during construction**\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 2\n#>       x     y\n#>   <int> <dbl>\n#> 1     1     2\n#> 2     2     4\n#> 3     3     6\n```\n\n\n:::\n:::\n\n\n<details>\n<summary>Side Quest: Row Names</summary>\n\n- character vector containing only unique values\n- get and set with `rownames()`\n- can use them to subset rows\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       age  hair\n#> Bob    35 blond\n#> Susan  27 brown\n#> Sam    18 black\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"Bob\"   \"Susan\" \"Sam\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     age  hair\n#> Bob  35 blond\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"Susan\" \"Bob\"   \"Sam\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     age  hair\n#> Bob  27 brown\n```\n\n\n:::\n:::\n\n\nThere are three reasons why row names are undesirable:\n\n3. Metadata is data, so storing it in a different way to the rest of the data is fundamentally a bad idea. \n2. Row names are a poor abstraction for labelling rows because they only work when a row can be identified by a single string. This fails in many cases.\n3. Row names must be unique, so any duplication of rows (e.g. from bootstrapping) will create new row names.\n\n</details>\n\n\n### Printing\n\nData frames and tibbles print differently\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       age  hair\n#> Susan  35 blond\n#> Bob    27 brown\n#> Sam    18 black\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 2\n#>     age hair \n#>   <dbl> <chr>\n#> 1    35 blond\n#> 2    27 brown\n#> 3    18 black\n```\n\n\n:::\n:::\n\n\n\n### Subsetting\n\nTwo undesirable subsetting behaviours:\n\n1. When you subset columns with `df[, vars]`, you will get a vector if vars selects one variable, otherwise you’ll get a data frame, unless you always remember to use `df[, vars, drop = FALSE]`.\n2. When you attempt to extract a single column with `df$x` and there is no column `x`, a data frame will instead select any variable that starts with `x`. If no variable starts with `x`, `df$x` will return NULL.\n\nTibbles tweak these behaviours so that a [ always returns a tibble, and a $ doesn’t do partial matching and warns if it can’t find a variable (*this is what makes tibbles surly*).\n\n### Testing\n\nWhether data frame: `is.data.frame()`. Note: both data frame and tibble are data frames.\n\nWhether tibble: `tibble::is_tibble`. Note: only tibbles are tibbles. Vanilla data frames are not.\n\n### Coercion\n\n-   To data frame: `as.data.frame()`\n-   To tibble: `tibble::as_tibble()`\n\n### List Columns\n\nList-columns are allowed in data frames but you have to do a little extra work by either adding the list-column after creation or wrapping the list in `I()`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x          y\n#> 1 1       1, 2\n#> 2 2    1, 2, 3\n#> 3 3 1, 2, 3, 4\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x          y\n#> 1 1       1, 2\n#> 2 2    1, 2, 3\n#> 3 3 1, 2, 3, 4\n```\n\n\n:::\n:::\n\n\n### Matrix and data frame columns\n\n- As long as the number of rows matches the data frame, it’s also possible to have a matrix or data frame as a column of a data frame.\n- same as list-columns, must either addi the list-column after creation or wrapping the list in `I()`\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 'data.frame':\t3 obs. of  3 variables:\n#>  $ x: num  10 20 30\n#>  $ y: 'AsIs' int [1:3, 1:3] 1 2 3 4 5 6 7 8 9\n#>  $ z:'data.frame':\t3 obs. of  2 variables:\n#>   ..$ a: int  3 2 1\n#>   ..$ b: chr  \"a\" \"b\" \"c\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    4    7\n#> [2,]    2    5    8\n#> [3,]    3    6    9\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 3 a\n#> 2 2 b\n#> 3 1 c\n```\n\n\n:::\n:::\n\n\n\n### Exercises\n\n1. Can you have a data frame with zero rows? What about zero columns?\n\n<details><summary>Answer(s)</summary>\nYes, you can create these data frames easily; either during creation or via subsetting. Even both dimensions can be zero. Create a 0-row, 0-column, or an empty data frame directly:\n\n\n::: {.cell}\n\n:::\n\n\nCreate similar data frames via subsetting the respective dimension with either 0, `NULL`, `FALSE` or a valid 0-length atomic (`logical(0)`, `character(0)`, `integer(0)`, `double(0)`). Negative integer sequences would also work. The following example uses a zero:\n\n\n::: {.cell}\n\n:::\n\n\n\n</details>\n\n2. What happens if you attempt to set rownames that are not unique?\n\n<details><summary>Answer(s)</summary>\nMatrices can have duplicated row names, so this does not cause problems.\n\nData frames, however, require unique rownames and you get different results depending on how you attempt to set them. If you set them directly or via `row.names()`, you get an error:\n\n\n::: {.cell}\n\n:::\n\n\nIf you use subsetting, `[` automatically deduplicates:\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n3. If `df` is a data frame, what can you say about `t(df)`, and `t(t(df))`? Perform some experiments, making sure to try different column types.\n\n<details><summary>Answer(s)</summary>\nBoth of `t(df)` and `t(t(df))` will return matrices:\n\n\n::: {.cell}\n\n:::\n\n\nThe dimensions will respect the typical transposition rules:\n\n\n::: {.cell}\n\n:::\n\n\nBecause the output is a matrix, every column is coerced to the same type. (It is implemented within `t.data.frame()` via `as.matrix()` which is described below).\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n4. What does `as.matrix()` do when applied to a data frame with columns of different types? How does it differ from `data.matrix()`?\n\n<details><summary>Answer(s)</summary>\nThe type of the result of as.matrix depends on the types of the input columns (see `?as.matrix`):\n\n> The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g. all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.\n\nOn the other hand, `data.matrix` will always return a numeric matrix (see `?data.matrix()`).\n\n> Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. […] Character columns are first converted to factors and then to integers.\n\nWe can illustrate and compare the mechanics of these functions using a concrete example. `as.matrix()` makes it possible to retrieve most of the original information from the data frame but leaves us with characters. To retrieve all information from `data.matrix()`’s output, we would need a lookup table for each column.\n\n\n::: {.cell}\n\n:::\n\n\n</details>\n\n\n\n\n\n\n\n## `NULL`\n\nSpecial type of object that:\n\n-   Length 0\n-   Cannot have attributes\n\n\n::: {.cell}\n\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in attr(x, \"y\") <- 1: attempt to set an attribute on NULL\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n:::\n\n\n\n## Digestif\n\nLet is use some of this chapter's skills on the `penguins` data.\n\n### Attributes\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [344 × 17] (S3: tbl_df/tbl/data.frame)\n#>  $ studyName          : chr [1:344] \"PAL0708\" \"PAL0708\" \"PAL0708\" \"PAL0708\" ...\n#>  $ Sample Number      : num [1:344] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ Species            : chr [1:344] \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" ...\n#>  $ Region             : chr [1:344] \"Anvers\" \"Anvers\" \"Anvers\" \"Anvers\" ...\n#>  $ Island             : chr [1:344] \"Torgersen\" \"Torgersen\" \"Torgersen\" \"Torgersen\" ...\n#>  $ Stage              : chr [1:344] \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" ...\n#>  $ Individual ID      : chr [1:344] \"N1A1\" \"N1A2\" \"N2A1\" \"N2A2\" ...\n#>  $ Clutch Completion  : chr [1:344] \"Yes\" \"Yes\" \"Yes\" \"Yes\" ...\n#>  $ Date Egg           : Date[1:344], format: \"2007-11-11\" \"2007-11-11\" ...\n#>  $ Culmen Length (mm) : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...\n#>  $ Culmen Depth (mm)  : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...\n#>  $ Flipper Length (mm): num [1:344] 181 186 195 NA 193 190 181 195 193 190 ...\n#>  $ Body Mass (g)      : num [1:344] 3750 3800 3250 NA 3450 ...\n#>  $ Sex                : chr [1:344] \"MALE\" \"FEMALE\" \"FEMALE\" NA ...\n#>  $ Delta 15 N (o/oo)  : num [1:344] NA 8.95 8.37 NA 8.77 ...\n#>  $ Delta 13 C (o/oo)  : num [1:344] NA -24.7 -25.3 NA -25.3 ...\n#>  $ Comments           : chr [1:344] \"Not enough blood for isotopes.\" NA NA \"Adult not sampled.\" ...\n#>  - attr(*, \"spec\")=List of 3\n#>   ..$ cols   :List of 17\n#>   .. ..$ studyName          : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Sample Number      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Species            : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Region             : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Island             : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Stage              : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Individual ID      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Clutch Completion  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Date Egg           :List of 1\n#>   .. .. ..$ format: chr \"\"\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_date\" \"collector\"\n#>   .. ..$ Culmen Length (mm) : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Culmen Depth (mm)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Flipper Length (mm): list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Body Mass (g)      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Sex                : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Delta 15 N (o/oo)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Delta 13 C (o/oo)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Comments           : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   ..$ default: list()\n#>   .. ..- attr(*, \"class\")= chr [1:2] \"collector_guess\" \"collector\"\n#>   ..$ skip   : num 1\n#>   ..- attr(*, \"class\")= chr \"col_spec\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [344 × 17] (S3: tbl_df/tbl/data.frame)\n#>  $ studyName          : chr [1:344] \"PAL0708\" \"PAL0708\" \"PAL0708\" \"PAL0708\" ...\n#>  $ Sample Number      : num [1:344] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ Species            : chr [1:344] \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" ...\n#>  $ Region             : chr [1:344] \"Anvers\" \"Anvers\" \"Anvers\" \"Anvers\" ...\n#>  $ Island             : chr [1:344] \"Torgersen\" \"Torgersen\" \"Torgersen\" \"Torgersen\" ...\n#>  $ Stage              : chr [1:344] \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" ...\n#>  $ Individual ID      : chr [1:344] \"N1A1\" \"N1A2\" \"N2A1\" \"N2A2\" ...\n#>  $ Clutch Completion  : chr [1:344] \"Yes\" \"Yes\" \"Yes\" \"Yes\" ...\n#>  $ Date Egg           : Date[1:344], format: \"2007-11-11\" \"2007-11-11\" ...\n#>  $ Culmen Length (mm) : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...\n#>  $ Culmen Depth (mm)  : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...\n#>  $ Flipper Length (mm): num [1:344] 181 186 195 NA 193 190 181 195 193 190 ...\n#>  $ Body Mass (g)      : num [1:344] 3750 3800 3250 NA 3450 ...\n#>  $ Sex                : chr [1:344] \"MALE\" \"FEMALE\" \"FEMALE\" NA ...\n#>  $ Delta 15 N (o/oo)  : num [1:344] NA 8.95 8.37 NA 8.77 ...\n#>  $ Delta 13 C (o/oo)  : num [1:344] NA -24.7 -25.3 NA -25.3 ...\n#>  $ Comments           : chr [1:344] \"Not enough blood for isotopes.\" NA NA \"Adult not sampled.\" ...\n```\n\n\n:::\n:::\n\n\n### Data Frames vs Tibbles\n\n\n::: {.cell}\n\n:::\n\n\n#### Printing\n\n* Tip: print out these results in RStudio under different editor themes\n\n\n::: {.cell}\n\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   species    island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g\n#> 1  Adelie Torgersen           39.1          18.7               181        3750\n#> 2  Adelie Torgersen           39.5          17.4               186        3800\n#> 3  Adelie Torgersen           40.3          18.0               195        3250\n#> 4  Adelie Torgersen             NA            NA                NA          NA\n#> 5  Adelie Torgersen           36.7          19.3               193        3450\n#> 6  Adelie Torgersen           39.3          20.6               190        3650\n#>      sex year\n#> 1   male 2007\n#> 2 female 2007\n#> 3 female 2007\n#> 4   <NA> 2007\n#> 5 female 2007\n#> 6   male 2007\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 344 × 8\n#>    species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g\n#>    <fct>   <fct>              <dbl>         <dbl>             <int>       <int>\n#>  1 Adelie  Torgersen           39.1          18.7               181        3750\n#>  2 Adelie  Torgersen           39.5          17.4               186        3800\n#>  3 Adelie  Torgersen           40.3          18                 195        3250\n#>  4 Adelie  Torgersen           NA            NA                  NA          NA\n#>  5 Adelie  Torgersen           36.7          19.3               193        3450\n#>  6 Adelie  Torgersen           39.3          20.6               190        3650\n#>  7 Adelie  Torgersen           38.9          17.8               181        3625\n#>  8 Adelie  Torgersen           39.2          19.6               195        4675\n#>  9 Adelie  Torgersen           34.1          18.1               193        3475\n#> 10 Adelie  Torgersen           42            20.2               190        4250\n#> # ℹ 334 more rows\n#> # ℹ 2 more variables: sex <fct>, year <int>\n```\n\n\n:::\n:::\n\n\n### Atomic Vectors\n\n\n::: {.cell}\n\n:::\n\n\n<details>\n<summary>`typeof()` and `class()`</summary>\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"data.frame\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"tbl_df\"     \"tbl\"        \"data.frame\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n</details>\n\n### Column Names\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"species\"           \"island\"            \"bill_length_mm\"   \n#> [4] \"bill_depth_mm\"     \"flipper_length_mm\" \"body_mass_g\"      \n#> [7] \"sex\"               \"year\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE\n```\n\n\n:::\n:::\n\n\n* What if we only invoke a partial name of a column of a tibble?\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n\n* What if we only invoke a partial name of a column of a data frame?\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2007 2007 2007 2007 2007 2007\n```\n\n\n:::\n:::\n\n\n* Is this evaluation in alphabetical order or column order?\n\n\n::: {.cell}\n\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n## Chapter Quiz\n\n1. What are the four common types of atomic vectors? What are the two rare types?\n\n<details><summary>Answer(s)</summary>\nThe four common types of atomic vector are logical, integer, double and character. The two rarer types are complex and raw.\n</details>\n\n2. What are attributes? How do you get them and set them?\n\n<details><summary>Answer(s)</summary>\nAttributes allow you to associate arbitrary additional metadata to any object. You can get and set individual attributes with `attr(x, \"y\")` and `attr(x, \"y\") <- value`; or you can get and set all attributes at once with `attributes()`.\n</details>\n\n3. How is a list different from an atomic vector? How is a matrix different from a data frame?\n\n<details><summary>Answer(s)</summary>\nThe elements of a list can be any type (even a list); the elements of an atomic vector are all of the same type. Similarly, every element of a matrix must be the same type; in a data frame, different columns can have different types.\n</details>\n\n4. Can you have a list that is a matrix? Can a data frame have a column that is a matrix?\n\n<details><summary>Answer(s)</summary>\nYou can make a list-array by assigning dimensions to a list. You can make a matrix a column of a data frame with `df$x <- matrix()`, or by using `I()` when creating a new data frame `data.frame(x = I(matrix()))`.\n</details>\n\n5. How do tibbles behave differently from data frames?\n\n<details><summary>Answer(s)</summary>\nTibbles have an enhanced print method, never coerce strings to factors, and provide stricter subsetting methods.\n</details>\n",
+    "markdown": "---\nengine: knitr\ntitle: Vectors\n---\n\n## Learning objectives:\n\n-   Learn about different types of vectors and their attributes\n-   Navigate through vector types and their value types\n-   Venture into factors and date-time objects\n-   Discuss the differences between data frames and tibbles\n-   Do not get absorbed by the `NA` and `NULL` black hole\n\n\n## Session Info\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(\"dplyr\")\nlibrary(\"gt\")\nlibrary(\"palmerpenguins\")\n```\n:::\n\n\n\n<details>\n<summary>Session Info</summary>\n\n::: {.cell}\n\n```{.r .cell-code}\nutils::sessionInfo()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> R version 4.5.1 (2025-06-13 ucrt)\n#> Platform: x86_64-w64-mingw32/x64\n#> Running under: Windows 11 x64 (build 26100)\n#> \n#> Matrix products: default\n#>   LAPACK version 3.12.1\n#> \n#> locale:\n#> [1] LC_COLLATE=English_United States.utf8 \n#> [2] LC_CTYPE=English_United States.utf8   \n#> [3] LC_MONETARY=English_United States.utf8\n#> [4] LC_NUMERIC=C                          \n#> [5] LC_TIME=English_United States.utf8    \n#> \n#> time zone: America/Chicago\n#> tzcode source: internal\n#> \n#> attached base packages:\n#> [1] stats     graphics  grDevices utils     datasets  methods   base     \n#> \n#> other attached packages:\n#> [1] palmerpenguins_0.1.1 gt_1.0.0             dplyr_1.1.4         \n#> \n#> loaded via a namespace (and not attached):\n#>  [1] digest_0.6.37     R6_2.6.1          fastmap_1.2.0     tidyselect_1.2.1 \n#>  [5] xfun_0.53         magrittr_2.0.3    glue_1.8.0        tibble_3.3.0     \n#>  [9] knitr_1.50        pkgconfig_2.0.3   htmltools_0.5.8.1 rmarkdown_2.29   \n#> [13] generics_0.1.4    lifecycle_1.0.4   xml2_1.3.8        cli_3.6.5        \n#> [17] vctrs_0.6.5       compiler_4.5.1    tools_4.5.1       pillar_1.11.0    \n#> [21] evaluate_1.0.4    yaml_2.3.10       rlang_1.1.6       jsonlite_2.0.0   \n#> [25] keyring_1.4.1\n```\n\n\n:::\n:::\n\n</details>\n\n## Aperitif\n\n\n\n## Counting Penguins\n\nConsider this code to count the number of Gentoo penguins in the `penguins` data set. We see that there are 124 Gentoo penguins.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsum(\"Gentoo\" == penguins$species)\n# output: 124\n```\n:::\n\n\n## In\n\nOne subtle error can arise in trying out `%in%` here instead.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nspecies_vector <- penguins |> select(species)\nprint(\"Gentoo\" %in% species_vector)\n# output: FALSE\n```\n:::\n\n\n\n\n## Fix: base R \n\n\n::: {.cell}\n\n```{.r .cell-code}\nspecies_unlist <- penguins |> select(species) |> unlist()\nprint(\"Gentoo\" %in% species_unlist)\n# output: TRUE\n```\n:::\n\n\n## Fix: dplyr\n\n\n::: {.cell}\n\n```{.r .cell-code}\nspecies_pull <- penguins |> select(species) |> pull()\nprint(\"Gentoo\" %in% species_pull)\n# output: TRUE\n```\n:::\n\n\n## Motivation\n\n* What are the different types of vectors?\n* How does this affect accessing vectors?\n\n<details>\n<summary>Side Quest: Looking up the `%in%` operator</summary>\nIf you want to look up the manual pages for the `%in%` operator with the `?`, use backticks:\n\n\n::: {.cell}\n\n```{.r .cell-code}\n?`%in%`\n```\n:::\n\n\nand we find that `%in%` is a wrapper for the `match()` function.\n\n</details>\n\n\n## Types of Vectors\n\n \n\nTwo main types:\n\n-   **Atomic**: Elements all the same type.\n-   **List**: Elements are different Types.\n\nClosely related but not technically a vector:\n\n-   **NULL**: Null elements. Often length zero.\n\n\n## Types of Atomic Vectors (1/2)\n\n{width=50%} \n\n## Types of Atomic Vectors (2/2)\n\n-   **Logical**: True/False\n-   **Integer**: Numeric (discrete, no decimals)\n-   **Double**: Numeric (continuous, decimals)\n-   **Character**: String\n\n## Vectors of Length One\n\n**Scalars** are vectors that consist of a single value.\n\n## Logicals\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlgl1 <- TRUE\nlgl2 <- T #abbreviation for TRUE\nlgl3 <- FALSE\nlgl4 <- F #abbreviation for FALSE\n```\n:::\n\n\n## Doubles\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# integer, decimal, scientific, or hexidecimal format\ndbl1 <- 1\ndbl2 <- 1.234 # decimal\ndbl3 <- 1.234e0 # scientific format\ndbl4 <- 0xcafe # hexidecimal format\n```\n:::\n\n\n## Integers\n\nIntegers must be followed by L and cannot have fractional values\n\n\n::: {.cell}\n\n```{.r .cell-code}\nint1 <- 1L\nint2 <- 1234L\nint3 <- 1234e0L\nint4 <- 0xcafeL\n```\n:::\n\n\n<details>\n<summary>Pop Quiz: Why \"L\" for integers?</summary>\nWickham notes that the use of `L` dates back to the **C** programming language and its \"long int\" type for memory allocation.\n</details>\n\n## Strings\n\nStrings can use single or double quotes and special characters are escaped with \\\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr1 <- \"hello\" # double quotes\nstr2 <- 'hello' # single quotes\nstr3 <- \"مرحبًا\" # Unicode\nstr4 <- \"\\U0001f605\" # sweaty_smile 😅\n```\n:::\n\n\n## Longer 1/2\n\nThere are several ways to make longer vectors:\n\n**1. With single values** inside c() for combine.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlgl_var <- c(TRUE, FALSE)\nint_var <- c(1L, 6L, 10L)\ndbl_var <- c(1, 2.5, 4.5)\nchr_var <- c(\"these are\", \"some strings\")\n```\n:::\n\n\n \n\n## Longer 2/2\n\n**2. With other vectors**\n\n\n::: {.cell}\n\n```{.r .cell-code}\nc(c(1, 2), c(3, 4)) # output is not nested\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 4\n```\n\n\n:::\n:::\n\n\n## Type and Length\n\nWe can determine the type of a vector with `typeof()` and its length with `length()`\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n<div id=\"scliwijqex\" style=\"padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;\">\n<style>#scliwijqex table {\n  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#scliwijqex thead, #scliwijqex tbody, #scliwijqex tfoot, #scliwijqex tr, #scliwijqex td, #scliwijqex th {\n  border-style: none;\n}\n\n#scliwijqex p {\n  margin: 0;\n  padding: 0;\n}\n\n#scliwijqex .gt_table {\n  display: table;\n  border-collapse: collapse;\n  line-height: normal;\n  margin-left: auto;\n  margin-right: auto;\n  color: #333333;\n  font-size: 16px;\n  font-weight: normal;\n  font-style: normal;\n  background-color: #FFFFFF;\n  width: auto;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #A8A8A8;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #A8A8A8;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n}\n\n#scliwijqex .gt_caption {\n  padding-top: 4px;\n  padding-bottom: 4px;\n}\n\n#scliwijqex .gt_title {\n  color: #333333;\n  font-size: 125%;\n  font-weight: initial;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-color: #FFFFFF;\n  border-bottom-width: 0;\n}\n\n#scliwijqex .gt_subtitle {\n  color: #333333;\n  font-size: 85%;\n  font-weight: initial;\n  padding-top: 3px;\n  padding-bottom: 5px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-color: #FFFFFF;\n  border-top-width: 0;\n}\n\n#scliwijqex .gt_heading {\n  background-color: #FFFFFF;\n  text-align: center;\n  border-bottom-color: #FFFFFF;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#scliwijqex .gt_bottom_border {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#scliwijqex .gt_col_headings {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#scliwijqex .gt_col_heading {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 6px;\n  padding-left: 5px;\n  padding-right: 5px;\n  overflow-x: hidden;\n}\n\n#scliwijqex .gt_column_spanner_outer {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  padding-top: 0;\n  padding-bottom: 0;\n  padding-left: 4px;\n  padding-right: 4px;\n}\n\n#scliwijqex .gt_column_spanner_outer:first-child {\n  padding-left: 0;\n}\n\n#scliwijqex .gt_column_spanner_outer:last-child {\n  padding-right: 0;\n}\n\n#scliwijqex .gt_column_spanner {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 5px;\n  overflow-x: hidden;\n  display: inline-block;\n  width: 100%;\n}\n\n#scliwijqex .gt_spanner_row {\n  border-bottom-style: hidden;\n}\n\n#scliwijqex .gt_group_heading {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  text-align: left;\n}\n\n#scliwijqex .gt_empty_group_heading {\n  padding: 0.5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: middle;\n}\n\n#scliwijqex .gt_from_md > :first-child {\n  margin-top: 0;\n}\n\n#scliwijqex .gt_from_md > :last-child {\n  margin-bottom: 0;\n}\n\n#scliwijqex .gt_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  margin: 10px;\n  border-top-style: solid;\n  border-top-width: 1px;\n  border-top-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  overflow-x: hidden;\n}\n\n#scliwijqex .gt_stub {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#scliwijqex .gt_stub_row_group {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n  vertical-align: top;\n}\n\n#scliwijqex .gt_row_group_first td {\n  border-top-width: 2px;\n}\n\n#scliwijqex .gt_row_group_first th {\n  border-top-width: 2px;\n}\n\n#scliwijqex .gt_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#scliwijqex .gt_first_summary_row {\n  border-top-style: solid;\n  border-top-color: #D3D3D3;\n}\n\n#scliwijqex .gt_first_summary_row.thick {\n  border-top-width: 2px;\n}\n\n#scliwijqex .gt_last_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#scliwijqex .gt_grand_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#scliwijqex .gt_first_grand_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-style: double;\n  border-top-width: 6px;\n  border-top-color: #D3D3D3;\n}\n\n#scliwijqex .gt_last_grand_summary_row_top {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: double;\n  border-bottom-width: 6px;\n  border-bottom-color: #D3D3D3;\n}\n\n#scliwijqex .gt_striped {\n  background-color: rgba(128, 128, 128, 0.05);\n}\n\n#scliwijqex .gt_table_body {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#scliwijqex .gt_footnotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#scliwijqex .gt_footnote {\n  margin: 0px;\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#scliwijqex .gt_sourcenotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#scliwijqex .gt_sourcenote {\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#scliwijqex .gt_left {\n  text-align: left;\n}\n\n#scliwijqex .gt_center {\n  text-align: center;\n}\n\n#scliwijqex .gt_right {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n\n#scliwijqex .gt_font_normal {\n  font-weight: normal;\n}\n\n#scliwijqex .gt_font_bold {\n  font-weight: bold;\n}\n\n#scliwijqex .gt_font_italic {\n  font-style: italic;\n}\n\n#scliwijqex .gt_super {\n  font-size: 65%;\n}\n\n#scliwijqex .gt_footnote_marks {\n  font-size: 75%;\n  vertical-align: 0.4em;\n  position: initial;\n}\n\n#scliwijqex .gt_asterisk {\n  font-size: 100%;\n  vertical-align: 0;\n}\n\n#scliwijqex .gt_indent_1 {\n  text-indent: 5px;\n}\n\n#scliwijqex .gt_indent_2 {\n  text-indent: 10px;\n}\n\n#scliwijqex .gt_indent_3 {\n  text-indent: 15px;\n}\n\n#scliwijqex .gt_indent_4 {\n  text-indent: 20px;\n}\n\n#scliwijqex .gt_indent_5 {\n  text-indent: 25px;\n}\n\n#scliwijqex .katex-display {\n  display: inline-flex !important;\n  margin-bottom: 0.75em !important;\n}\n\n#scliwijqex div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {\n  height: 0px !important;\n}\n</style>\n<table class=\"gt_table\" data-quarto-disable-processing=\"false\" data-quarto-bootstrap=\"false\">\n  <thead>\n    <tr class=\"gt_heading\">\n      <td colspan=\"4\" class=\"gt_heading gt_title gt_font_normal gt_bottom_border\" style>Types of Atomic Vectors<span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span></td>\n    </tr>\n    \n    <tr class=\"gt_col_headings\">\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_names\">name</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_values\">value</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_type\">typeof()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_length\">length()</th>\n    </tr>\n  </thead>\n  <tbody class=\"gt_table_body\">\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">lgl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">TRUE, FALSE</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">logical</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">2</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">int_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1L, 6L, 10L</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">integer</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">3</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">dbl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1, 2.5, 4.5</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">double</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">3</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">chr_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">'these are', 'some strings'</td>\n<td headers=\"var_type\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">character</td>\n<td headers=\"var_length\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">2</td></tr>\n  </tbody>\n  \n  <tfoot class=\"gt_footnotes\">\n    <tr>\n      <td class=\"gt_footnote\" colspan=\"4\"><span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span> Source: https://adv-r.hadley.nz/index.html</td>\n    </tr>\n  </tfoot>\n</table>\n</div>\n```\n\n:::\n:::\n\n\n## Side Quest: Penguins\n\n<details>\n\n::: {.cell}\n\n```{.r .cell-code}\ntypeof(penguins$species)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(penguins$species)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_unlist)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_unlist)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_pull)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_pull)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n\n</details>\n\n## Missing values: Contagion\n\nFor most computations, an operation over values that includes a missing value yields a missing value (unless you're careful)\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# contagion\n5*NA\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] NA\n```\n\n\n:::\n\n```{.r .cell-code}\nsum(c(1, 2, NA, 3))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] NA\n```\n\n\n:::\n:::\n\n\n## Missing values: Contagion Exceptions\n\n\n::: {.cell}\n\n```{.r .cell-code}\nNA ^ 0\n#> [1] 1\nNA | TRUE\n#> [1] TRUE\nNA & FALSE\n#> [1] FALSE\n```\n:::\n\n\n\n#### Innoculation\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsum(c(1, 2, NA, 3), na.rm = TRUE)\n# output: 6\n```\n:::\n\n\nTo search for missing values use `is.na()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(NA, 5, NA, 10)\nx == NA\n# output: NA NA NA NA [BATMAN!]\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nis.na(x)\n# output: TRUE FALSE TRUE FALSE\n```\n:::\n\n\n## Missing Values: NA Types \n\n<details>\nEach type has its own NA type\n\n-   Logical: `NA`\n-   Integer: `NA_integer`\n-   Double: `NA_double`\n-   Character: `NA_character`\n\nThis may not matter in many contexts.\n\nCan matter for operations where types matter like `dplyr::if_else()`.\n</details>\n\n\n## Testing (1/2)\n\n**What type of vector `is.*`() it?**\n\nTest data type:\n\n-   Logical: `is.logical()`\n-   Integer: `is.integer()`\n-   Double: `is.double()`\n-   Character: `is.character()`\n\n\n## Testing (2/2)\n\n**What type of object is it?**\n\nDon't test objects with these tools:\n\n-   `is.vector()`\n-   `is.atomic()`\n-   `is.numeric()` \n\nThey don’t test if you have a vector, atomic vector, or numeric vector; you’ll need to carefully read the documentation to figure out what they actually do (preview: *attributes*)\n\n## Side Quest: rlang `is_*()`\n\n<details>\n<summary>Maybe use `{rlang}`?</summary>\n\n-   `rlang::is_vector`\n-   `rlang::is_atomic`\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# vector\nrlang::is_vector(c(1, 2))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\nrlang::is_vector(list(1, 2))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\n# atomic\nrlang::is_atomic(c(1, 2))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\nrlang::is_atomic(list(1, \"a\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] FALSE\n```\n\n\n:::\n:::\n\n\nSee more [here](https://rlang.r-lib.org/reference/type-predicates.html)\n</details>\n\n\n## Coercion\n\n* R follows rules for coercion: character → double → integer → logical\n\n* R can coerce either automatically or explicitly\n\n#### **Automatic**\n\nTwo contexts for automatic coercion:\n\n1.  Combination\n2.  Mathematical\n\n\n\n## Coercion by Combination:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr(c(TRUE, \"TRUE\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  chr [1:2] \"TRUE\" \"TRUE\"\n```\n\n\n:::\n:::\n\n\n## Coercion by Mathematical operations:\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# imagine a logical vector about whether an attribute is present\nhas_attribute <- c(TRUE, FALSE, TRUE, TRUE)\n\n# number with attribute\nsum(has_attribute)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3\n```\n\n\n:::\n:::\n\n\n## **Explicit**\n\n<!--\n\nUse `as.*()`\n\n-   Logical: `as.logical()`\n-   Integer: `as.integer()`\n-   Double: `as.double()`\n-   Character: `as.character()`\n\n-->\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n<div id=\"prgzooqwyi\" style=\"padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;\">\n<style>#prgzooqwyi table {\n  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#prgzooqwyi thead, #prgzooqwyi tbody, #prgzooqwyi tfoot, #prgzooqwyi tr, #prgzooqwyi td, #prgzooqwyi th {\n  border-style: none;\n}\n\n#prgzooqwyi p {\n  margin: 0;\n  padding: 0;\n}\n\n#prgzooqwyi .gt_table {\n  display: table;\n  border-collapse: collapse;\n  line-height: normal;\n  margin-left: auto;\n  margin-right: auto;\n  color: #333333;\n  font-size: 16px;\n  font-weight: normal;\n  font-style: normal;\n  background-color: #FFFFFF;\n  width: auto;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #A8A8A8;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #A8A8A8;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_caption {\n  padding-top: 4px;\n  padding-bottom: 4px;\n}\n\n#prgzooqwyi .gt_title {\n  color: #333333;\n  font-size: 125%;\n  font-weight: initial;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-color: #FFFFFF;\n  border-bottom-width: 0;\n}\n\n#prgzooqwyi .gt_subtitle {\n  color: #333333;\n  font-size: 85%;\n  font-weight: initial;\n  padding-top: 3px;\n  padding-bottom: 5px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-color: #FFFFFF;\n  border-top-width: 0;\n}\n\n#prgzooqwyi .gt_heading {\n  background-color: #FFFFFF;\n  text-align: center;\n  border-bottom-color: #FFFFFF;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_bottom_border {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_col_headings {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_col_heading {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 6px;\n  padding-left: 5px;\n  padding-right: 5px;\n  overflow-x: hidden;\n}\n\n#prgzooqwyi .gt_column_spanner_outer {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: normal;\n  text-transform: inherit;\n  padding-top: 0;\n  padding-bottom: 0;\n  padding-left: 4px;\n  padding-right: 4px;\n}\n\n#prgzooqwyi .gt_column_spanner_outer:first-child {\n  padding-left: 0;\n}\n\n#prgzooqwyi .gt_column_spanner_outer:last-child {\n  padding-right: 0;\n}\n\n#prgzooqwyi .gt_column_spanner {\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 5px;\n  overflow-x: hidden;\n  display: inline-block;\n  width: 100%;\n}\n\n#prgzooqwyi .gt_spanner_row {\n  border-bottom-style: hidden;\n}\n\n#prgzooqwyi .gt_group_heading {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  text-align: left;\n}\n\n#prgzooqwyi .gt_empty_group_heading {\n  padding: 0.5px;\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  vertical-align: middle;\n}\n\n#prgzooqwyi .gt_from_md > :first-child {\n  margin-top: 0;\n}\n\n#prgzooqwyi .gt_from_md > :last-child {\n  margin-bottom: 0;\n}\n\n#prgzooqwyi .gt_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  margin: 10px;\n  border-top-style: solid;\n  border-top-width: 1px;\n  border-top-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 1px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 1px;\n  border-right-color: #D3D3D3;\n  vertical-align: middle;\n  overflow-x: hidden;\n}\n\n#prgzooqwyi .gt_stub {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#prgzooqwyi .gt_stub_row_group {\n  color: #333333;\n  background-color: #FFFFFF;\n  font-size: 100%;\n  font-weight: initial;\n  text-transform: inherit;\n  border-right-style: solid;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n  padding-left: 5px;\n  padding-right: 5px;\n  vertical-align: top;\n}\n\n#prgzooqwyi .gt_row_group_first td {\n  border-top-width: 2px;\n}\n\n#prgzooqwyi .gt_row_group_first th {\n  border-top-width: 2px;\n}\n\n#prgzooqwyi .gt_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#prgzooqwyi .gt_first_summary_row {\n  border-top-style: solid;\n  border-top-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_first_summary_row.thick {\n  border-top-width: 2px;\n}\n\n#prgzooqwyi .gt_last_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_grand_summary_row {\n  color: #333333;\n  background-color: #FFFFFF;\n  text-transform: inherit;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#prgzooqwyi .gt_first_grand_summary_row {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-top-style: double;\n  border-top-width: 6px;\n  border-top-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_last_grand_summary_row_top {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 5px;\n  padding-right: 5px;\n  border-bottom-style: double;\n  border-bottom-width: 6px;\n  border-bottom-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_striped {\n  background-color: rgba(128, 128, 128, 0.05);\n}\n\n#prgzooqwyi .gt_table_body {\n  border-top-style: solid;\n  border-top-width: 2px;\n  border-top-color: #D3D3D3;\n  border-bottom-style: solid;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_footnotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_footnote {\n  margin: 0px;\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#prgzooqwyi .gt_sourcenotes {\n  color: #333333;\n  background-color: #FFFFFF;\n  border-bottom-style: none;\n  border-bottom-width: 2px;\n  border-bottom-color: #D3D3D3;\n  border-left-style: none;\n  border-left-width: 2px;\n  border-left-color: #D3D3D3;\n  border-right-style: none;\n  border-right-width: 2px;\n  border-right-color: #D3D3D3;\n}\n\n#prgzooqwyi .gt_sourcenote {\n  font-size: 90%;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\n#prgzooqwyi .gt_left {\n  text-align: left;\n}\n\n#prgzooqwyi .gt_center {\n  text-align: center;\n}\n\n#prgzooqwyi .gt_right {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n\n#prgzooqwyi .gt_font_normal {\n  font-weight: normal;\n}\n\n#prgzooqwyi .gt_font_bold {\n  font-weight: bold;\n}\n\n#prgzooqwyi .gt_font_italic {\n  font-style: italic;\n}\n\n#prgzooqwyi .gt_super {\n  font-size: 65%;\n}\n\n#prgzooqwyi .gt_footnote_marks {\n  font-size: 75%;\n  vertical-align: 0.4em;\n  position: initial;\n}\n\n#prgzooqwyi .gt_asterisk {\n  font-size: 100%;\n  vertical-align: 0;\n}\n\n#prgzooqwyi .gt_indent_1 {\n  text-indent: 5px;\n}\n\n#prgzooqwyi .gt_indent_2 {\n  text-indent: 10px;\n}\n\n#prgzooqwyi .gt_indent_3 {\n  text-indent: 15px;\n}\n\n#prgzooqwyi .gt_indent_4 {\n  text-indent: 20px;\n}\n\n#prgzooqwyi .gt_indent_5 {\n  text-indent: 25px;\n}\n\n#prgzooqwyi .katex-display {\n  display: inline-flex !important;\n  margin-bottom: 0.75em !important;\n}\n\n#prgzooqwyi div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {\n  height: 0px !important;\n}\n</style>\n<table class=\"gt_table\" data-quarto-disable-processing=\"false\" data-quarto-bootstrap=\"false\">\n  <thead>\n    <tr class=\"gt_heading\">\n      <td colspan=\"6\" class=\"gt_heading gt_title gt_font_normal gt_bottom_border\" style>Coercion of Atomic Vectors<span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span></td>\n    </tr>\n    \n    <tr class=\"gt_col_headings\">\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_names\">name</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"var_values\">value</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_logical\">as.logical()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_integer\">as.integer()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_double\">as.double()</th>\n      <th class=\"gt_col_heading gt_columns_bottom_border gt_center\" rowspan=\"1\" colspan=\"1\" scope=\"col\" id=\"as_character\">as.character()</th>\n    </tr>\n  </thead>\n  <tbody class=\"gt_table_body\">\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">lgl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">TRUE, FALSE</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE FALSE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 0</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1 0</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'TRUE' 'FALSE'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">int_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1L, 6L, 10L</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE TRUE TRUE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 6 10</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1 6 10</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'1' '6' '10'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">dbl_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">1, 2.5, 4.5</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">TRUE TRUE TRUE</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">1 2 4</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">1.0 2.5 4.5</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'1' '2.5' '4.5'</td></tr>\n    <tr><td headers=\"var_names\" class=\"gt_row gt_center\">chr_var</td>\n<td headers=\"var_values\" class=\"gt_row gt_center\">'these are', 'some strings'</td>\n<td headers=\"as_logical\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">NA NA</td>\n<td headers=\"as_integer\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">NA_integer</td>\n<td headers=\"as_double\" class=\"gt_row gt_center\" style=\"background-color: #F9E3D6;\">NA_double</td>\n<td headers=\"as_character\" class=\"gt_row gt_center\" style=\"background-color: #E0FFFF;\">'these are', 'some strings'</td></tr>\n  </tbody>\n  \n  <tfoot class=\"gt_footnotes\">\n    <tr>\n      <td class=\"gt_footnote\" colspan=\"6\"><span class=\"gt_footnote_marks\" style=\"white-space:nowrap;font-style:italic;font-weight:normal;line-height:0;\"><sup>1</sup></span> Source: https://adv-r.hadley.nz/index.html</td>\n    </tr>\n  </tfoot>\n</table>\n</div>\n```\n\n:::\n:::\n\n\nBut note that coercion may fail in one of two ways, or both:\n\n-   With warning/error\n-   NAs\n\n\n::: {.cell}\n\n```{.r .cell-code}\nas.integer(c(1, 2, \"three\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1]  1  2 NA\n```\n\n\n:::\n:::\n\n\n## Exercises 1/5\n\n1. How do you create raw and complex scalars?\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n```{.r .cell-code}\nas.raw(42)\n#> [1] 2a\ncharToRaw(\"A\")\n#> [1] 41\ncomplex(length.out = 1, real = 1, imaginary = 1)\n#> [1] 1+1i\n```\n:::\n\n</details>\n\n## Exercises 2/5\n\n2. Test your knowledge of the vector coercion rules by predicting the output of the following uses of c():\n\n\n::: {.cell}\n\n```{.r .cell-code}\nc(1, FALSE)\nc(\"a\", 1)\nc(TRUE, 1L)\n```\n:::\n\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n```{.r .cell-code}\nc(1, FALSE)      # will be coerced to double    -> 1 0\nc(\"a\", 1)        # will be coerced to character -> \"a\" \"1\"\nc(TRUE, 1L)      # will be coerced to integer   -> 1 1\n```\n:::\n\n</details>\n\n## Exercises 3/5\n\n3. Why is `1 == \"1\"` true? Why is `-1 < FALSE` true? Why is `\"one\" < 2` false?\n\n<details><summary>Answer(s)</summary>\nThese comparisons are carried out by operator-functions (==, <), which coerce their arguments to a common type. In the examples above, these types will be character, double and character: 1 will be coerced to \"1\", FALSE is represented as 0 and 2 turns into \"2\" (and numbers precede letters in lexicographic order (may depend on locale)).\n\n</details>\n\n## Exercises 4/5\n\n4. Why is the default missing value, NA, a logical vector? What’s special about logical vectors?\n\n<details><summary>Answer(s)</summary>\nThe presence of missing values shouldn’t affect the type of an object. Recall that there is a type-hierarchy for coercion from character → double → integer → logical. When combining `NA`s with other atomic types, the `NA`s will be coerced to integer (`NA_integer_`), double (`NA_real_`) or character (`NA_character_`) and not the other way round. If `NA` were a character and added to a set of other values all of these would be coerced to character as well.\n</details>\n\n## Exercises 5/5\n\n5. Precisely what do `is.atomic()`, `is.numeric()`, and `is.vector()` test for?\n\n<details><summary>Answer(s)</summary>\n\n* `is.atomic()` tests if an object is an atomic vector or is `NULL` (!). Atomic vectors are objects of type logical, integer, double, complex, character or raw.\n* `is.numeric()` tests if an object has type integer or double and is not of class `factor`, `Date`, `POSIXt` or `difftime`.\n* `is.vector()` tests if an object is a vector or an expression and has no attributes, apart from names. Vectors are atomic vectors or lists.\n \n</details>\n\n\n## Attributes\n\nAttributes are name-value pairs that attach metadata to an object (vector).\n\n* **Name-value pairs**: attributes have a name and a value\n* **Metadata**: not data itself, but data about the data\n \n## Getting and Setting\n\nThree functions:\n\n1. retrieve and modify single attributes with `attr()`\n2. retrieve en masse with `attributes()`\n3. set en masse with `structure()`\n\n## Single attribute\n\nUse `attr()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# some object\na <- c(1, 2, 3)\n\n# set attribute\nattr(x = a, which = \"attribute_name\") <- \"some attribute\"\n\n# get attribute\nattr(a, \"attribute_name\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"some attribute\"\n```\n\n\n:::\n:::\n\n\n## Multiple attributes\n\n`structure()`: set multiple attributes, `attributes()`: get multiple attributes\n\n:::: columns\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\na <- 1:3\nattr(a, \"x\") <- \"abcdef\"\nattr(a, \"x\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"abcdef\"\n```\n\n\n:::\n\n```{.r .cell-code}\nattr(a, \"y\") <- 4:6\nstr(attributes(a))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 2\n#>  $ x: chr \"abcdef\"\n#>  $ y: int [1:3] 4 5 6\n```\n\n\n:::\n\n```{.r .cell-code}\nb <- structure(\n  1:3, \n  x = \"abcdef\",\n  y = 4:6\n)\nidentical(a, b)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n \n:::\n::::\n\n\n## Why\n\nThree particularly important attributes: \n\n1. **names** - a character vector giving each element a name\n2. **dimension** - (or dim) turns vectors into matrices and arrays \n3. **class** - powers the S3 object system (we'll learn more about this in chapter 13)\n\nMost attributes are lost by most operations.  Only two attributes are routinely preserved: names and dimension.\n\n## Names\n\n~~Three~~ Four ways to name:\n\n:::: columns\n\n::: {.column width=\"50%\"}\n\n::: {.cell}\n\n```{.r .cell-code}\n# (1) On creation: \nx <- c(A = 1, B = 2, C = 3)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> A B C \n#> 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\n# (2) Assign to names():\ny <- 1:3\nnames(y) <- c(\"a\", \"b\", \"c\")\ny\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\n# (3) Inline:\nz <- setNames(1:3, c(\"a\", \"b\", \"c\"))\nz\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n:::\n\n:::\n\n::: {.column width=\"50%\"}\n \n:::\n\n::::\n\n## rlang Names\n\n:::: columns\n\n::: {.column width=\"50%\"}\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# (4) Inline with {rlang}:\na <- 1:3\nrlang::set_names(\n  a,\n  c(\"a\", \"b\", \"c\")\n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> a b c \n#> 1 2 3\n```\n\n\n:::\n:::\n\n\n:::\n\n::: {.column width=\"50%\"}\n \n:::\n\n::::\n\n\n## Removing names\n\n* `x <- unname(x)` or `names(x) <- NULL`\n* Thematically but not directly related: labelled class vectors with `haven::labelled()`\n\n\n## Dimensions: `matrix()` and `array()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Two scalar arguments specify row and column sizes\nx <- matrix(1:6, nrow = 2, ncol = 3)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n```\n\n\n:::\n\n```{.r .cell-code}\n# One vector argument to describe all dimensions\ny <- array(1:12, c(2, 3, 2)) # rows, columns, no of arrays\ny\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> , , 1\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n#> \n#> , , 2\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    7    9   11\n#> [2,]    8   10   12\n```\n\n\n:::\n:::\n\n\n## Dimensions: assign to `dim()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# You can also modify an object in place by setting dim()\nz <- 1:6\ndim(z) <- c(2, 3) # rows, columns\nz\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n```\n\n\n:::\n\n```{.r .cell-code}\na <- 1:12\ndim(a) <- c(2, 3, 2) # rows, columns, no of arrays\na\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> , , 1\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    1    3    5\n#> [2,]    2    4    6\n#> \n#> , , 2\n#> \n#>      [,1] [,2] [,3]\n#> [1,]    7    9   11\n#> [2,]    8   10   12\n```\n\n\n:::\n:::\n\n\n\n## Functions for working with vectors, matrices and arrays (1/2):\n\nVector | Matrix\t| Array\n:----- | :---------- | :-----\n`names()` | `rownames()`, `colnames()` | `dimnames()`\n`length()` | `nrow()`, `ncol()` | `dim()`\n`c()` | `rbind()`, `cbind()` | `abind::abind()`\n— | `t()` | `aperm()`\n`is.null(dim(x))` | `is.matrix()` | `is.array()`\n\n* **Caution**: Vector without `dim` set has `NULL` dimensions, not `1`.\n* One dimension?\n\n## Functions for working with vectors, matrices and arrays (2/2):\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr(1:3)                   # 1d vector\n#>  int [1:3] 1 2 3\nstr(matrix(1:3, ncol = 1)) # column vector\n#>  int [1:3, 1] 1 2 3\nstr(matrix(1:3, nrow = 1)) # row vector\n#>  int [1, 1:3] 1 2 3\nstr(array(1:3, 3))         # \"array\" vector\n#>  int [1:3(1d)] 1 2 3\n```\n:::\n\n\n\n## Exercises 1/4\n\n1. How is `setNames()` implemented? Read the source code.\n\n<details><summary>Answer(s)</summary>\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsetNames <- function(object = nm, nm) {\n  names(object) <- nm\n  object\n}\n```\n:::\n\n\n- Data arg 1st = works well with pipe.\n- 1st arg is optional\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsetNames( , c(\"a\", \"b\", \"c\"))\n#>   a   b   c \n#> \"a\" \"b\" \"c\"\n```\n:::\n\n</details>\n\n## Exercises 1/4 (cont)\n\n1. How is `unname()` implemented? Read the source code.\n\n<details><summary>Answer(s)</summary>\n\n\n::: {.cell}\n\n```{.r .cell-code}\nunname <- function(obj, force = FALSE) {\n  if (!is.null(names(obj))) \n    names(obj) <- NULL\n  if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) \n    dimnames(obj) <- NULL\n  obj\n}\n```\n:::\n\n`unname()` sets existing `names` or `dimnames` to `NULL`.\n</details>\n\n## Exercises 2/4\n\n2. What does `dim()` return when applied to a 1-dimensional vector? When might you use `NROW()` or `NCOL()`?\n\n<details><summary>Answer(s)</summary>\n\n> `dim()` returns `NULL` when applied to a 1d vector.\n\n`NROW()` and `NCOL()` treats `NULL` and vectors like they have dimensions:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:10\nnrow(x)\n#> NULL\nncol(x)\n#> NULL\nNROW(x)\n#> [1] 10\nNCOL(x)\n#> [1] 1\n```\n:::\n\n\n</details>\n\n## Exercises 3/4\n\n3. How would you describe the following three objects? What makes them different from `1:5`?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx1 <- array(1:5, c(1, 1, 5))\nx2 <- array(1:5, c(1, 5, 1))\nx3 <- array(1:5, c(5, 1, 1))\n```\n:::\n\n\n<details><summary>Answer(s)</summary>\n\n::: {.cell}\n\n```{.r .cell-code}\nx1 <- array(1:5, c(1, 1, 5))  # 1 row,  1 column,  5 in third dim.\nx2 <- array(1:5, c(1, 5, 1))  # 1 row,  5 columns, 1 in third dim.\nx3 <- array(1:5, c(5, 1, 1))  # 5 rows, 1 column,  1 in third dim.\n```\n:::\n\n</details>\n\n## Exercises 4/4\n\n4. An early draft used this code to illustrate `structure()`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstructure(1:5, comment = \"my attribute\")\n#> [1] 1 2 3 4 5\n```\n:::\n\n\nWhy don't you see the comment attribute on print? Is the attribute missing, or is there something else special about it?\n\n<details><summary>Answer(s)</summary>\nThe documentation states (see `?comment`):\n\n> Contrary to other attributes, the comment is not printed (by print or print.default).\n\n## Exercises 4/4 (cont)\n\n<details><summary>Answer(s)</summary>\nAlso, from `?attributes:`\n\n> Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set.\n\nRetrieve comment attributes with `attr()`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nfoo <- structure(1:5, comment = \"my attribute\")\n\nattributes(foo)\n#> $comment\n#> [1] \"my attribute\"\nattr(foo, which = \"comment\")\n#> [1] \"my attribute\"\n```\n:::\n\n\n</details>\n\n\n\n## **Class** - S3 atomic vectors\n\n \n\nCredit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham\n\n**Having a class attribute turns an object into an S3 object.**\n\nWhat makes S3 atomic vectors different?\n\n1. behave differently from a regular vector when passed to a generic function \n2. often store additional information in other attributes\n\n\n## Four important S3 vectors used in base R:\n\n1. **Factors** (categorical data)\n2. **Dates**\n3. **Date-times** (POSIXct)\n4. **Durations** (difftime)\n\n## Factors\n\nA factor is a vector used to store categorical data that can contain only predefined values.\n\nFactors are integer vectors with:\n\n-   Class: \"factor\"\n-   Attributes: \"levels\", or the set of allowed values\n\n## Factors examples\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolors = c('red', 'blue', 'green','red','red', 'green')\ncolors_factor <- factor(\n  x = colors, levels = c('red', 'blue', 'green', 'yellow')\n)\n```\n:::\n\n\n:::: columns\n\n::: column\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntable(colors)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> colors\n#>  blue green   red \n#>     1     2     3\n```\n\n\n:::\n\n```{.r .cell-code}\ntable(colors_factor)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> colors_factor\n#>    red   blue  green yellow \n#>      3      1      2      0\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\ntypeof(colors_factor)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(colors_factor)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\nattributes(colors_factor)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $levels\n#> [1] \"red\"    \"blue\"   \"green\"  \"yellow\"\n#> \n#> $class\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n:::\n::::\n\n## Custom Order\n\nFactors can be ordered. This can be useful for models or visualizations where order matters.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nvalues <- c('high', 'med', 'low', 'med', 'high', 'low', 'med', 'high')\nordered_factor <- ordered(\n  x = values,\n  levels = c('low', 'med', 'high') # in order\n)\nordered_factor\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] high med  low  med  high low  med  high\n#> Levels: low < med < high\n```\n\n\n:::\n\n```{.r .cell-code}\ntable(values)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> values\n#> high  low  med \n#>    3    2    3\n```\n\n\n:::\n\n```{.r .cell-code}\ntable(ordered_factor)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> ordered_factor\n#>  low  med high \n#>    2    3    3\n```\n\n\n:::\n:::\n\n\n## Dates\n\nDates are:\n\n-   Double vectors\n-   With class \"Date\"\n-   No other attributes\n\n\n::: {.cell}\n\n```{.r .cell-code}\nnotes_date <- Sys.Date()\n\n# type\ntypeof(notes_date)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# class\nattributes(notes_date)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"Date\"\n```\n\n\n:::\n:::\n\n\n## Dates Unix epoch\n\nThe double component represents the number of days since since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time) `1970-01-01`\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndate <- as.Date(\"1970-02-01\")\nunclass(date)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 31\n```\n\n\n:::\n:::\n\n\n## Date-times\n\nThere are 2 Date-time representations in base R:\n\n-   POSIXct, where \"ct\" denotes *calendar time*\n-   POSIXlt, where \"lt\" designates *local time*\n\n<!--\n\nJust for fun:\n\"How to pronounce 'POSIXct'?\"\nhttps://www.howtopronounce.com/posixct\n\n-->\n\n## Dates-times: POSIXct\n\nWe'll focus on POSIXct because:\n\n-   Simplest\n-   Built on an atomic (double) vector\n-   Most appropriate for use in a data frame\n\nLet's now build and deconstruct a Date-time\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Build\nnote_date_time <- as.POSIXct(\n  x = Sys.time(), # time\n  tz = \"America/New_York\" # time zone, used only for formatting\n)\n\n# Inspect\nnote_date_time\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"2025-09-03 07:11:21 EDT\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# - type\ntypeof(note_date_time)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# - attributes\nattributes(note_date_time)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"POSIXct\" \"POSIXt\" \n#> \n#> $tzone\n#> [1] \"America/New_York\"\n```\n\n\n:::\n\n```{.r .cell-code}\nstructure(note_date_time, tzone = \"Europe/Paris\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"2025-09-03 13:11:21 CEST\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndate_time <- as.POSIXct(\"2024-02-22 12:34:56\", tz = \"EST\")\nunclass(date_time)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1708623296\n#> attr(,\"tzone\")\n#> [1] \"EST\"\n```\n\n\n:::\n:::\n\n\n\n## Durations\n\nDurations represent the amount of time between pairs of dates or date-times.\n\n-   Double vectors\n-   Class: \"difftime\"\n-   Attributes: \"units\", or the unit of duration (e.g., weeks, hours, minutes, seconds, etc.)\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Construct\none_minute <- as.difftime(1, units = \"mins\")\n# Inspect\none_minute\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> Time difference of 1 mins\n```\n\n\n:::\n\n```{.r .cell-code}\n# Dissect\n# - type\ntypeof(one_minute)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"double\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# - attributes\nattributes(one_minute)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $class\n#> [1] \"difftime\"\n#> \n#> $units\n#> [1] \"mins\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntime_since_01_01_1970 <- notes_date - date\ntime_since_01_01_1970\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> Time difference of 20303 days\n```\n\n\n:::\n:::\n\n\n\nSee also:\n\n-   [`lubridate::make_difftime()`](https://lubridate.tidyverse.org/reference/make_difftime.html)\n-   [`clock::date_time_build()`](https://clock.r-lib.org/reference/date_time_build.html)\n\n\n## Exercises 1/3\n\n1. What sort of object does `table()` return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables?\n\n<details><summary>Answer(s)</summary>\n\n`table()` returns a contingency table of its input variables. It is implemented as an integer vector with class table and dimensions (which makes it act like an array). Its attributes are dim (dimensions) and dimnames (one name for each input column). The dimensions correspond to the number of unique values (factor levels) in each input variable.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- table(mtcars[c(\"vs\", \"cyl\", \"am\")])\n\ntypeof(x)\n#> [1] \"integer\"\nattributes(x)\n#> $dim\n#> [1] 2 3 2\n#> \n#> $dimnames\n#> $dimnames$vs\n#> [1] \"0\" \"1\"\n#> \n#> $dimnames$cyl\n#> [1] \"4\" \"6\" \"8\"\n#> \n#> $dimnames$am\n#> [1] \"0\" \"1\"\n#> \n#> \n#> $class\n#> [1] \"table\"\n```\n:::\n\n</details>\n\n## Exercises 2/3\n\n2. What happens to a factor when you modify its levels?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nf1 <- factor(letters)\nlevels(f1) <- rev(levels(f1))\n```\n:::\n\n\n<details><summary>Answer(s)</summary>\nThe underlying integer values stay the same, but the levels are changed, making it look like the data has changed.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nf1 <- factor(letters)\nf1\n#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z\n#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z\nas.integer(f1)\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25\n#> [26] 26\n\nlevels(f1) <- rev(levels(f1))\nf1\n#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a\n#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a\nas.integer(f1)\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25\n#> [26] 26\n```\n:::\n\n</details>\n\n## Exercises 3/3\n\n3. What does this code do? How do `f2` and `f3` differ from `f1`?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nf2 <- rev(factor(letters))\nf3 <- factor(letters, levels = rev(letters))\n```\n:::\n\n\n<details><summary>Answer(s)</summary>\nFor `f2` and `f3` either the order of the factor elements or its levels are being reversed. For `f1` both transformations are occurring.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Reverse element order\n(f2 <- rev(factor(letters)))\n#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a\n#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z\nas.integer(f2)\n#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2\n#> [26]  1\n\n# Reverse factor levels (when creating factor)\n(f3 <- factor(letters, levels = rev(letters)))\n#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z\n#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a\nas.integer(f3)\n#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2\n#> [26]  1\n```\n:::\n\n</details>\n\n\n## Lists\n\n* sometimes called a generic vector or recursive vector\n* Recall ([section 2.3.3](https://adv-r.hadley.nz/names-values.html#list-references)): each element is really a *reference* to another object\n* an be composed of elements of different types (as opposed to atomic vectors which must be of only one type)\n\n## Constructing\n\nSimple lists:\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Construct\nsimple_list <- list(\n  c(TRUE, FALSE),   # logicals\n  1:20,             # integers\n  c(1.2, 2.3, 3.4), # doubles\n  c(\"primo\", \"secundo\", \"tercio\") # characters\n)\n\nsimple_list\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1]  TRUE FALSE\n#> \n#> [[2]]\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20\n#> \n#> [[3]]\n#> [1] 1.2 2.3 3.4\n#> \n#> [[4]]\n#> [1] \"primo\"   \"secundo\" \"tercio\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# Inspect\n# - type\ntypeof(simple_list)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# - structure\nstr(simple_list)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : logi [1:2] TRUE FALSE\n#>  $ : int [1:20] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ : num [1:3] 1.2 2.3 3.4\n#>  $ : chr [1:3] \"primo\" \"secundo\" \"tercio\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# Accessing\nsimple_list[1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1]  TRUE FALSE\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[2]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1.2 2.3 3.4\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[4]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] \"primo\"   \"secundo\" \"tercio\"\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[[1]][2]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] FALSE\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[[2]][8]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 8\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[[3]][2]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.3\n```\n\n\n:::\n\n```{.r .cell-code}\nsimple_list[[4]][3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"tercio\"\n```\n\n\n:::\n:::\n\n\n## Even Simpler List\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Construct\nsimpler_list <- list(TRUE, FALSE, \n                    1, 2, 3, 4, 5, \n                    1.2, 2.3, 3.4, \n                    \"primo\", \"secundo\", \"tercio\")\n\n# Accessing\nsimpler_list[1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\nsimpler_list[5]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 3\n```\n\n\n:::\n\n```{.r .cell-code}\nsimpler_list[9]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 2.3\n```\n\n\n:::\n\n```{.r .cell-code}\nsimpler_list[11]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] \"primo\"\n```\n\n\n:::\n:::\n\n\n## Nested lists:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nnested_list <- list(\n  # first level\n  list(\n    # second level\n    list(\n      # third level\n      list(1)\n    )\n  )\n)\n\nstr(nested_list)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 1\n#>  $ :List of 1\n#>   ..$ :List of 1\n#>   .. ..$ :List of 1\n#>   .. .. ..$ : num 1\n```\n\n\n:::\n:::\n\n\nLike JSON.\n\n## Combined lists\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlist_comb1 <- list(list(1, 2), list(3, 4)) # with list()\nlist_comb2 <- c(list(1, 2), list(3, 4)) # with c()\n\n# compare structure\nstr(list_comb1)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 2\n#>  $ :List of 2\n#>   ..$ : num 1\n#>   ..$ : num 2\n#>  $ :List of 2\n#>   ..$ : num 3\n#>   ..$ : num 4\n```\n\n\n:::\n\n```{.r .cell-code}\nstr(list_comb2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : num 1\n#>  $ : num 2\n#>  $ : num 3\n#>  $ : num 4\n```\n\n\n:::\n\n```{.r .cell-code}\n# does this work if they are different data types?\nlist_comb3 <- c(list(1, 2), list(TRUE, FALSE))\nstr(list_comb3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> List of 4\n#>  $ : num 1\n#>  $ : num 2\n#>  $ : logi TRUE\n#>  $ : logi FALSE\n```\n\n\n:::\n:::\n\n\n## Testing\n\nCheck that is a list:\n\n-   `is.list()`\n-   \\`rlang::is_list()\\`\\`\n\nThe two do the same, except that the latter can check for the number of elements\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# is list\nbase::is.list(list_comb2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\nrlang::is_list(list_comb2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\n# is list of 4 elements\nrlang::is_list(x = list_comb2, n = 4)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n\n```{.r .cell-code}\n# is a vector (of a special type)\n# remember the family tree?\nrlang::is_vector(list_comb2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n:::\n\n\n## Coercion\n\nUse `as.list()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlist(1:3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nas.list(1:3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1\n#> \n#> [[2]]\n#> [1] 2\n#> \n#> [[3]]\n#> [1] 3\n```\n\n\n:::\n:::\n\n\n## Matrices and arrays\n\nAlthough not often used, the dimension attribute can be added to create **list-matrices** or **list-arrays**.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nl <- list(1:3, \"a\", TRUE, 1.0)\ndim(l) <- c(2, 2); l\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1]      [,2]\n#> [1,] integer,3 TRUE\n#> [2,] \"a\"       1\n```\n\n\n:::\n\n```{.r .cell-code}\nl[[1, 1]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n:::\n\n\n\n## Exercises 1/3\n\n1. List all the ways that a list differs from an atomic vector.\n\n<details><summary>Answer(s)</summary>\n\n* Atomic vectors are always homogeneous (all elements must be of the same type). Lists may be heterogeneous (the elements can be of different types) as described in the introduction of the vectors chapter.\n* Atomic vectors point to one address in memory, while lists contain a separate reference for each element. (This was described in the list sections of the vectors and the names and values chapters.)\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlobstr::ref(1:2)\n#> [1:0x7fcd936f6e80] <int>\nlobstr::ref(list(1:2, 2))\n#> █ [1:0x7fcd93d53048] <list> \n#> ├─[2:0x7fcd91377e40] <int> \n#> └─[3:0x7fcd93b41eb0] <dbl>\n```\n:::\n\n\n\n* Subsetting with out-of-bounds and NA values leads to different output. For example, [ returns NA for atomics and NULL for lists. (This is described in more detail within the subsetting chapter.)\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Subsetting atomic vectors\n(1:2)[3]\n#> [1] NA\n(1:2)[NA]\n#> [1] NA NA\n\n# Subsetting lists\nas.list(1:2)[3]\n#> [[1]]\n#> NULL\nas.list(1:2)[NA]\n#> [[1]]\n#> NULL\n#> \n#> [[2]]\n#> NULL\n```\n:::\n\n\n\n</details>\n\n## Exercises 2/3\n\n2. Why do you need to use `unlist()` to convert a list to an atomic vector? Why doesn’t `as.vector()` work?\n\n<details><summary>Answer(s)</summary>\nA list is already a vector, though not an atomic one! Note that as.vector() and is.vector() use different definitions of “vector!”\n\n\n::: {.cell}\n\n```{.r .cell-code}\nis.vector(as.vector(mtcars))\n#> [1] FALSE\n```\n:::\n\n\n</details>\n\n## Exercises 3/3\n\n3. Compare and contrast `c()` and `unlist()` when combining a date and date-time into a single vector.\n\n<details><summary>Answer(s)</summary>\nDate and date-time objects are both built upon doubles. While dates store the number of days since the reference date 1970-01-01 (also known as “the Epoch”) in days, date-time-objects (POSIXct) store the time difference to this date in seconds.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndate    <- as.Date(\"1970-01-02\")\ndttm_ct <- as.POSIXct(\"1970-01-01 01:00\", tz = \"UTC\")\n\n# Internal representations\nunclass(date)\n#> [1] 1\nunclass(dttm_ct)\n#> [1] 3600\n#> attr(,\"tzone\")\n#> [1] \"UTC\"\n```\n:::\n\n\nAs the c() generic only dispatches on its first argument, combining date and date-time objects via c() could lead to surprising results in older R versions (pre R 4.0.0):\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Output in R version 3.6.2\nc(date, dttm_ct)  # equal to c.Date(date, dttm_ct) \n#> [1] \"1970-01-02\" \"1979-11-10\"\nc(dttm_ct, date)  # equal to c.POSIXct(date, dttm_ct)\n#> [1] \"1970-01-01 02:00:00 CET\" \"1970-01-01 01:00:01 CET\"\n```\n:::\n\n\nIn the first statement above c.Date() is executed, which incorrectly treats the underlying double of dttm_ct (3600) as days instead of seconds. Conversely, when c.POSIXct() is called on a date, one day is counted as one second only.\n\nWe can highlight these mechanics by the following code:\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Output in R version 3.6.2\nunclass(c(date, dttm_ct))  # internal representation\n#> [1] 1 3600\ndate + 3599\n#> \"1979-11-10\"\n```\n:::\n\n\nAs of R 4.0.0 these issues have been resolved and both methods now convert their input first into POSIXct and Date, respectively.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nc(dttm_ct, date)\n#> [1] \"1970-01-01 01:00:00 UTC\" \"1970-01-02 00:00:00 UTC\"\nunclass(c(dttm_ct, date))\n#> [1]  3600 86400\n\nc(date, dttm_ct)\n#> [1] \"1970-01-02\" \"1970-01-01\"\nunclass(c(date, dttm_ct))\n#> [1] 1 0\n```\n:::\n\n\nHowever, as c() strips the time zone (and other attributes) of POSIXct objects, some caution is still recommended.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n(dttm_ct <- as.POSIXct(\"1970-01-01 01:00\", tz = \"HST\"))\n#> [1] \"1970-01-01 01:00:00 HST\"\nattributes(c(dttm_ct))\n#> $class\n#> [1] \"POSIXct\" \"POSIXt\"\n```\n:::\n\n\nA package that deals with these kinds of problems in more depth and provides a structural solution for them is the {vctrs} package9 which is also used throughout the tidyverse.10\n\nLet’s look at unlist(), which operates on list input.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Attributes are stripped\nunlist(list(date, dttm_ct))  \n#> [1]     1 39600\n```\n:::\n\n\nWe see again that dates and date-times are internally stored as doubles. Unfortunately, this is all we are left with, when unlist strips the attributes of the list.\n\nTo summarise: c() coerces types and strips time zones. Errors may have occurred in older R versions because of inappropriate method dispatch/immature methods. unlist() strips attributes.\n</details>\n\n\n## Data frames and tibbles\n\n \n\nCredit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham\n\n## Data frame\n\nA data frame is a:\n\n-   Named list of vectors (i.e., column names)\n-   Attributes:\n    -   (column) `names`\n    -   `row.names`\n    -   Class: \"data frame\"\n\n## Data frame, examples 1/2:\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Construct\ndf <- data.frame(\n  col1 = c(1, 2, 3),              # named atomic vector\n  col2 = c(\"un\", \"deux\", \"trois\") # another named atomic vector\n  # ,stringsAsFactors = FALSE # default for versions after R 4.1\n)\n# Inspect\ndf\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   col1  col2\n#> 1    1    un\n#> 2    2  deux\n#> 3    3 trois\n```\n\n\n:::\n\n```{.r .cell-code}\n# Deconstruct\n# - type\ntypeof(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\n# - attributes\nattributes(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $names\n#> [1] \"col1\" \"col2\"\n#> \n#> $class\n#> [1] \"data.frame\"\n#> \n#> $row.names\n#> [1] 1 2 3\n```\n\n\n:::\n:::\n\n\n\n## Data frame, examples 2/2:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nrownames(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"1\" \"2\" \"3\"\n```\n\n\n:::\n\n```{.r .cell-code}\ncolnames(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\" \"col2\"\n```\n\n\n:::\n\n```{.r .cell-code}\nnames(df) # Same as colnames(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\" \"col2\"\n```\n\n\n:::\n\n```{.r .cell-code}\nnrow(df) \n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3\n```\n\n\n:::\n\n```{.r .cell-code}\nncol(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2\n```\n\n\n:::\n\n```{.r .cell-code}\nlength(df) # Same as ncol(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2\n```\n\n\n:::\n:::\n\n\nUnlike other lists, the length of each vector must be the same (i.e. as many vector elements as rows in the data frame).\n\n## Tibble\n\nCreated to relieve some of the frustrations and pain points created by data frames, tibbles are data frames that are:\n\n-   Lazy (do less)\n-   Surly (complain more)\n\n## Lazy\n\nTibbles do not:\n\n-   Coerce strings\n-   Transform non-syntactic names\n-   Recycle vectors of length greater than 1\n\n## ! Coerce strings\n\n\n::: {.cell}\n\n```{.r .cell-code}\nchr_col <- c(\"don't\", \"factor\", \"me\", \"bro\")\n\n# data frame\ndf <- data.frame(\n  a = chr_col,\n  # in R 4.1 and earlier, this was the default\n  stringsAsFactors = TRUE\n)\n\n# tibble\ntbl <- tibble::tibble(\n  a = chr_col\n)\n\n# contrast the structure\nstr(df$a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  Factor w/ 4 levels \"bro\",\"don't\",..: 2 3 4 1\n```\n\n\n:::\n\n```{.r .cell-code}\nstr(tbl$a)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  chr [1:4] \"don't\" \"factor\" \"me\" \"bro\"\n```\n\n\n:::\n:::\n\n\n## ! Transform non-syntactic names\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# data frame\ndf <- data.frame(\n  `1` = c(1, 2, 3)\n)\n\n# tibble\ntbl <- tibble::tibble(\n  `1` = c(1, 2, 3)\n)\n\n# contrast the names\nnames(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"X1\"\n```\n\n\n:::\n\n```{.r .cell-code}\nnames(tbl)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"1\"\n```\n\n\n:::\n:::\n\n\n## ! Recycle vectors of length greater than 1\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# data frame\ndf <- data.frame(\n  col1 = c(1, 2, 3, 4),\n  col2 = c(1, 2)\n)\n\n# tibble\ntbl <- tibble::tibble(\n  col1 = c(1, 2, 3, 4),\n  col2 = c(1, 2)\n)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in `tibble::tibble()`:\n#> ! Tibble columns must have compatible sizes.\n#> • Size 4: Existing data.\n#> • Size 2: Column `col2`.\n#> ℹ Only values of size one are recycled.\n```\n\n\n:::\n:::\n\n\n## Surly\n\nTibbles do only what they're asked and complain if what they're asked doesn't make sense:\n\n-   Subsetting always yields a tibble\n-   Complains if cannot find column\n\n## Subsetting always yields a tibble\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# data frame\ndf <- data.frame(\n  col1 = c(1, 2, 3, 4)\n)\n\n# tibble\ntbl <- tibble::tibble(\n  col1 = c(1, 2, 3, 4)\n)\n\n# contrast\ndf_col <- df[, \"col1\"]\nstr(df_col)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n\n```{.r .cell-code}\ntbl_col <- tbl[, \"col1\"]\nstr(tbl_col)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [4 × 1] (S3: tbl_df/tbl/data.frame)\n#>  $ col1: num [1:4] 1 2 3 4\n```\n\n\n:::\n\n```{.r .cell-code}\n# to select a vector, do one of these instead\ntbl_col_1 <- tbl[[\"col1\"]]\nstr(tbl_col_1)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n\n```{.r .cell-code}\ntbl_col_2 <- dplyr::pull(tbl, col1)\nstr(tbl_col_2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>  num [1:4] 1 2 3 4\n```\n\n\n:::\n:::\n\n\n## Complains if cannot find column\n\n\n::: {.cell}\n\n```{.r .cell-code}\nnames(df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndf$col\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 4\n```\n\n\n:::\n\n```{.r .cell-code}\nnames(tbl)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"col1\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntbl$col\n```\n\n::: {.cell-output .cell-output-stderr}\n\n```\n#> Warning: Unknown or uninitialised column: `col`.\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n## One more difference\n\n**`tibble()` allows you to refer to variables created during construction**\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntibble::tibble(\n  x = 1:3,\n  y = x * 2 # x refers to the line above\n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 2\n#>       x     y\n#>   <int> <dbl>\n#> 1     1     2\n#> 2     2     4\n#> 3     3     6\n```\n\n\n:::\n:::\n\n\n<details>\n<summary>Side Quest: Row Names</summary>\n\n- character vector containing only unique values\n- get and set with `rownames()`\n- can use them to subset rows\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf3 <- data.frame(\n  age = c(35, 27, 18),\n  hair = c(\"blond\", \"brown\", \"black\"),\n  row.names = c(\"Bob\", \"Susan\", \"Sam\")\n)\ndf3\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       age  hair\n#> Bob    35 blond\n#> Susan  27 brown\n#> Sam    18 black\n```\n\n\n:::\n\n```{.r .cell-code}\nrownames(df3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"Bob\"   \"Susan\" \"Sam\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndf3[\"Bob\", ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     age  hair\n#> Bob  35 blond\n```\n\n\n:::\n\n```{.r .cell-code}\nrownames(df3) <- c(\"Susan\", \"Bob\", \"Sam\")\nrownames(df3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"Susan\" \"Bob\"   \"Sam\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndf3[\"Bob\", ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     age  hair\n#> Bob  27 brown\n```\n\n\n:::\n:::\n\n\nThere are three reasons why row names are undesirable:\n\n3. Metadata is data, so storing it in a different way to the rest of the data is fundamentally a bad idea. \n2. Row names are a poor abstraction for labelling rows because they only work when a row can be identified by a single string. This fails in many cases.\n3. Row names must be unique, so any duplication of rows (e.g. from bootstrapping) will create new row names.\n\n</details>\n\n\n## Tibles: Printing\n\nData frames and tibbles print differently\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf3\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       age  hair\n#> Susan  35 blond\n#> Bob    27 brown\n#> Sam    18 black\n```\n\n\n:::\n\n```{.r .cell-code}\ntibble::as_tibble(df3)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 2\n#>     age hair \n#>   <dbl> <chr>\n#> 1    35 blond\n#> 2    27 brown\n#> 3    18 black\n```\n\n\n:::\n:::\n\n\n\n## Tibles: Subsetting\n\nTwo undesirable subsetting behaviours:\n\n1. When you subset columns with `df[, vars]`, you will get a vector if vars selects one variable, otherwise you’ll get a data frame, unless you always remember to use `df[, vars, drop = FALSE]`.\n2. When you attempt to extract a single column with `df$x` and there is no column `x`, a data frame will instead select any variable that starts with `x`. If no variable starts with `x`, `df$x` will return NULL.\n\nTibbles tweak these behaviours so that a [ always returns a tibble, and a $ doesn’t do partial matching and warns if it can’t find a variable (*this is what makes tibbles surly*).\n\n## Tibles: Testing\n\nWhether data frame: `is.data.frame()`. Note: both data frame and tibble are data frames.\n\nWhether tibble: `tibble::is_tibble`. Note: only tibbles are tibbles. Vanilla data frames are not.\n\n## Tibles: Coercion\n\n-   To data frame: `as.data.frame()`\n-   To tibble: `tibble::as_tibble()`\n\n## Tibles: List Columns\n\nList-columns are allowed in data frames but you have to do a little extra work by either adding the list-column after creation or wrapping the list in `I()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf4 <- data.frame(x = 1:3)\ndf4$y <- list(1:2, 1:3, 1:4)\ndf4\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x          y\n#> 1 1       1, 2\n#> 2 2    1, 2, 3\n#> 3 3 1, 2, 3, 4\n```\n\n\n:::\n\n```{.r .cell-code}\ndf5 <- data.frame(\n  x = 1:3, \n  y = I(list(1:2, 1:3, 1:4))\n)\ndf5\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x          y\n#> 1 1       1, 2\n#> 2 2    1, 2, 3\n#> 3 3 1, 2, 3, 4\n```\n\n\n:::\n:::\n\n\n## Tibbles: Matrix and data frame columns\n\n- As long as the number of rows matches the data frame, it’s also possible to have a matrix or data frame as a column of a data frame.\n- same as list-columns, must either addi the list-column after creation or wrapping the list in `I()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndfm <- data.frame(\n  x = 1:3 * 10,\n  y = I(matrix(1:9, nrow = 3))\n)\n\ndfm$z <- data.frame(a = 3:1, b = letters[1:3], stringsAsFactors = FALSE)\n\nstr(dfm)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> 'data.frame':\t3 obs. of  3 variables:\n#>  $ x: num  10 20 30\n#>  $ y: 'AsIs' int [1:3, 1:3] 1 2 3 4 5 6 7 8 9\n#>  $ z:'data.frame':\t3 obs. of  2 variables:\n#>   ..$ a: int  3 2 1\n#>   ..$ b: chr  \"a\" \"b\" \"c\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndfm$y\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    4    7\n#> [2,]    2    5    8\n#> [3,]    3    6    9\n```\n\n\n:::\n\n```{.r .cell-code}\ndfm$z\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 3 a\n#> 2 2 b\n#> 3 1 c\n```\n\n\n:::\n:::\n\n\n\n## Exercises 1/4\n\n1. Can you have a data frame with zero rows? What about zero columns?\n\n<details><summary>Answer(s)</summary>\nYes, you can create these data frames easily; either during creation or via subsetting. Even both dimensions can be zero. Create a 0-row, 0-column, or an empty data frame directly:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndata.frame(a = integer(), b = logical())\n#> [1] a b\n#> <0 rows> (or 0-length row.names)\n\ndata.frame(row.names = 1:3)  # or data.frame()[1:3, ]\n#> data frame with 0 columns and 3 rows\n\ndata.frame()\n#> data frame with 0 columns and 0 rows\n```\n:::\n\n\nCreate similar data frames via subsetting the respective dimension with either 0, `NULL`, `FALSE` or a valid 0-length atomic (`logical(0)`, `character(0)`, `integer(0)`, `double(0)`). Negative integer sequences would also work. The following example uses a zero:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmtcars[0, ]\n#>  [1] mpg  cyl  disp hp   drat wt   qsec vs   am   gear carb\n#> <0 rows> (or 0-length row.names)\n\nmtcars[ , 0]  # or mtcars[0]\n#> data frame with 0 columns and 32 rows\n\nmtcars[0, 0]\n#> data frame with 0 columns and 0 rows\n```\n:::\n\n\n\n</details>\n\n## Exercises 2/4\n\n2. What happens if you attempt to set rownames that are not unique?\n\n<details><summary>Answer(s)</summary>\nMatrices can have duplicated row names, so this does not cause problems.\n\nData frames, however, require unique rownames and you get different results depending on how you attempt to set them. If you set them directly or via `row.names()`, you get an error:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndata.frame(row.names = c(\"x\", \"y\", \"y\"))\n#> Error in data.frame(row.names = c(\"x\", \"y\", \"y\")): duplicate row.names: y\n\ndf <- data.frame(x = 1:3)\nrow.names(df) <- c(\"x\", \"y\", \"y\")\n#> Warning: non-unique value when setting 'row.names': 'y'\n#> Error in `.rowNamesDF<-`(x, value = value): duplicate 'row.names' are not allowed\n```\n:::\n\n\nIf you use subsetting, `[` automatically deduplicates:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nrow.names(df) <- c(\"x\", \"y\", \"z\")\ndf[c(1, 1, 1), , drop = FALSE]\n#>     x\n#> x   1\n#> x.1 1\n#> x.2 1\n```\n:::\n\n\n</details>\n\n## Exercises 3/4\n\n3. If `df` is a data frame, what can you say about `t(df)`, and `t(t(df))`? Perform some experiments, making sure to try different column types.\n\n<details><summary>Answer(s)</summary>\nBoth of `t(df)` and `t(t(df))` will return matrices:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(x = 1:3, y = letters[1:3])\nis.matrix(df)\n#> [1] FALSE\nis.matrix(t(df))\n#> [1] TRUE\nis.matrix(t(t(df)))\n#> [1] TRUE\n```\n:::\n\n\nThe dimensions will respect the typical transposition rules:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndim(df)\n#> [1] 3 2\ndim(t(df))\n#> [1] 2 3\ndim(t(t(df)))\n#> [1] 3 2\n```\n:::\n\n\nBecause the output is a matrix, every column is coerced to the same type. (It is implemented within `t.data.frame()` via `as.matrix()` which is described below).\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf\n#>   x y\n#> 1 1 a\n#> 2 2 b\n#> 3 3 c\nt(df)\n#>   [,1] [,2] [,3]\n#> x \"1\"  \"2\"  \"3\" \n#> y \"a\"  \"b\"  \"c\"\n```\n:::\n\n\n</details>\n\n## Exercises 4/4\n\n4. What does `as.matrix()` do when applied to a data frame with columns of different types? How does it differ from `data.matrix()`?\n\n<details><summary>Answer(s)</summary>\nThe type of the result of as.matrix depends on the types of the input columns (see `?as.matrix`):\n\n> The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g. all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.\n\nOn the other hand, `data.matrix` will always return a numeric matrix (see `?data.matrix()`).\n\n> Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. […] Character columns are first converted to factors and then to integers.\n\nWe can illustrate and compare the mechanics of these functions using a concrete example. `as.matrix()` makes it possible to retrieve most of the original information from the data frame but leaves us with characters. To retrieve all information from `data.matrix()`’s output, we would need a lookup table for each column.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf_coltypes <- data.frame(\n  a = c(\"a\", \"b\"),\n  b = c(TRUE, FALSE),\n  c = c(1L, 0L),\n  d = c(1.5, 2),\n  e = factor(c(\"f1\", \"f2\"))\n)\n\nas.matrix(df_coltypes)\n#>      a   b       c   d     e   \n#> [1,] \"a\" \"TRUE\"  \"1\" \"1.5\" \"f1\"\n#> [2,] \"b\" \"FALSE\" \"0\" \"2.0\" \"f2\"\ndata.matrix(df_coltypes)\n#>      a b c   d e\n#> [1,] 1 1 1 1.5 1\n#> [2,] 2 0 0 2.0 2\n```\n:::\n\n\n</details>\n\n\n## `NULL`\n\nSpecial type of object that:\n\n-   Length 0\n-   Cannot have attributes\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntypeof(NULL)\n#> [1] \"NULL\"\n\nlength(NULL)\n#> [1] 0\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- NULL\nattr(x, \"y\") <- 1\n```\n\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in attr(x, \"y\") <- 1: attempt to set an attribute on NULL\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nis.null(NULL)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE\n```\n\n\n:::\n:::\n\n\n\n## Digestif\n\nLet is use some of this chapter's skills on the `penguins` data.\n\n## Attributes\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr(penguins_raw)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [344 × 17] (S3: tbl_df/tbl/data.frame)\n#>  $ studyName          : chr [1:344] \"PAL0708\" \"PAL0708\" \"PAL0708\" \"PAL0708\" ...\n#>  $ Sample Number      : num [1:344] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ Species            : chr [1:344] \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" ...\n#>  $ Region             : chr [1:344] \"Anvers\" \"Anvers\" \"Anvers\" \"Anvers\" ...\n#>  $ Island             : chr [1:344] \"Torgersen\" \"Torgersen\" \"Torgersen\" \"Torgersen\" ...\n#>  $ Stage              : chr [1:344] \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" ...\n#>  $ Individual ID      : chr [1:344] \"N1A1\" \"N1A2\" \"N2A1\" \"N2A2\" ...\n#>  $ Clutch Completion  : chr [1:344] \"Yes\" \"Yes\" \"Yes\" \"Yes\" ...\n#>  $ Date Egg           : Date[1:344], format: \"2007-11-11\" \"2007-11-11\" ...\n#>  $ Culmen Length (mm) : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...\n#>  $ Culmen Depth (mm)  : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...\n#>  $ Flipper Length (mm): num [1:344] 181 186 195 NA 193 190 181 195 193 190 ...\n#>  $ Body Mass (g)      : num [1:344] 3750 3800 3250 NA 3450 ...\n#>  $ Sex                : chr [1:344] \"MALE\" \"FEMALE\" \"FEMALE\" NA ...\n#>  $ Delta 15 N (o/oo)  : num [1:344] NA 8.95 8.37 NA 8.77 ...\n#>  $ Delta 13 C (o/oo)  : num [1:344] NA -24.7 -25.3 NA -25.3 ...\n#>  $ Comments           : chr [1:344] \"Not enough blood for isotopes.\" NA NA \"Adult not sampled.\" ...\n#>  - attr(*, \"spec\")=List of 3\n#>   ..$ cols   :List of 17\n#>   .. ..$ studyName          : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Sample Number      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Species            : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Region             : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Island             : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Stage              : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Individual ID      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Clutch Completion  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Date Egg           :List of 1\n#>   .. .. ..$ format: chr \"\"\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_date\" \"collector\"\n#>   .. ..$ Culmen Length (mm) : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Culmen Depth (mm)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Flipper Length (mm): list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Body Mass (g)      : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Sex                : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   .. ..$ Delta 15 N (o/oo)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Delta 13 C (o/oo)  : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_double\" \"collector\"\n#>   .. ..$ Comments           : list()\n#>   .. .. ..- attr(*, \"class\")= chr [1:2] \"collector_character\" \"collector\"\n#>   ..$ default: list()\n#>   .. ..- attr(*, \"class\")= chr [1:2] \"collector_guess\" \"collector\"\n#>   ..$ skip   : num 1\n#>   ..- attr(*, \"class\")= chr \"col_spec\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nstr(penguins_raw, give.attr = FALSE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> tibble [344 × 17] (S3: tbl_df/tbl/data.frame)\n#>  $ studyName          : chr [1:344] \"PAL0708\" \"PAL0708\" \"PAL0708\" \"PAL0708\" ...\n#>  $ Sample Number      : num [1:344] 1 2 3 4 5 6 7 8 9 10 ...\n#>  $ Species            : chr [1:344] \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" \"Adelie Penguin (Pygoscelis adeliae)\" ...\n#>  $ Region             : chr [1:344] \"Anvers\" \"Anvers\" \"Anvers\" \"Anvers\" ...\n#>  $ Island             : chr [1:344] \"Torgersen\" \"Torgersen\" \"Torgersen\" \"Torgersen\" ...\n#>  $ Stage              : chr [1:344] \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" \"Adult, 1 Egg Stage\" ...\n#>  $ Individual ID      : chr [1:344] \"N1A1\" \"N1A2\" \"N2A1\" \"N2A2\" ...\n#>  $ Clutch Completion  : chr [1:344] \"Yes\" \"Yes\" \"Yes\" \"Yes\" ...\n#>  $ Date Egg           : Date[1:344], format: \"2007-11-11\" \"2007-11-11\" ...\n#>  $ Culmen Length (mm) : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...\n#>  $ Culmen Depth (mm)  : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...\n#>  $ Flipper Length (mm): num [1:344] 181 186 195 NA 193 190 181 195 193 190 ...\n#>  $ Body Mass (g)      : num [1:344] 3750 3800 3250 NA 3450 ...\n#>  $ Sex                : chr [1:344] \"MALE\" \"FEMALE\" \"FEMALE\" NA ...\n#>  $ Delta 15 N (o/oo)  : num [1:344] NA 8.95 8.37 NA 8.77 ...\n#>  $ Delta 13 C (o/oo)  : num [1:344] NA -24.7 -25.3 NA -25.3 ...\n#>  $ Comments           : chr [1:344] \"Not enough blood for isotopes.\" NA NA \"Adult not sampled.\" ...\n```\n\n\n:::\n:::\n\n\n## Data Frames vs Tibbles\n\n\n::: {.cell}\n\n```{.r .cell-code}\npenguins_df <- data.frame(penguins)\npenguins_tb <- penguins #i.e. penguins was already a tibble\n```\n:::\n\n\n## Printing\n\n* Tip: print out these results in RStudio under different editor themes\n\n\n::: {.cell}\n\n```{.r .cell-code}\nprint(penguins_df) #don't run this\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nhead(penguins_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   species    island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g\n#> 1  Adelie Torgersen           39.1          18.7               181        3750\n#> 2  Adelie Torgersen           39.5          17.4               186        3800\n#> 3  Adelie Torgersen           40.3          18.0               195        3250\n#> 4  Adelie Torgersen             NA            NA                NA          NA\n#> 5  Adelie Torgersen           36.7          19.3               193        3450\n#> 6  Adelie Torgersen           39.3          20.6               190        3650\n#>      sex year\n#> 1   male 2007\n#> 2 female 2007\n#> 3 female 2007\n#> 4   <NA> 2007\n#> 5 female 2007\n#> 6   male 2007\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\npenguins_tb\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 344 × 8\n#>    species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g\n#>    <fct>   <fct>              <dbl>         <dbl>             <int>       <int>\n#>  1 Adelie  Torgersen           39.1          18.7               181        3750\n#>  2 Adelie  Torgersen           39.5          17.4               186        3800\n#>  3 Adelie  Torgersen           40.3          18                 195        3250\n#>  4 Adelie  Torgersen           NA            NA                  NA          NA\n#>  5 Adelie  Torgersen           36.7          19.3               193        3450\n#>  6 Adelie  Torgersen           39.3          20.6               190        3650\n#>  7 Adelie  Torgersen           38.9          17.8               181        3625\n#>  8 Adelie  Torgersen           39.2          19.6               195        4675\n#>  9 Adelie  Torgersen           34.1          18.1               193        3475\n#> 10 Adelie  Torgersen           42            20.2               190        4250\n#> # ℹ 334 more rows\n#> # ℹ 2 more variables: sex <fct>, year <int>\n```\n\n\n:::\n:::\n\n\n## Atomic Vectors\n\n\n::: {.cell}\n\n```{.r .cell-code}\nspecies_vector_df <- penguins_df |> select(species)\nspecies_unlist_df <- penguins_df |> select(species) |> unlist()\nspecies_pull_df   <- penguins_df |> select(species) |> pull()\n\nspecies_vector_tb <- penguins_tb |> select(species)\nspecies_unlist_tb <- penguins_tb |> select(species) |> unlist()\nspecies_pull_tb   <- penguins_tb |> select(species) |> pull()\n```\n:::\n\n\n<details>\n<summary>`typeof()` and `class()`</summary>\n\n::: {.cell}\n\n```{.r .cell-code}\ntypeof(species_vector_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_vector_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"data.frame\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_unlist_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_unlist_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_pull_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_pull_df)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_vector_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_vector_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"tbl_df\"     \"tbl\"        \"data.frame\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_unlist_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_unlist_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n\n```{.r .cell-code}\ntypeof(species_pull_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(species_pull_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"factor\"\n```\n\n\n:::\n:::\n\n\n</details>\n\n## Column Names\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolnames(penguins_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"species\"           \"island\"            \"bill_length_mm\"   \n#> [4] \"bill_depth_mm\"     \"flipper_length_mm\" \"body_mass_g\"      \n#> [7] \"sex\"               \"year\"\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nnames(penguins_tb) == colnames(penguins_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nnames(penguins_df) == names(penguins_tb)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE\n```\n\n\n:::\n:::\n\n\n## What if we only invoke a partial name of a column of a tibble?\n\n\n::: {.cell}\n\n```{.r .cell-code}\npenguins_tb$y \n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n\n* What if we only invoke a partial name of a column of a data frame?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nhead(penguins_df$y) #instead of `year`\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2007 2007 2007 2007 2007 2007\n```\n\n\n:::\n:::\n\n\n* Is this evaluation in alphabetical order or column order?\n\n\n::: {.cell}\n\n```{.r .cell-code}\npenguins_df_se_sp <- penguins_df |> select(sex, species)\npenguins_df_sp_se <- penguins_df |> select(species, sex)\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nhead(penguins_df_se_sp$s)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\nhead(penguins_df_sp_se$s)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n\n## Chapter Quiz 1/5\n\n1. What are the four common types of atomic vectors? What are the two rare types?\n\n<details><summary>Answer(s)</summary>\nThe four common types of atomic vector are logical, integer, double and character. The two rarer types are complex and raw.\n</details>\n\n## Chapter Quiz 2/5\n\n2. What are attributes? How do you get them and set them?\n\n<details><summary>Answer(s)</summary>\nAttributes allow you to associate arbitrary additional metadata to any object. You can get and set individual attributes with `attr(x, \"y\")` and `attr(x, \"y\") <- value`; or you can get and set all attributes at once with `attributes()`.\n</details>\n\n## Chapter Quiz 3/5\n\n3. How is a list different from an atomic vector? How is a matrix different from a data frame?\n\n<details><summary>Answer(s)</summary>\nThe elements of a list can be any type (even a list); the elements of an atomic vector are all of the same type. Similarly, every element of a matrix must be the same type; in a data frame, different columns can have different types.\n</details>\n\n## Chapter Quiz 4/5\n\n4. Can you have a list that is a matrix? Can a data frame have a column that is a matrix?\n\n<details><summary>Answer(s)</summary>\nYou can make a list-array by assigning dimensions to a list. You can make a matrix a column of a data frame with `df$x <- matrix()`, or by using `I()` when creating a new data frame `data.frame(x = I(matrix()))`.\n</details>\n\n## Chapter Quiz 5/5\n\n5. How do tibbles behave differently from data frames?\n\n<details><summary>Answer(s)</summary>\nTibbles have an enhanced print method, never coerce strings to factors, and provide stricter subsetting methods.\n</details>\n",
     "supporting": [],
     "filters": [
       "rmarkdown/pagebreak.lua"
diff --git a/_freeze/slides/04/execute-results/html.json b/_freeze/slides/04/execute-results/html.json
@@ -1,9 +1,11 @@
 {
-  "hash": "b5ac0f8cfdf1cfbdbe9f5056fb42d7ee",
+  "hash": "d7ca12190e1edb91ae8731c452ae68f5",
   "result": {
     "engine": "knitr",
-    "markdown": "---\nengine: knitr\ntitle: Subsetting\n---\n\n## Learning objectives:\n\n- Select multiple elements from a vector with `[`\n- Learn about the 3 subsetting operators: `[[`, `[`, and `$`\n- Learn how subsetting works with different vector types\n- Learn how subsetting can be combined with assignment\n\n# Selecting multiple elements\n\n## 1. Positive integers return elements at specified positions\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(1.1, 2.2, 3.3, 4.4) # decimal = original position\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(4, 1)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 4.4 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1, 1, 1)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 1.1 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1.9999)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1\n```\n\n\n:::\n:::\n\n\nReals *truncate* to integers.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(1.0001, 1.9999)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 1.1\n```\n\n\n:::\n:::\n\n\n## 2. Negative integers remove specified elements\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[-c(1, 3)] # same as x[c(-1, -3)] or x[c(2, 4)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 4.4\n```\n\n\n:::\n:::\n\n\n## 2b. Mixing negative and positive integers throws an error\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(-1, 3)]\n```\n\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in x[c(-1, 3)]: only 0's may be mixed with negative subscripts\n```\n\n\n:::\n:::\n\n\n## 2c. Zeros ignored with other ints \n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(-1, 0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(-1, 0, 0, 0, 0, 0 ,0 ,0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1, 0, 2, 0, 3, 0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3\n```\n\n\n:::\n:::\n\n\n\n## 3. Logical vectors select specified elements\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(TRUE, TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[x < 3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2\n```\n\n\n:::\n\n```{.r .cell-code}\ncond <- x > 2.5\nx[cond]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3.3 4.4\n```\n\n\n:::\n:::\n\n\n## 3b. Shorter element are recycled to higher length\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[FALSE]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> numeric(0)\n```\n\n\n:::\n\n```{.r .cell-code}\nx[TRUE]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(FALSE, TRUE)] # equivalent to: x[c(FALSE, TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 4.4\n```\n\n\n:::\n:::\n\n\n- Easy to understand if x or y is 1, best to avoid other lengths\n\n## 3c. NA index returns NA\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(NA, TRUE, NA, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1]  NA 2.2  NA 4.4\n```\n\n\n:::\n:::\n\n## 3d. Extra TRUE index returns NA\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4  NA  NA\n```\n\n\n:::\n\n```{.r .cell-code}\nx[1:5]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4  NA\n```\n\n\n:::\n:::\n\n\n## 4. Indexing with nothing returns original vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n:::\n\n\n## 5. Indexing with just 0 returns 0-length vector (with class)\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[0]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> numeric(0)\n```\n\n\n:::\n\n```{.r .cell-code}\nletters[0]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> character(0)\n```\n\n\n:::\n:::\n\n\n## 6. Indexing with character vector returns element of named vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\n(y <- setNames(x, letters[1:4]))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a   b   c   d \n#> 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\ny[c(\"d\", \"b\", \"a\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   d   b   a \n#> 4.4 2.2 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\ny[c(\"a\", \"a\", \"a\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a   a   a \n#> 1.1 1.1 1.1\n```\n\n\n:::\n:::\n\n\n## 6b. Names must be exact for `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nz <- c(abc = 1, def = 2)\nz\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> abc def \n#>   1   2\n```\n\n\n:::\n\n```{.r .cell-code}\nz[c(\"a\", \"d\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> <NA> <NA> \n#>   NA   NA\n```\n\n\n:::\n:::\n\n\n## Subsetting a list with `[` returns a list\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmy_list <- list(a = c(T, F), b = letters[5:15], c = 100:108)\nmy_list\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n\n```{.r .cell-code}\nmy_list[c(\"a\", \"b\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n```\n\n\n:::\n:::\n\n\n## Lists use same rules for `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmy_list[2:3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n\n```{.r .cell-code}\nmy_list[c(TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays take multidimensional indices\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- matrix(1:9, nrow = 3)\na\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    4    7\n#> [2,]    2    5    8\n#> [3,]    3    6    9\n```\n\n\n:::\n\n```{.r .cell-code}\na[1:2, 2:3] # rows, columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2]\n#> [1,]    4    7\n#> [2,]    5    8\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays can accept character, logical, etc\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolnames(a) <- c(\"A\", \"B\", \"C\")\na[c(TRUE, TRUE, FALSE), c(\"B\", \"A\")] # a[1:2, 2:1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      B A\n#> [1,] 4 1\n#> [2,] 5 2\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays are also vectors\n\n\n::: {.cell}\n\n```{.r .cell-code}\nvals <- outer(1:5, 1:5, FUN = \"paste\", sep = \",\") # All chr combos of 1:5\nvals\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1]  [,2]  [,3]  [,4]  [,5] \n#> [1,] \"1,1\" \"1,2\" \"1,3\" \"1,4\" \"1,5\"\n#> [2,] \"2,1\" \"2,2\" \"2,3\" \"2,4\" \"2,5\"\n#> [3,] \"3,1\" \"3,2\" \"3,3\" \"3,4\" \"3,5\"\n#> [4,] \"4,1\" \"4,2\" \"4,3\" \"4,4\" \"4,5\"\n#> [5,] \"5,1\" \"5,2\" \"5,3\" \"5,4\" \"5,5\"\n```\n\n\n:::\n\n```{.r .cell-code}\nvals[c(4, 15)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"4,1\" \"5,3\"\n```\n\n\n:::\n\n```{.r .cell-code}\na[a > 5]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 6 7 8 9\n```\n\n\n:::\n:::\n\n\n## Data frames subset list-like with single index\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])\ndf[1:2]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x y\n#> 1 1 3\n#> 2 2 2\n#> 3 3 1\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[c(\"x\", \"z\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n#> 3 3 c\n```\n\n\n:::\n:::\n\n\n## Data frames subset matrix-like with multiple indices\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf[1:2, c(\"x\", \"z\")] # rows, columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[df$x == 2, ] # matching rows, all columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x y z\n#> 2 2 2 b\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, c(\"x\", \"z\")] # equivalent to no ,\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n#> 3 3 c\n```\n\n\n:::\n:::\n\n\n## Subsetting a tibble with `[` returns a tibble\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntbl <- tibble::as_tibble(df)\ndf[, 1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, 1, drop = FALSE] # Prevent errors\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x\n#> 1 1\n#> 2 2\n#> 3 3\n```\n\n\n:::\n\n```{.r .cell-code}\ntbl[, 1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 1\n#>       x\n#>   <int>\n#> 1     1\n#> 2     2\n#> 3     3\n```\n\n\n:::\n:::\n\n\n# Selecting a single element\n\n## `[[` selects a single element\n\n:::: {.columns}\n\n::: {.column}\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(1:3, \"a\", 4:6)\nx[1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(x[1])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[1]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(x[[1]])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[1]][[1]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n:::\n\n::: {.column}\n\n\n:::\n\n::::\n\n## `$` is shorthand for `[[..., exact = FALSE]]`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(abc = 1)\nx$abc\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\nx$a\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[\"a\"]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[\"a\", exact = FALSE]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\noptions(warnPartialMatchDollar = TRUE)\nx$a\n```\n\n::: {.cell-output .cell-output-stderr}\n\n```\n#> Warning in x$a: partial match of 'a' to 'abc'\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Behavior for missing-ish indices is inconsistent\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- c(a = 1L, b = 2L)\nlst <- list(a = 1:2)\n\n# Errors:\n# a[[NULL]]\n# lst[[NULL]]\n# a[[5]]\n# lst[[5]]\n# a[[\"c\"]]\n# a[[NA]]\n\nlst[[\"c\"]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\nlst[[NA]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n## `purrr::pluck()` and `purrr::chuck()` provide consistent wrappers\n\n- `purrr::pluck()` always returns `NULL` or `.default` for (non-`NULL`) missing\n- `purrr::chuck()` always throws error\n\n\n::: {.cell}\n\n```{.r .cell-code}\npurrr::pluck(a, 5)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(a, \"c\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(lst, 5)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(lst, \"c\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n## S4 has two additional subsetting operators\n\n- `@` equivalent to `$` (but error if bad)\n- `slot()` equivalent to `[[`\n\nMore in Chapter 15\n\n# Subsetting and assignment\n\n## Can assign to position with `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:5\nx[1:2] <- c(101, 102)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 101 102   3   4   5\n```\n\n\n:::\n\n```{.r .cell-code}\nx[1:3] <- 1:2\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 1 4 5\n```\n\n\n:::\n:::\n\n\n## Remove list component with `NULL`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(a = 1, b = 2)\nx[[\"b\"]] <- NULL\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Use `list(NULL)` to add `NULL`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(a = 1, b = 2)\nx[[\"b\"]] <- list(NULL)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1] 1\n#> \n#> $b\n#> $b[[1]]\n#> NULL\n```\n\n\n:::\n:::\n\n\n## Subset with nothing to retain shape\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(a = 1:3, b = 1:3)\ndf[] <- \"a\"\ndf\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 a a\n#> 2 a a\n#> 3 a a\n```\n\n\n:::\n\n```{.r .cell-code}\ndf <- \"a\"\ndf\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"a\"\n```\n\n\n:::\n:::\n\n\n# Applications\n\n## Use a lookup vector and recycling rules to translate values\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(\"b\", \"g\", \"x\", \"g\", \"g\", \"b\")\nlookup <- c(b = \"blue\", g = \"green\", x = NA)\nlookup[x]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       b       g       x       g       g       b \n#>  \"blue\" \"green\"      NA \"green\" \"green\"  \"blue\"\n```\n\n\n:::\n\n```{.r .cell-code}\nunname(lookup[x])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"blue\"  \"green\" NA      \"green\" \"green\" \"blue\"\n```\n\n\n:::\n:::\n\n\n## Use a lookup table to generate rows of data\n\n\n::: {.cell}\n\n```{.r .cell-code}\ninfo <- data.frame(\n  code = c(\"b\", \"g\", \"x\"),\n  color = c(\"blue\", \"green\", NA),\n  other_thing = 3:1\n)\nmatch(x, info$code) # Indices of info$code in x\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 2 2 1\n```\n\n\n:::\n\n```{.r .cell-code}\ninfo[match(x, info$code), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     code color other_thing\n#> 1      b  blue           3\n#> 2      g green           2\n#> 3      x  <NA>           1\n#> 2.1    g green           2\n#> 2.2    g green           2\n#> 1.1    b  blue           3\n```\n\n\n:::\n:::\n\n\n## Sort with `order()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(\"b\", \"c\", \"a\")\norder(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3 1 2\n```\n\n\n:::\n\n```{.r .cell-code}\nx[order(x)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"a\" \"b\" \"c\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndf <- data.frame(b = 3:1, a = 1:3)\ndf[order(df$b), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   b a\n#> 3 1 3\n#> 2 2 2\n#> 1 3 1\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, order(names(df))]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 1 3\n#> 2 2 2\n#> 3 3 1\n```\n\n\n:::\n:::\n\n\n## Expand counts\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))\nrep(1:nrow(df), df$n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 1 1 2 2 2 2 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[rep(1:nrow(df), df$n), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     x  y n\n#> 1   2  9 3\n#> 1.1 2  9 3\n#> 1.2 2  9 3\n#> 2   4 11 5\n#> 2.1 4 11 5\n#> 2.2 4 11 5\n#> 2.3 4 11 5\n#> 2.4 4 11 5\n#> 3   1  6 1\n```\n\n\n:::\n:::\n\n\n## Ran out of time to make slides for\n\nIdeally a future cohort should expand these:\n\n- Remove df columns with `setdiff()`\n- Logically subset rows `df[df$col > 5, ]`\n- The next slide about `which()`\n\n## Boolean algebra versus sets (logical and integer)\n\n- `which()` gives the indices of a Boolean vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\n(x1 <- 1:10 %% 2 == 0) # 1-10 divisible by 2\n#  [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE\n(x2 <- which(x1))\n# [1]  2  4  6  8 10\n(y1 <- 1:10 %% 5 == 0) # 1-10 divisible by 5\n#  [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE\n(y2 <- which(y1))\n# [1]  5 10\nx1 & y1\n# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE\n```\n:::\n\n",
-    "supporting": [],
+    "markdown": "---\nengine: knitr\ntitle: Subsetting\n---\n\n## Learning objectives:\n\n- Select multiple elements from a vector with `[`\n- Select single elements from a vector with `[[` and `$`\n- Assign to subsets of vectors\n- Use subsetting to expand data\n\n# Selecting multiple elements\n\n## 1. Positive integers return elements at specified positions\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(1.1, 2.2, 3.3, 4.4) # decimal = original position\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(4, 1)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 4.4 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1, 1, 1)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 1.1 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1.9999)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1\n```\n\n\n:::\n:::\n\n\nReals *truncate* to integers.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(1.0001, 1.9999)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 1.1\n```\n\n\n:::\n:::\n\n\n## 2. Negative integers remove specified elements\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[-c(1, 3)] # same as x[c(-1, -3)] or x[c(2, 4)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 4.4\n```\n\n\n:::\n:::\n\n\n## 2b. Mixing negative and positive integers throws an error\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(-1, 3)]\n```\n\n::: {.cell-output .cell-output-error}\n\n```\n#> Error in x[c(-1, 3)]: only 0's may be mixed with negative subscripts\n```\n\n\n:::\n:::\n\n\n## 2c. Zeros ignored with other ints \n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(-1, 0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(-1, 0, 0, 0, 0, 0 ,0 ,0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(1, 0, 2, 0, 3, 0)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3\n```\n\n\n:::\n:::\n\n\n\n## 3. Logical vectors select specified elements\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(TRUE, TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[x < 3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2\n```\n\n\n:::\n\n```{.r .cell-code}\ncond <- x > 2.5\nx[cond]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3.3 4.4\n```\n\n\n:::\n:::\n\n\n## 3b. Shorter element are recycled to higher length\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[FALSE]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> numeric(0)\n```\n\n\n:::\n\n```{.r .cell-code}\nx[TRUE]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\nx[c(FALSE, TRUE)] # equivalent to: x[c(FALSE, TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 4.4\n```\n\n\n:::\n:::\n\n\n- Easy to understand if x or y is 1, best to avoid other lengths\n\n## 3c. NA index returns NA\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(NA, TRUE, NA, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1]  NA 2.2  NA 4.4\n```\n\n\n:::\n:::\n\n## 3d. Extra TRUE index returns NA\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 2.2 3.3 4.4  NA  NA\n```\n\n\n:::\n\n```{.r .cell-code}\nx[1:5]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4  NA\n```\n\n\n:::\n:::\n\n\n## 4. Indexing with nothing returns original vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1.1 2.2 3.3 4.4\n```\n\n\n:::\n:::\n\n\n## 5. Indexing with just 0 returns 0-length vector (with class)\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx[0]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> numeric(0)\n```\n\n\n:::\n\n```{.r .cell-code}\nletters[0]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> character(0)\n```\n\n\n:::\n:::\n\n\n## 6. Indexing with character vector returns element of named vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\n(y <- setNames(x, letters[1:4]))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a   b   c   d \n#> 1.1 2.2 3.3 4.4\n```\n\n\n:::\n\n```{.r .cell-code}\ny[c(\"d\", \"b\", \"a\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   d   b   a \n#> 4.4 2.2 1.1\n```\n\n\n:::\n\n```{.r .cell-code}\ny[c(\"a\", \"a\", \"a\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a   a   a \n#> 1.1 1.1 1.1\n```\n\n\n:::\n:::\n\n\n## 6b. Names must be exact for `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nz <- c(abc = 1, def = 2)\nz\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> abc def \n#>   1   2\n```\n\n\n:::\n\n```{.r .cell-code}\nz[c(\"a\", \"d\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> <NA> <NA> \n#>   NA   NA\n```\n\n\n:::\n:::\n\n\n## Subsetting a list with `[` returns a list\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmy_list <- list(a = c(T, F), b = letters[5:15], c = 100:108)\nmy_list\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n\n```{.r .cell-code}\nmy_list[c(\"a\", \"b\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n```\n\n\n:::\n:::\n\n\n## Lists use same rules for `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmy_list[2:3]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $b\n#>  [1] \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\"\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n\n```{.r .cell-code}\nmy_list[c(TRUE, FALSE, TRUE)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1]  TRUE FALSE\n#> \n#> $c\n#> [1] 100 101 102 103 104 105 106 107 108\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays take multidimensional indices\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- matrix(1:9, nrow = 3)\na\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2] [,3]\n#> [1,]    1    4    7\n#> [2,]    2    5    8\n#> [3,]    3    6    9\n```\n\n\n:::\n\n```{.r .cell-code}\na[1:2, 2:3] # rows, columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1] [,2]\n#> [1,]    4    7\n#> [2,]    5    8\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays can accept character, logical, etc\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolnames(a) <- c(\"A\", \"B\", \"C\")\na[c(TRUE, TRUE, FALSE), c(\"B\", \"A\")] # a[1:2, 2:1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      B A\n#> [1,] 4 1\n#> [2,] 5 2\n```\n\n\n:::\n:::\n\n\n## Matrices & arrays are also vectors\n\n\n::: {.cell}\n\n```{.r .cell-code}\nvals <- outer(1:5, 1:5, FUN = \"paste\", sep = \",\") # All chr combos of 1:5\nvals\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>      [,1]  [,2]  [,3]  [,4]  [,5] \n#> [1,] \"1,1\" \"1,2\" \"1,3\" \"1,4\" \"1,5\"\n#> [2,] \"2,1\" \"2,2\" \"2,3\" \"2,4\" \"2,5\"\n#> [3,] \"3,1\" \"3,2\" \"3,3\" \"3,4\" \"3,5\"\n#> [4,] \"4,1\" \"4,2\" \"4,3\" \"4,4\" \"4,5\"\n#> [5,] \"5,1\" \"5,2\" \"5,3\" \"5,4\" \"5,5\"\n```\n\n\n:::\n\n```{.r .cell-code}\nvals[c(4, 15)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"4,1\" \"5,3\"\n```\n\n\n:::\n\n```{.r .cell-code}\na[a > 5]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 6 7 8 9\n```\n\n\n:::\n:::\n\n\n## Data frames subset list-like with single index\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])\ndf[1:2]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x y\n#> 1 1 3\n#> 2 2 2\n#> 3 3 1\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[c(\"x\", \"z\")]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n#> 3 3 c\n```\n\n\n:::\n:::\n\n\n## Data frames subset matrix-like with multiple indices\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf[1:2, c(\"x\", \"z\")] # rows, columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[df$x == 2, ] # matching rows, all columns\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x y z\n#> 2 2 2 b\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, c(\"x\", \"z\")] # equivalent to no ,\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x z\n#> 1 1 a\n#> 2 2 b\n#> 3 3 c\n```\n\n\n:::\n:::\n\n\n## Subsetting a tibble with `[` returns a tibble\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntbl <- tibble::as_tibble(df)\ndf[, 1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, 1, drop = FALSE] # Prevent errors\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   x\n#> 1 1\n#> 2 2\n#> 3 3\n```\n\n\n:::\n\n```{.r .cell-code}\ntbl[, 1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> # A tibble: 3 × 1\n#>       x\n#>   <int>\n#> 1     1\n#> 2     2\n#> 3     3\n```\n\n\n:::\n:::\n\n\n# Selecting a single element\n\n## `[[` selects a single element\n\n:::: columns\n\n::: column\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(1:3, \"a\", 4:6)\nx[1]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [[1]]\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(x[1])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"list\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[1]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\nclass(x[[1]])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"integer\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[1]][[1]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n:::\n\n::: column\n\n\n:::\n\n::::\n\n## `$` is shorthand for `[[..., exact = FALSE]]`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(abc = 1)\nx$abc\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\nx$a\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[\"a\"]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\nx[[\"a\", exact = FALSE]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n\n```{.r .cell-code}\noptions(warnPartialMatchDollar = TRUE)\nx$a\n```\n\n::: {.cell-output .cell-output-stderr}\n\n```\n#> Warning in x$a: partial match of 'a' to 'abc'\n```\n\n\n:::\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Behavior for missing-ish indices is inconsistent\n\n\n::: {.cell}\n\n```{.r .cell-code}\na <- c(a = 1L, b = 2L)\nlst <- list(a = 1:2)\n\n# Errors:\n# a[[NULL]]\n# lst[[NULL]]\n# a[[5]]\n# lst[[5]]\n# a[[\"c\"]]\n# a[[NA]]\n\nlst[[\"c\"]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\nlst[[NA]]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n## `purrr::pluck()` and `purrr::chuck()` provide consistent wrappers\n\n- `purrr::pluck()` always returns `NULL` or `.default` for (non-`NULL`) missing\n- `purrr::chuck()` always throws error\n\n\n::: {.cell}\n\n```{.r .cell-code}\npurrr::pluck(a, 5)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(a, \"c\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(lst, 5)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n\n```{.r .cell-code}\npurrr::pluck(lst, \"c\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> NULL\n```\n\n\n:::\n:::\n\n\n## S4 has two additional subsetting operators\n\n- `@` equivalent to `$` (but error if bad)\n- `slot()` equivalent to `[[`\n\nMore in Chapter 15\n\n# Subsetting and assignment\n\n## Can assign to position with `[`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- 1:5\nx[1:2] <- c(101, 102)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 101 102   3   4   5\n```\n\n\n:::\n\n```{.r .cell-code}\nx[1:3] <- 1:2\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 1 4 5\n```\n\n\n:::\n:::\n\n\n## Remove list component with `NULL`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(a = 1, b = 2)\nx[[\"b\"]] <- NULL\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1] 1\n```\n\n\n:::\n:::\n\n\n## Use `list(NULL)` to add `NULL`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- list(a = 1, b = 2)\nx[[\"b\"]] <- list(NULL)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> $a\n#> [1] 1\n#> \n#> $b\n#> $b[[1]]\n#> NULL\n```\n\n\n:::\n:::\n\n\n## Subset with nothing to retain shape\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(a = 1:3, b = 1:3)\ndf[] <- \"a\"\ndf\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 a a\n#> 2 a a\n#> 3 a a\n```\n\n\n:::\n\n```{.r .cell-code}\ndf <- \"a\"\ndf\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"a\"\n```\n\n\n:::\n:::\n\n\n# Applications\n\n## Use a lookup vector and recycling rules to translate values\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(\"b\", \"g\", \"x\", \"g\", \"g\", \"b\")\nlookup <- c(b = \"blue\", g = \"green\", x = NA)\nlookup[x]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>       b       g       x       g       g       b \n#>  \"blue\" \"green\"      NA \"green\" \"green\"  \"blue\"\n```\n\n\n:::\n\n```{.r .cell-code}\nunname(lookup[x])\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"blue\"  \"green\" NA      \"green\" \"green\" \"blue\"\n```\n\n\n:::\n:::\n\n\n## Use a lookup table to generate rows of data\n\n\n::: {.cell}\n\n```{.r .cell-code}\ninfo <- data.frame(\n  code = c(\"b\", \"g\", \"x\"),\n  color = c(\"blue\", \"green\", NA),\n  other_thing = 3:1\n)\nmatch(x, info$code) # Indices of info$code in x\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 2 3 2 2 1\n```\n\n\n:::\n\n```{.r .cell-code}\ninfo[match(x, info$code), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     code color other_thing\n#> 1      b  blue           3\n#> 2      g green           2\n#> 3      x  <NA>           1\n#> 2.1    g green           2\n#> 2.2    g green           2\n#> 1.1    b  blue           3\n```\n\n\n:::\n:::\n\n\n## Sort with `order()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(\"b\", \"c\", \"a\")\norder(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 3 1 2\n```\n\n\n:::\n\n```{.r .cell-code}\nx[order(x)]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] \"a\" \"b\" \"c\"\n```\n\n\n:::\n\n```{.r .cell-code}\ndf <- data.frame(b = 3:1, a = 1:3)\ndf[order(df$b), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   b a\n#> 3 1 3\n#> 2 2 2\n#> 1 3 1\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[, order(names(df))]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>   a b\n#> 1 1 3\n#> 2 2 2\n#> 3 3 1\n```\n\n\n:::\n:::\n\n\n## Expand counts\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndf <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))\nrep(1:nrow(df), df$n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#> [1] 1 1 1 2 2 2 2 2 3\n```\n\n\n:::\n\n```{.r .cell-code}\ndf[rep(1:nrow(df), df$n), ]\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n#>     x  y n\n#> 1   2  9 3\n#> 1.1 2  9 3\n#> 1.2 2  9 3\n#> 2   4 11 5\n#> 2.1 4 11 5\n#> 2.2 4 11 5\n#> 2.3 4 11 5\n#> 2.4 4 11 5\n#> 3   1  6 1\n```\n\n\n:::\n:::\n\n\n## Ran out of time to make slides for\n\nIdeally a future cohort should expand these:\n\n- Remove df columns with `setdiff()`\n- Logically subset rows `df[df$col > 5, ]`\n- The next slide about `which()`\n\n## Boolean algebra versus sets (logical and integer)\n\n- `which()` gives the indices of a Boolean vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\n(x1 <- 1:10 %% 2 == 0) # 1-10 divisible by 2\n#  [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE\n(x2 <- which(x1))\n# [1]  2  4  6  8 10\n(y1 <- 1:10 %% 5 == 0) # 1-10 divisible by 5\n#  [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE\n(y2 <- which(y1))\n# [1]  5 10\nx1 & y1\n# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE\n```\n:::\n\n",
+    "supporting": [
+      "04_files"
+    ],
     "filters": [
       "rmarkdown/pagebreak.lua"
     ],
diff --git a/slides/02.qmd b/slides/02.qmd
@@ -110,9 +110,9 @@ untracemem(y)
 
 ## A function's environment follows copy-on-modify rules
 
-:::: {.columns}
+:::: columns
 
-::: {.column}
+::: column
 ```{r}
 f <- function(a) {
   a
@@ -126,7 +126,7 @@ obj_addr(z) # No address change
 ```
 :::
 
-::: {.column}
+::: column
 
 :::
 
@@ -141,9 +141,9 @@ obj_addr(z) # No address change
 
 ## `ref()` shows the memory address of a list and its *elements*
 
-:::: {.columns}
+:::: columns
 
-::: {.column}
+::: column
 ```{r}
 l1 <- list(1, 2, 3)
 obj_addr(l1)
@@ -153,7 +153,7 @@ ref(l1, l2)
 ```
 :::
 
-::: {.column}
+::: column
 {width=50%}
 :::
 
@@ -183,9 +183,9 @@ ref(d1, d2)
 
 ## Characters are unique due to the global string pool
 
-:::: {.columns}
+:::: columns
 
-::: {.column}
+::: column
 ```{r}
 x <- 1:4
 ref(x)
@@ -198,7 +198,7 @@ ref(y, character = TRUE)
 ```
 :::
 
-::: {.column}
+::: column
 
 :::
 
diff --git a/slides/03.Rmd b/slides/03.Rmd
@@ -1,1951 +0,0 @@
----
-engine: knitr
-title: Vectors
----
-
-## Learning objectives:
-
--   Learn about different types of vectors and their attributes
--   Navigate through vector types and their value types
--   Venture into factors and date-time objects
--   Discuss the differences between data frames and tibbles
--   Do not get absorbed by the `NA` and `NULL` black hole
-
-```{r ch9_setup, message = FALSE, warning = FALSE}
-library("dplyr")
-library("gt")
-library("palmerpenguins")
-```
-
-
-<details>
-<summary>Session Info</summary>
-```{r}
-utils::sessionInfo()
-```
-</details>
-
-## Aperitif
-
-
-
-### Counting Penguins
-
-Consider this code to count the number of Gentoo penguins in the `penguins` data set. We see that there are 124 Gentoo penguins.
-
-```{r, eval = FALSE}
-sum("Gentoo" == penguins$species)
-# output: 124
-```
-
-### In
-
-One subtle error can arise in trying out `%in%` here instead.
-
-```{r, results = 'hide'}
-species_vector <- penguins |> select(species)
-print("Gentoo" %in% species_vector)
-# output: FALSE
-```
-
-
-
-### Fix: base R 
-
-```{r, results = 'hide'}
-species_unlist <- penguins |> select(species) |> unlist()
-print("Gentoo" %in% species_unlist)
-# output: TRUE
-```
-
-### Fix: dplyr
-
-```{r, results = 'hide'}
-species_pull <- penguins |> select(species) |> pull()
-print("Gentoo" %in% species_pull)
-# output: TRUE
-```
-
-### Motivation
-
-* What are the different types of vectors?
-* How does this affect accessing vectors?
-
-<details>
-<summary>Side Quest: Looking up the `%in%` operator</summary>
-If you want to look up the manual pages for the `%in%` operator with the `?`, use backticks:
-
-```{r, eval = FALSE}
-?`%in%`
-```
-
-and we find that `%in%` is a wrapper for the `match()` function.
-
-</details>
-
-
-## Types of Vectors
-
- 
-
-Two main types:
-
--   **Atomic**: Elements all the same type.
--   **List**: Elements are different Types.
-
-Closely related but not technically a vector:
-
--   **NULL**: Null elements. Often length zero.
-
-## Atomic Vectors
-
-### Types of atomic vectors
-
- 
-
--   **Logical**: True/False
--   **Integer**: Numeric (discrete, no decimals)
--   **Double**: Numeric (continuous, decimals)
--   **Character**: String
-
-### Vectors of Length One
-
-**Scalars** are vectors that consist of a single value.
-
-#### Logicals
-
-```{r vec_lgl}
-lgl1 <- TRUE
-lgl2 <- T #abbreviation for TRUE
-lgl3 <- FALSE
-lgl4 <- F #abbreviation for FALSE
-```
-
-#### Doubles
-
-```{r vec_dbl}
-# integer, decimal, scientific, or hexidecimal format
-dbl1 <- 1
-dbl2 <- 1.234 # decimal
-dbl3 <- 1.234e0 # scientific format
-dbl4 <- 0xcafe # hexidecimal format
-```
-
-#### Integers
-
-Integers must be followed by L and cannot have fractional values
-
-```{r vec_int}
-int1 <- 1L
-int2 <- 1234L
-int3 <- 1234e0L
-int4 <- 0xcafeL
-```
-
-<details>
-<summary>Pop Quiz: Why "L" for integers?</summary>
-Wickham notes that the use of `L` dates back to the **C** programming language and its "long int" type for memory allocation.
-</details>
-
-#### Strings
-
-Strings can use single or double quotes and special characters are escaped with \
-
-```{r vec_str}
-str1 <- "hello" # double quotes
-str2 <- 'hello' # single quotes
-str3 <- "مرحبًا" # Unicode
-str4 <- "\U0001f605" # sweaty_smile
-```
-
-### Longer
-
-There are several ways to make longer vectors:
-
-**1. With single values** inside c() for combine.
-
-```{r long_single}
-lgl_var <- c(TRUE, FALSE)
-int_var <- c(1L, 6L, 10L)
-dbl_var <- c(1, 2.5, 4.5)
-chr_var <- c("these are", "some strings")
-```
-
- 
-
-**2. With other vectors**
-
-```{r long_vec}
-c(c(1, 2), c(3, 4)) # output is not nested
-```
-
-<details>
-<summary>Side Quest: rlang</summary>
-
-`{rlang}` has [vector constructor functions too](https://rlang.r-lib.org/reference/vector-construction.html):
-
--   `rlang::lgl(...)`
--   `rlang::int(...)`
--   `rlang::dbl(...)`
--   `rlang::chr(...)`
-
-They look to do both more and less than `c()`.
-
--   More:
-    -   Enforce type
-    -   Splice lists
-    -   More types: `rlang::bytes()`, `rlang::cpl(...)`
--   Less:
-    -   Stricter rules on names
-
-Note: currently has `questioning` lifecycle badge, since these constructors may get moved to `vctrs`
-
-</details>
-
-### Type and Length
-
-We can determine the type of a vector with `typeof()` and its length with `length()`
-
-```{r type_length, echo = FALSE}
-# typeof(lgl_var)
-# typeof(int_var)
-# typeof(dbl_var)
-# typeof(chr_var)
-# 
-# length(lgl_var)
-# length(int_var)
-# length(dbl_var)
-# length(chr_var)
-
-var_names <- c("lgl_var", "int_var", "dbl_var", "chr_var")
-var_values <- c("TRUE, FALSE", "1L, 6L, 10L", "1, 2.5, 4.5", "'these are', 'some strings'")
-var_type <- c("logical", "integer", "double", "character")
-var_length <- c(2, 3, 3, 2)
-
-type_length_df <- data.frame(var_names, var_values, var_type, var_length)
-
-# make gt table
-type_length_df |>
-  gt() |>
-  cols_align(align = "center") |>
-  cols_label(
-    var_names ~ "name",
-    var_values ~ "value",
-    var_type ~ "typeof()",
-    var_length ~ "length()"
-  ) |>
-  tab_header(
-    title = "Types of Atomic Vectors",
-    subtitle = ""
-  ) |>
-  tab_footnote(
-    footnote = "Source: https://adv-r.hadley.nz/index.html",
-    locations = cells_title(groups = "title")
-  ) |>
-  tab_style(
-    style = list(cell_fill(color = "#F9E3D6")),
-    locations = cells_body(columns = var_type)
-  ) |>
-  tab_style(
-    style = list(cell_fill(color = "lightcyan")),
-    locations = cells_body(columns = var_length)
-  )
-```
-
-<details>
-<summary>Side Quest: Penguins</summary>
-```{r}
-typeof(penguins$species)
-class(penguins$species)
-
-typeof(species_unlist)
-class(species_unlist)
-
-typeof(species_pull)
-class(species_pull)
-```
-
-</details>
-
-### Missing values
-
-#### Contagion
-
-For most computations, an operation over values that includes a missing value yields a missing value (unless you're careful)
-
-```{r na_contagion}
-# contagion
-5*NA
-sum(c(1, 2, NA, 3))
-```
-
-#### Exceptions
-
-```{r na_exceptions, eval = FALSE}
-NA ^ 0
-#> [1] 1
-NA | TRUE
-#> [1] TRUE
-NA & FALSE
-#> [1] FALSE
-```
-
-
-#### Innoculation
-
-```{r na_innoculation, eval = FALSE}
-sum(c(1, 2, NA, 3), na.rm = TRUE)
-# output: 6
-```
-
-To search for missing values use `is.na()`
-
-```{r na_search, eval = FALSE}
-x <- c(NA, 5, NA, 10)
-x == NA
-# output: NA NA NA NA [BATMAN!]
-```
-
-```{r na_search_better, eval = FALSE}
-is.na(x)
-# output: TRUE FALSE TRUE FALSE
-```
-
-<details>
-<summary>Side Quest: NA Types</summary>
-
-Each type has its own NA type
-
--   Logical: `NA`
--   Integer: `NA_integer`
--   Double: `NA_double`
--   Character: `NA_character`
-
-This may not matter in many contexts.
-
-But this does matter for operations where types matter like `dplyr::if_else()`.
-</details>
-
-
-### Testing
-
-**What type of vector `is.*`() it?**
-
-Test data type:
-
--   Logical: `is.logical()`
--   Integer: `is.integer()`
--   Double: `is.double()`
--   Character: `is.character()`
-
-**What type of object is it?**
-
-Don't test objects with these tools:
-
--   `is.vector()`
--   `is.atomic()`
--   `is.numeric()` 
-
-They don’t test if you have a vector, atomic vector, or numeric vector; you’ll need to carefully read the documentation to figure out what they actually do (preview: *attributes*)
-
-<details>
-<summary>Side Quest: rlang</summary>
-
-Instead, maybe, use `{rlang}`
-
--   `rlang::is_vector`
--   `rlang::is_atomic`
-
-```{r test_rlang}
-# vector
-rlang::is_vector(c(1, 2))
-rlang::is_vector(list(1, 2))
-
-# atomic
-rlang::is_atomic(c(1, 2))
-rlang::is_atomic(list(1, "a"))
-
-```
-
-See more [here](https://rlang.r-lib.org/reference/type-predicates.html)
-</details>
-
-
-### Coercion
-
-* R follows rules for coercion: character → double → integer → logical
-
-* R can coerce either automatically or explicitly
-
-#### **Automatic**
-
-Two contexts for automatic coercion:
-
-1.  Combination
-2.  Mathematical
-
-##### Coercion by Combination:
-
-```{r coerce_c}
-str(c(TRUE, "TRUE"))
-```
-
-##### Coercion by Mathematical operations:
-
-```{r coerce_math}
-# imagine a logical vector about whether an attribute is present
-has_attribute <- c(TRUE, FALSE, TRUE, TRUE)
-
-# number with attribute
-sum(has_attribute)
-```
-
-#### **Explicit**
-
-<!--
-
-Use `as.*()`
-
--   Logical: `as.logical()`
--   Integer: `as.integer()`
--   Double: `as.double()`
--   Character: `as.character()`
-
--->
-
-```{r explicit_coercion, echo = FALSE}
-# dbl_var
-# as.integer(dbl_var)
-# lgl_var
-# as.character(lgl_var)
-
-var_names <- c("lgl_var", "int_var", "dbl_var", "chr_var")
-var_values <- c("TRUE, FALSE", "1L, 6L, 10L", "1, 2.5, 4.5", "'these are', 'some strings'")
-as_logical <- c("TRUE FALSE", "TRUE TRUE TRUE", "TRUE TRUE TRUE", "NA NA")
-as_integer <- c("1 0", "1 6 10", "1 2 4", 'NA_integer')
-as_double <- c("1 0", "1 6 10", "1.0 2.5 4.5", 'NA_double')
-as_character <- c("'TRUE' 'FALSE'", "'1' '6' '10'", "'1' '2.5' '4.5'", "'these are', 'some strings'")
-
-coercion_df <- data.frame(var_names, var_values, as_logical, as_integer, as_double, as_character)
-
-coercion_df |>
-  gt() |>
-  cols_align(align = "center") |>
-  cols_label(
-    var_names ~ "name",
-    var_values ~ "value",
-    as_logical ~ "as.logical()",
-    as_integer ~ "as.integer()",
-    as_double ~ "as.double()",
-    as_character ~ "as.character()"
-  ) |>
-  tab_header(
-    title = "Coercion of Atomic Vectors",
-    subtitle = ""
-  ) |>
-  tab_footnote(
-    footnote = "Source: https://adv-r.hadley.nz/index.html",
-    locations = cells_title(groups = "title")
-  ) |>
-  tab_style(
-    style = list(cell_fill(color = "#F9E3D6")),
-    locations = cells_body(columns = c(as_logical, as_double))
-  ) |>
-  tab_style(
-    style = list(cell_fill(color = "lightcyan")),
-    locations = cells_body(columns = c(as_integer, as_character))
-  )
-```
-
-But note that coercion may fail in one of two ways, or both:
-
--   With warning/error
--   NAs
-
-```{r coerce_error}
-as.integer(c(1, 2, "three"))
-```
-
-### Exercises
-
-1. How do you create raw and complex scalars?
-
-<details><summary>Answer(s)</summary>
-```{r, eval = FALSE}
-as.raw(42)
-#> [1] 2a
-charToRaw("A")
-#> [1] 41
-complex(length.out = 1, real = 1, imaginary = 1)
-#> [1] 1+1i
-```
-</details>
-
-2. Test your knowledge of the vector coercion rules by predicting the output of the following uses of c():
-
-```{r, eval = FALSE}
-c(1, FALSE)
-c("a", 1)
-c(TRUE, 1L)
-```
-
-<details><summary>Answer(s)</summary>
-```{r, eval = FALSE}
-c(1, FALSE)      # will be coerced to double    -> 1 0
-c("a", 1)        # will be coerced to character -> "a" "1"
-c(TRUE, 1L)      # will be coerced to integer   -> 1 1
-```
-</details>
-
-3. Why is `1 == "1"` true? Why is `-1 < FALSE` true? Why is `"one" < 2` false?
-
-<details><summary>Answer(s)</summary>
-These comparisons are carried out by operator-functions (==, <), which coerce their arguments to a common type. In the examples above, these types will be character, double and character: 1 will be coerced to "1", FALSE is represented as 0 and 2 turns into "2" (and numbers precede letters in lexicographic order (may depend on locale)).
-
-</details>
-
-4. Why is the default missing value, NA, a logical vector? What’s special about logical vectors?
-
-<details><summary>Answer(s)</summary>
-The presence of missing values shouldn’t affect the type of an object. Recall that there is a type-hierarchy for coercion from character → double → integer → logical. When combining `NA`s with other atomic types, the `NA`s will be coerced to integer (`NA_integer_`), double (`NA_real_`) or character (`NA_character_`) and not the other way round. If `NA` were a character and added to a set of other values all of these would be coerced to character as well.
-</details>
-
-5. Precisely what do `is.atomic()`, `is.numeric()`, and `is.vector()` test for?
-
-<details><summary>Answer(s)</summary>
-The documentation states that:
-
-* `is.atomic()` tests if an object is an atomic vector (as defined in *Advanced R*) or is `NULL` (!).
-* `is.numeric()` tests if an object has type integer or double and is not of class `factor`, `Date`, `POSIXt` or `difftime`.
-* `is.vector()` tests if an object is a vector (as defined in *Advanced R*) or an expression and has no attributes, apart from names.
-
-Atomic vectors are defined in *Advanced R* as objects of type logical, integer, double, complex, character or raw. Vectors are defined as atomic vectors or lists.
-</details>
-
-
-
-## Attributes
-
-Attributes are name-value pairs that attach metadata to an object(vector).
-
-* **Name-value pairs**: attributes have a name and a value
-* **Metadata**: not data itself, but data about the data
-
-### How? 
-
-#### Getting and Setting
-
-Three functions:
-
-1. retrieve and modify single attributes with `attr()`
-2. retrieve en masse with `attributes()`
-3. set en masse with `structure()`
-
-**Single attribute**
-
-Use `attr()`
-
-```{r attr_single}
-# some object
-a <- c(1, 2, 3)
-
-# set attribute
-attr(x = a, which = "attribute_name") <- "some attribute"
-
-# get attribute
-attr(a, "attribute_name")
-```
-
-**Multiple attributes**
-
-To set multiple attributes, use `structure()` To get multiple attributes, use `attributes()`
-
-```{r attr_multiple_not_from_textbook, echo = FALSE}
-# b <- c(4, 5, 6)
-# 
-# # set
-# b <- structure(
-#   .Data = b,
-#   attrib1_name = "first_attribute",
-#   attrib2_name = "second_attribute"
-# )
-# 
-# # get
-# attributes(b)
-# str(attributes(b))
-```
-
-
-```{r attr_multiple, eval = FALSE}
-a <- 1:3
-attr(a, "x") <- "abcdef"
-attr(a, "x")
-#> [1] "abcdef"
-
-attr(a, "y") <- 4:6
-str(attributes(a))
-#> List of 2
-#>  $ x: chr "abcdef"
-#>  $ y: int [1:3] 4 5 6
-
-# Or equivalently
-a <- structure(
-  1:3, 
-  x = "abcdef",
-  y = 4:6
-)
-str(attributes(a))
-#> List of 2
-#>  $ x: chr "abcdef"
-#>  $ y: int [1:3] 4 5 6
-```
-
- 
-
-### Why
-
-Three particularly important attributes: 
-
-1. **names** - a character vector giving each element a name
-2. **dimension** - (or dim) turns vectors into matrices and arrays 
-3. **class** - powers the S3 object system (we'll learn more about this in chapter 13)
-
-Most attributes are lost by most operations.  Only two attributes are routinely preserved: names and dimension.
-
-#### Names
-
-~~Three~~ Four ways to name:
-
-```{r names}
-# (1) When creating it: 
-x <- c(A = 1, B = 2, C = 3)
-x
-
-# (2) By assigning a character vector to names()
-y <- 1:3
-names(y) <- c("a", "b", "c")
-y
-
-# (3) Inline, with setNames():
-z <- setNames(1:3, c("a", "b", "c"))
-z
-
-```
-
- 
-
-```{r names_via_rlang}
-# (4) By setting names--with {rlang}
-a <- 1:3
-rlang::set_names(
-  x = a,
-  nm = c("a", "b", "c")
-)
-
-```
-
- 
-
-* You can remove names from a vector by using `x <- unname(x)` or `names(x) <- NULL`.
-* Thematically but not directly related: labelled class vectors with `haven::labelled()`
-
-
-#### Dimensions
-
-Create matrices and arrays with `matrix()` and `array()`, or by using the assignment form of `dim()`:
-
-```{r dimensions}
-# Two scalar arguments specify row and column sizes
-x <- matrix(1:6, nrow = 2, ncol = 3)
-x
-
-# One vector argument to describe all dimensions
-y <- array(1:24, c(2, 3, 4)) # rows, columns, no of arrays
-y
-
-# You can also modify an object in place by setting dim()
-z <- 1:6
-dim(z) <- c(2, 3) # rows, columns
-z
-
-a <- 1:24
-dim(a) <- c(2, 3, 4) # rows, columns, no of arrays
-a
-```
-
-##### Functions for working with vectors, matrices and arrays:
-
-Vector | Matrix	| Array
-:----- | :---------- | :-----
-`names()` | `rownames()`, `colnames()` | `dimnames()`
-`length()` | `nrow()`, `ncol()` | `dim()`
-`c()` | `rbind()`, `cbind()` | `abind::abind()`
-— | `t()` | `aperm()`
-`is.null(dim(x))` | `is.matrix()` | `is.array()`
-
-* **Caution**: A vector without a `dim` attribute set is often thought of as 1-dimensional, but actually has `NULL` dimensions.
-* One dimension?
-
-```{r examples_of_1D, eval = FALSE}
-str(1:3)                   # 1d vector
-#>  int [1:3] 1 2 3
-str(matrix(1:3, ncol = 1)) # column vector
-#>  int [1:3, 1] 1 2 3
-str(matrix(1:3, nrow = 1)) # row vector
-#>  int [1, 1:3] 1 2 3
-str(array(1:3, 3))         # "array" vector
-#>  int [1:3(1d)] 1 2 3
-```
-
-
-### Exercises
-
-1. How is `setNames()` implemented? How is `unname()` implemented? Read the source code.
-
-<details><summary>Answer(s)</summary>
-`setNames()` is implemented as:
-
-```{r, eval = FALSE}
-setNames <- function(object = nm, nm) {
-  names(object) <- nm
-  object
-}
-```
-
-Because the data argument comes first, `setNames()` also works well with the magrittr-pipe operator. When no first argument is given, the result is a named vector (this is rather untypical as required arguments usually come first):
-
-```{r, eval = FALSE}
-setNames( , c("a", "b", "c"))
-#>   a   b   c 
-#> "a" "b" "c"
-```
-
-`unname()` is implemented in the following way:
-
-```{r, eval = FALSE}
-unname <- function(obj, force = FALSE) {
-  if (!is.null(names(obj))) 
-    names(obj) <- NULL
-  if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) 
-    dimnames(obj) <- NULL
-  obj
-}
-```
-
-`unname()` removes existing names (or dimnames) by setting them to `NULL`.
-</details>
-
-2. What does `dim()` return when applied to a 1-dimensional vector? When might you use `NROW()` or `NCOL()`?
-
-<details><summary>Answer(s)</summary>
-
-> dim() will return NULL when applied to a 1d vector.
-
-One may want to use `NROW()` or `NCOL()` to handle atomic vectors, lists and NULL values in the same way as one column matrices or data frames. For these objects `nrow()` and `ncol()` return NULL:
-
-```{r, eval = FALSE}
-x <- 1:10
-
-# Return NULL
-nrow(x)
-#> NULL
-ncol(x)
-#> NULL
-
-# Pretend it's a column vector
-NROW(x)
-#> [1] 10
-NCOL(x)
-#> [1] 1
-```
-
-</details>
-
-3. How would you describe the following three objects? What makes them different from `1:5`?
-
-```{r}
-x1 <- array(1:5, c(1, 1, 5))
-x2 <- array(1:5, c(1, 5, 1))
-x3 <- array(1:5, c(5, 1, 1))
-```
-
-<details><summary>Answer(s)</summary>
-```{r, eval = FALSE}
-x1 <- array(1:5, c(1, 1, 5))  # 1 row,  1 column,  5 in third dim.
-x2 <- array(1:5, c(1, 5, 1))  # 1 row,  5 columns, 1 in third dim.
-x3 <- array(1:5, c(5, 1, 1))  # 5 rows, 1 column,  1 in third dim.
-```
-</details>
-
-
-4. An early draft used this code to illustrate `structure()`:
-
-```{r, eval = FALSE}
-structure(1:5, comment = "my attribute")
-#> [1] 1 2 3 4 5
-```
-
-But when you print that object you don’t see the comment attribute. Why? Is the attribute missing, or is there something else special about it?
-
-<details><summary>Answer(s)</summary>
-The documentation states (see `?comment`):
-
-> Contrary to other attributes, the comment is not printed (by print or print.default).
-
-Also, from `?attributes:`
-
-> Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set.
-
-We can retrieve comment attributes by calling them explicitly:
-
-```{r, eval = FALSE}
-foo <- structure(1:5, comment = "my attribute")
-
-attributes(foo)
-#> $comment
-#> [1] "my attribute"
-attr(foo, which = "comment")
-#> [1] "my attribute"
-```
-
-</details>
-
-
-
-## **Class** - S3 atomic vectors
-
- 
-
-Credit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham
-
-**Having a class attribute turns an object into an S3 object.**
-
-What makes S3 atomic vectors different?
-
-1. behave differently from a regular vector when passed to a generic function 
-2. often store additional information in other attributes
-
-Four important S3 vectors used in base R:
-
-1. **Factors** (categorical data)
-2. **Dates**
-3. **Date-times** (POSIXct)
-4. **Durations** (difftime)
-
-### Factors
-
-A factor is a vector used to store categorical data that can contain only predefined values.
-
-Factors are integer vectors with:
-
--   Class: "factor"
--   Attributes: "levels", or the set of allowed values
-
-```{r factor}
-colors = c('red', 'blue', 'green','red','red', 'green')
-# Build a factor
-a_factor <- factor(
-  # values
-  x = colors,
-  # exhaustive list of values
-  levels = c('red', 'blue', 'green', 'yellow')
-)
-```
-
-```{r factor_table}
-# Useful when some possible values are not present in the data
-table(colors)
-table(a_factor)
-
-# - type
-typeof(a_factor)
-class(a_factor)
-
-# - attributes
-attributes(a_factor)
-```
-
-#### Custom Order
-
-Factors can be ordered. This can be useful for models or visualizations where order matters.
-
-```{r factor_ordered}
-
-values <- c('high', 'med', 'low', 'med', 'high', 'low', 'med', 'high')
-
-ordered_factor <- ordered(
-  # values
-  x = values,
-  # levels in ascending order
-  levels = c('low', 'med', 'high')
-)
-
-# Inspect
-ordered_factor
-
-table(values)
-table(ordered_factor)
-```
-
-### Dates
-
-Dates are:
-
--   Double vectors
--   With class "Date"
--   No other attributes
-
-```{r dates}
-notes_date <- Sys.Date()
-
-# type
-typeof(notes_date)
-
-# class
-attributes(notes_date)
-```
-
-The double component represents the number of days since since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time) `1970-01-01`
-
-```{r days_since_1970}
-date <- as.Date("1970-02-01")
-unclass(date)
-```
-
-### Date-times
-
-There are 2 Date-time representations in base R:
-
--   POSIXct, where "ct" denotes *calendar time*
--   POSIXlt, where "lt" designates *local time*
-
-<!--
-
-Just for fun:
-"How to pronounce 'POSIXct'?"
-https://www.howtopronounce.com/posixct
-
--->
-
-We'll focus on POSIXct because:
-
--   Simplest
--   Built on an atomic (double) vector
--   Most appropriate for use in a data frame
-
-Let's now build and deconstruct a Date-time
-
-```{r date_time}
-# Build
-note_date_time <- as.POSIXct(
-  x = Sys.time(), # time
-  tz = "America/New_York" # time zone, used only for formatting
-)
-
-# Inspect
-note_date_time
-
-# - type
-typeof(note_date_time)
-
-# - attributes
-attributes(note_date_time)
-
-structure(note_date_time, tzone = "Europe/Paris")
-```
-
-```{r date_time_format}
-date_time <- as.POSIXct("2024-02-22 12:34:56", tz = "EST")
-unclass(date_time)
-```
-
-
-### Durations
-
-Durations represent the amount of time between pairs of dates or date-times.
-
--   Double vectors
--   Class: "difftime"
--   Attributes: "units", or the unit of duration (e.g., weeks, hours, minutes, seconds, etc.)
-
-```{r durations}
-# Construct
-one_minute <- as.difftime(1, units = "mins")
-# Inspect
-one_minute
-
-# Dissect
-# - type
-typeof(one_minute)
-# - attributes
-attributes(one_minute)
-```
-
-```{r durations_math}
-time_since_01_01_1970 <- notes_date - date
-time_since_01_01_1970
-```
-
-
-See also:
-
--   [`lubridate::make_difftime()`](https://lubridate.tidyverse.org/reference/make_difftime.html)
--   [`clock::date_time_build()`](https://clock.r-lib.org/reference/date_time_build.html)
-
-
-### Exercises
-
-1. What sort of object does `table()` return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables?
-
-<details><summary>Answer(s)</summary>
-
-`table()` returns a contingency table of its input variables. It is implemented as an integer vector with class table and dimensions (which makes it act like an array). Its attributes are dim (dimensions) and dimnames (one name for each input column). The dimensions correspond to the number of unique values (factor levels) in each input variable.
-
-```{r, eval = FALSE}
-x <- table(mtcars[c("vs", "cyl", "am")])
-
-typeof(x)
-#> [1] "integer"
-attributes(x)
-#> $dim
-#> [1] 2 3 2
-#> 
-#> $dimnames
-#> $dimnames$vs
-#> [1] "0" "1"
-#> 
-#> $dimnames$cyl
-#> [1] "4" "6" "8"
-#> 
-#> $dimnames$am
-#> [1] "0" "1"
-#> 
-#> 
-#> $class
-#> [1] "table"
-```
-</details>
-
-2. What happens to a factor when you modify its levels?
-
-```{r, eval = FALSE}
-f1 <- factor(letters)
-levels(f1) <- rev(levels(f1))
-```
-
-<details><summary>Answer(s)</summary>
-The underlying integer values stay the same, but the levels are changed, making it look like the data has changed.
-
-```{r, eval = FALSE}
-f1 <- factor(letters)
-f1
-#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
-#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
-as.integer(f1)
-#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
-#> [26] 26
-
-levels(f1) <- rev(levels(f1))
-f1
-#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
-#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
-as.integer(f1)
-#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
-#> [26] 26
-```
-</details>
-
-3. What does this code do? How do `f2` and `f3` differ from `f1`?
-
-```{r, eval = FALSE}
-f2 <- rev(factor(letters))
-f3 <- factor(letters, levels = rev(letters))
-```
-
-<details><summary>Answer(s)</summary>
-For `f2` and `f3` either the order of the factor elements or its levels are being reversed. For `f1` both transformations are occurring.
-
-```{r, eval = FALSE}
-# Reverse element order
-(f2 <- rev(factor(letters)))
-#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
-#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
-as.integer(f2)
-#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2
-#> [26]  1
-
-# Reverse factor levels (when creating factor)
-(f3 <- factor(letters, levels = rev(letters)))
-#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
-#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
-as.integer(f3)
-#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2
-#> [26]  1
-```
-</details>
-
-
-
-
-
-
-## Lists
-
-* sometimes called a generic vector or recursive vector
-* Recall ([section 2.3.3](https://adv-r.hadley.nz/names-values.html#list-references)): each element is really a *reference* to another object
-* an be composed of elements of different types (as opposed to atomic vectors which must be of only one type)
-
-### Constructing
-
-Simple lists:
-
-```{r list_simple}
-# Construct
-simple_list <- list(
-  c(TRUE, FALSE),   # logicals
-  1:20,             # integers
-  c(1.2, 2.3, 3.4), # doubles
-  c("primo", "secundo", "tercio") # characters
-)
-
-simple_list
-
-# Inspect
-# - type
-typeof(simple_list)
-# - structure
-str(simple_list)
-
-# Accessing
-simple_list[1]
-simple_list[2]
-simple_list[3]
-simple_list[4]
-
-simple_list[[1]][2]
-simple_list[[2]][8]
-simple_list[[3]][2]
-simple_list[[4]][3]
-```
-
-Even Simpler List
-
-```{r list_simpler}
-# Construct
-simpler_list <- list(TRUE, FALSE, 
-                    1, 2, 3, 4, 5, 
-                    1.2, 2.3, 3.4, 
-                    "primo", "secundo", "tercio")
-
-# Accessing
-simpler_list[1]
-simpler_list[5]
-simpler_list[9]
-simpler_list[11]
-```
-
-Nested lists:
-
-```{r list_nested}
-nested_list <- list(
-  # first level
-  list(
-    # second level
-    list(
-      # third level
-      list(1)
-    )
-  )
-)
-
-str(nested_list)
-```
-
-Like JSON.
-
-Combined lists
-
-```{r list_combined}
-# with list()
-list_comb1 <- list(list(1, 2), list(3, 4))
-# with c()
-list_comb2 <- c(list(1, 2), list(3, 4))
-
-# compare structure
-str(list_comb1)
-str(list_comb2)
-
-# does this work if they are different data types?
-list_comb3 <- c(list(1, 2), list(TRUE, FALSE))
-str(list_comb3)
-```
-
-### Testing
-
-Check that is a list:
-
--   `is.list()`
--   \`rlang::is_list()\`\`
-
-The two do the same, except that the latter can check for the number of elements
-
-```{r list_test}
-# is list
-base::is.list(list_comb2)
-rlang::is_list(list_comb2)
-
-# is list of 4 elements
-rlang::is_list(x = list_comb2, n = 4)
-
-# is a vector (of a special type)
-# remember the family tree?
-rlang::is_vector(list_comb2)
-```
-
-### Coercion
-
-Use `as.list()`
-
-```{r list_coercion}
-list(1:3)
-as.list(1:3)
-```
-
-### Matrices and arrays
-
-Although not often used, the dimension attribute can be added to create **list-matrices** or **list-arrays**.
-
-```{r list_matrices_arrays}
-l <- list(1:3, "a", TRUE, 1.0)
-dim(l) <- c(2, 2)
-l
-
-l[[1, 1]]
-```
-
-
-### Exercises
-
-1. List all the ways that a list differs from an atomic vector.
-
-<details><summary>Answer(s)</summary>
-
-* Atomic vectors are always homogeneous (all elements must be of the same type). Lists may be heterogeneous (the elements can be of different types) as described in the introduction of the vectors chapter.
-* Atomic vectors point to one address in memory, while lists contain a separate reference for each element. (This was described in the list sections of the vectors and the names and values chapters.)
-
-```{r, eval = FALSE}
-lobstr::ref(1:2)
-#> [1:0x7fcd936f6e80] <int>
-lobstr::ref(list(1:2, 2))
-#> █ [1:0x7fcd93d53048] <list> 
-#> ├─[2:0x7fcd91377e40] <int> 
-#> └─[3:0x7fcd93b41eb0] <dbl>
-```
-
-
-* Subsetting with out-of-bounds and NA values leads to different output. For example, [ returns NA for atomics and NULL for lists. (This is described in more detail within the subsetting chapter.)
-
-```{r, eval = FALSE}
-# Subsetting atomic vectors
-(1:2)[3]
-#> [1] NA
-(1:2)[NA]
-#> [1] NA NA
-
-# Subsetting lists
-as.list(1:2)[3]
-#> [[1]]
-#> NULL
-as.list(1:2)[NA]
-#> [[1]]
-#> NULL
-#> 
-#> [[2]]
-#> NULL
-```
-
-
-</details>
-
-2. Why do you need to use `unlist()` to convert a list to an atomic vector? Why doesn’t `as.vector()` work?
-
-<details><summary>Answer(s)</summary>
-A list is already a vector, though not an atomic one! Note that as.vector() and is.vector() use different definitions of “vector!”
-
-```{r, eval = FALSE}
-is.vector(as.vector(mtcars))
-#> [1] FALSE
-```
-
-</details>
-
-3. Compare and contrast `c()` and `unlist()` when combining a date and date-time into a single vector.
-
-<details><summary>Answer(s)</summary>
-Date and date-time objects are both built upon doubles. While dates store the number of days since the reference date 1970-01-01 (also known as “the Epoch”) in days, date-time-objects (POSIXct) store the time difference to this date in seconds.
-
-```{r, eval = FALSE}
-date    <- as.Date("1970-01-02")
-dttm_ct <- as.POSIXct("1970-01-01 01:00", tz = "UTC")
-
-# Internal representations
-unclass(date)
-#> [1] 1
-unclass(dttm_ct)
-#> [1] 3600
-#> attr(,"tzone")
-#> [1] "UTC"
-```
-
-As the c() generic only dispatches on its first argument, combining date and date-time objects via c() could lead to surprising results in older R versions (pre R 4.0.0):
-
-```{r, eval = FALSE}
-# Output in R version 3.6.2
-c(date, dttm_ct)  # equal to c.Date(date, dttm_ct) 
-#> [1] "1970-01-02" "1979-11-10"
-c(dttm_ct, date)  # equal to c.POSIXct(date, dttm_ct)
-#> [1] "1970-01-01 02:00:00 CET" "1970-01-01 01:00:01 CET"
-```
-
-In the first statement above c.Date() is executed, which incorrectly treats the underlying double of dttm_ct (3600) as days instead of seconds. Conversely, when c.POSIXct() is called on a date, one day is counted as one second only.
-
-We can highlight these mechanics by the following code:
-
-```{r, eval = FALSE}
-# Output in R version 3.6.2
-unclass(c(date, dttm_ct))  # internal representation
-#> [1] 1 3600
-date + 3599
-#> "1979-11-10"
-```
-
-As of R 4.0.0 these issues have been resolved and both methods now convert their input first into POSIXct and Date, respectively.
-
-```{r, eval = FALSE}
-c(dttm_ct, date)
-#> [1] "1970-01-01 01:00:00 UTC" "1970-01-02 00:00:00 UTC"
-unclass(c(dttm_ct, date))
-#> [1]  3600 86400
-
-c(date, dttm_ct)
-#> [1] "1970-01-02" "1970-01-01"
-unclass(c(date, dttm_ct))
-#> [1] 1 0
-```
-
-However, as c() strips the time zone (and other attributes) of POSIXct objects, some caution is still recommended.
-
-```{r, eval = FALSE}
-(dttm_ct <- as.POSIXct("1970-01-01 01:00", tz = "HST"))
-#> [1] "1970-01-01 01:00:00 HST"
-attributes(c(dttm_ct))
-#> $class
-#> [1] "POSIXct" "POSIXt"
-```
-
-A package that deals with these kinds of problems in more depth and provides a structural solution for them is the {vctrs} package9 which is also used throughout the tidyverse.10
-
-Let’s look at unlist(), which operates on list input.
-
-```{r, eval = FALSE}
-# Attributes are stripped
-unlist(list(date, dttm_ct))  
-#> [1]     1 39600
-```
-
-We see again that dates and date-times are internally stored as doubles. Unfortunately, this is all we are left with, when unlist strips the attributes of the list.
-
-To summarise: c() coerces types and strips time zones. Errors may have occurred in older R versions because of inappropriate method dispatch/immature methods. unlist() strips attributes.
-</details>
-
-
-
-## Data frames and tibbles
-
- 
-
-Credit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham
-
-### Data frame
-
-A data frame is a:
-
--   Named list of vectors (i.e., column names)
--   Attributes:
-    -   (column) `names`
-    -   `row.names`
-    -   Class: "data frame"
-
-```{r data_frame}
-# Construct
-df <- data.frame(
-  col1 = c(1, 2, 3),              # named atomic vector
-  col2 = c("un", "deux", "trois") # another named atomic vector
-  # ,stringsAsFactors = FALSE # default for versions after R 4.1
-)
-
-# Inspect
-df
-
-# Deconstruct
-# - type
-typeof(df)
-# - attributes
-attributes(df)
-```
-
-```{r df_functions}
-rownames(df)
-colnames(df)
-names(df) # Same as colnames(df)
-
-nrow(df) 
-ncol(df)
-length(df) # Same as ncol(df)
-```
-
-Unlike other lists, the length of each vector must be the same (i.e. as many vector elements as rows in the data frame).
-
-### Tibble
-
-Created to relieve some of the frustrations and pain points created by data frames, tibbles are data frames that are:
-
--   Lazy (do less)
--   Surly (complain more)
-
-#### Lazy
-
-Tibbles do not:
-
--   Coerce strings
--   Transform non-syntactic names
--   Recycle vectors of length greater than 1
-
-**Coerce strings**
-
-```{r tbl_no_coerce}
-chr_col <- c("don't", "factor", "me", "bro")
-
-# data frame
-df <- data.frame(
-  a = chr_col,
-  # in R 4.1 and earlier, this was the default
-  stringsAsFactors = TRUE
-)
-
-# tibble
-tbl <- tibble::tibble(
-  a = chr_col
-)
-
-# contrast the structure
-str(df$a)
-str(tbl$a)
-
-```
-
-**Transform non-syntactic names**
-
-```{r tbl_col_name}
-# data frame
-df <- data.frame(
-  `1` = c(1, 2, 3)
-)
-
-# tibble
-tbl <- tibble::tibble(
-  `1` = c(1, 2, 3)
-)
-
-# contrast the names
-names(df)
-names(tbl)
-```
-
-**Recycle vectors of length greater than 1**
-
-```{r tbl_recycle, error=TRUE}
-# data frame
-df <- data.frame(
-  col1 = c(1, 2, 3, 4),
-  col2 = c(1, 2)
-)
-
-# tibble
-tbl <- tibble::tibble(
-  col1 = c(1, 2, 3, 4),
-  col2 = c(1, 2)
-)
-```
-
-#### Surly
-
-Tibbles do only what they're asked and complain if what they're asked doesn't make sense:
-
--   Subsetting always yields a tibble
--   Complains if cannot find column
-
-**Subsetting always yields a tibble**
-
-```{r tbl_subset}
-# data frame
-df <- data.frame(
-  col1 = c(1, 2, 3, 4)
-)
-
-# tibble
-tbl <- tibble::tibble(
-  col1 = c(1, 2, 3, 4)
-)
-
-# contrast
-df_col <- df[, "col1"]
-str(df_col)
-tbl_col <- tbl[, "col1"]
-str(tbl_col)
-
-# to select a vector, do one of these instead
-tbl_col_1 <- tbl[["col1"]]
-str(tbl_col_1)
-tbl_col_2 <- dplyr::pull(tbl, col1)
-str(tbl_col_2)
-```
-
-**Complains if cannot find column**
-
-```{r tbl_col_match, warning=TRUE}
-names(df)
-df$col
-
-names(tbl)
-tbl$col
-```
-
-#### One more difference
-
-**`tibble()` allows you to refer to variables created during construction**
-
-```{r df_tibble_diff}
-tibble::tibble(
-  x = 1:3,
-  y = x * 2 # x refers to the line above
-)
-```
-
-<details>
-<summary>Side Quest: Row Names</summary>
-
-- character vector containing only unique values
-- get and set with `rownames()`
-- can use them to subset rows
-
-```{r row_names}
-df3 <- data.frame(
-  age = c(35, 27, 18),
-  hair = c("blond", "brown", "black"),
-  row.names = c("Bob", "Susan", "Sam")
-)
-df3
-
-rownames(df3)
-df3["Bob", ]
-
-rownames(df3) <- c("Susan", "Bob", "Sam")
-rownames(df3)
-df3["Bob", ]
-```
-
-There are three reasons why row names are undesirable:
-
-3. Metadata is data, so storing it in a different way to the rest of the data is fundamentally a bad idea. 
-2. Row names are a poor abstraction for labelling rows because they only work when a row can be identified by a single string. This fails in many cases.
-3. Row names must be unique, so any duplication of rows (e.g. from bootstrapping) will create new row names.
-
-</details>
-
-
-### Printing
-
-Data frames and tibbles print differently
-
-```{r df_tibble_print}
-df3
-tibble::as_tibble(df3)
-```
-
-
-### Subsetting
-
-Two undesirable subsetting behaviours:
-
-1. When you subset columns with `df[, vars]`, you will get a vector if vars selects one variable, otherwise you’ll get a data frame, unless you always remember to use `df[, vars, drop = FALSE]`.
-2. When you attempt to extract a single column with `df$x` and there is no column `x`, a data frame will instead select any variable that starts with `x`. If no variable starts with `x`, `df$x` will return NULL.
-
-Tibbles tweak these behaviours so that a [ always returns a tibble, and a $ doesn’t do partial matching and warns if it can’t find a variable (*this is what makes tibbles surly*).
-
-### Testing
-
-Whether data frame: `is.data.frame()`. Note: both data frame and tibble are data frames.
-
-Whether tibble: `tibble::is_tibble`. Note: only tibbles are tibbles. Vanilla data frames are not.
-
-### Coercion
-
--   To data frame: `as.data.frame()`
--   To tibble: `tibble::as_tibble()`
-
-### List Columns
-
-List-columns are allowed in data frames but you have to do a little extra work by either adding the list-column after creation or wrapping the list in `I()`
-
-```{r list_columns}
-df4 <- data.frame(x = 1:3)
-df4$y <- list(1:2, 1:3, 1:4)
-df4
-
-df5 <- data.frame(
-  x = 1:3, 
-  y = I(list(1:2, 1:3, 1:4))
-)
-df5
-```
-
-### Matrix and data frame columns
-
-- As long as the number of rows matches the data frame, it’s also possible to have a matrix or data frame as a column of a data frame.
-- same as list-columns, must either addi the list-column after creation or wrapping the list in `I()`
-
-```{r matrix_df_columns}
-dfm <- data.frame(
-  x = 1:3 * 10,
-  y = I(matrix(1:9, nrow = 3))
-)
-
-dfm$z <- data.frame(a = 3:1, b = letters[1:3], stringsAsFactors = FALSE)
-
-str(dfm)
-dfm$y
-dfm$z
-```
-
-
-### Exercises
-
-1. Can you have a data frame with zero rows? What about zero columns?
-
-<details><summary>Answer(s)</summary>
-Yes, you can create these data frames easily; either during creation or via subsetting. Even both dimensions can be zero. Create a 0-row, 0-column, or an empty data frame directly:
-
-```{r, eval = FALSE}
-data.frame(a = integer(), b = logical())
-#> [1] a b
-#> <0 rows> (or 0-length row.names)
-
-data.frame(row.names = 1:3)  # or data.frame()[1:3, ]
-#> data frame with 0 columns and 3 rows
-
-data.frame()
-#> data frame with 0 columns and 0 rows
-```
-
-Create similar data frames via subsetting the respective dimension with either 0, `NULL`, `FALSE` or a valid 0-length atomic (`logical(0)`, `character(0)`, `integer(0)`, `double(0)`). Negative integer sequences would also work. The following example uses a zero:
-
-```{r, eval = FALSE}
-mtcars[0, ]
-#>  [1] mpg  cyl  disp hp   drat wt   qsec vs   am   gear carb
-#> <0 rows> (or 0-length row.names)
-
-mtcars[ , 0]  # or mtcars[0]
-#> data frame with 0 columns and 32 rows
-
-mtcars[0, 0]
-#> data frame with 0 columns and 0 rows
-```
-
-
-</details>
-
-2. What happens if you attempt to set rownames that are not unique?
-
-<details><summary>Answer(s)</summary>
-Matrices can have duplicated row names, so this does not cause problems.
-
-Data frames, however, require unique rownames and you get different results depending on how you attempt to set them. If you set them directly or via `row.names()`, you get an error:
-
-```{r, eval = FALSE}
-data.frame(row.names = c("x", "y", "y"))
-#> Error in data.frame(row.names = c("x", "y", "y")): duplicate row.names: y
-
-df <- data.frame(x = 1:3)
-row.names(df) <- c("x", "y", "y")
-#> Warning: non-unique value when setting 'row.names': 'y'
-#> Error in `.rowNamesDF<-`(x, value = value): duplicate 'row.names' are not allowed
-```
-
-If you use subsetting, `[` automatically deduplicates:
-
-```{r, eval = FALSE}
-row.names(df) <- c("x", "y", "z")
-df[c(1, 1, 1), , drop = FALSE]
-#>     x
-#> x   1
-#> x.1 1
-#> x.2 1
-```
-
-</details>
-
-3. If `df` is a data frame, what can you say about `t(df)`, and `t(t(df))`? Perform some experiments, making sure to try different column types.
-
-<details><summary>Answer(s)</summary>
-Both of `t(df)` and `t(t(df))` will return matrices:
-
-```{r, eval = FALSE}
-df <- data.frame(x = 1:3, y = letters[1:3])
-is.matrix(df)
-#> [1] FALSE
-is.matrix(t(df))
-#> [1] TRUE
-is.matrix(t(t(df)))
-#> [1] TRUE
-```
-
-The dimensions will respect the typical transposition rules:
-
-```{r, eval = FALSE}
-dim(df)
-#> [1] 3 2
-dim(t(df))
-#> [1] 2 3
-dim(t(t(df)))
-#> [1] 3 2
-```
-
-Because the output is a matrix, every column is coerced to the same type. (It is implemented within `t.data.frame()` via `as.matrix()` which is described below).
-
-```{r, eval = FALSE}
-df
-#>   x y
-#> 1 1 a
-#> 2 2 b
-#> 3 3 c
-t(df)
-#>   [,1] [,2] [,3]
-#> x "1"  "2"  "3" 
-#> y "a"  "b"  "c"
-```
-
-</details>
-
-4. What does `as.matrix()` do when applied to a data frame with columns of different types? How does it differ from `data.matrix()`?
-
-<details><summary>Answer(s)</summary>
-The type of the result of as.matrix depends on the types of the input columns (see `?as.matrix`):
-
-> The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g. all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.
-
-On the other hand, `data.matrix` will always return a numeric matrix (see `?data.matrix()`).
-
-> Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. […] Character columns are first converted to factors and then to integers.
-
-We can illustrate and compare the mechanics of these functions using a concrete example. `as.matrix()` makes it possible to retrieve most of the original information from the data frame but leaves us with characters. To retrieve all information from `data.matrix()`’s output, we would need a lookup table for each column.
-
-```{r, eval = FALSE}
-df_coltypes <- data.frame(
-  a = c("a", "b"),
-  b = c(TRUE, FALSE),
-  c = c(1L, 0L),
-  d = c(1.5, 2),
-  e = factor(c("f1", "f2"))
-)
-
-as.matrix(df_coltypes)
-#>      a   b       c   d     e   
-#> [1,] "a" "TRUE"  "1" "1.5" "f1"
-#> [2,] "b" "FALSE" "0" "2.0" "f2"
-data.matrix(df_coltypes)
-#>      a b c   d e
-#> [1,] 1 1 1 1.5 1
-#> [2,] 2 0 0 2.0 2
-```
-
-</details>
-
-
-
-
-
-
-
-## `NULL`
-
-Special type of object that:
-
--   Length 0
--   Cannot have attributes
-
-```{r null, results = 'hide'}
-typeof(NULL)
-#> [1] "NULL"
-
-length(NULL)
-#> [1] 0
-```
-
-```{r null_attr, error=TRUE}
-x <- NULL
-attr(x, "y") <- 1
-```
-
-```{r null_check}
-is.null(NULL)
-```
-
-
-## Digestif
-
-Let is use some of this chapter's skills on the `penguins` data.
-
-### Attributes
-
-```{r}
-str(penguins_raw)
-```
-
-```{r}
-str(penguins_raw, give.attr = FALSE)
-```
-
-### Data Frames vs Tibbles
-
-```{r}
-penguins_df <- data.frame(penguins)
-penguins_tb <- penguins #i.e. penguins was already a tibble
-```
-
-#### Printing
-
-* Tip: print out these results in RStudio under different editor themes
-
-```{r, eval = FALSE}
-print(penguins_df) #don't run this
-```
-
-```{r}
-head(penguins_df)
-```
-
-```{r}
-penguins_tb
-```
-
-### Atomic Vectors
-
-```{r}
-species_vector_df <- penguins_df |> select(species)
-species_unlist_df <- penguins_df |> select(species) |> unlist()
-species_pull_df   <- penguins_df |> select(species) |> pull()
-
-species_vector_tb <- penguins_tb |> select(species)
-species_unlist_tb <- penguins_tb |> select(species) |> unlist()
-species_pull_tb   <- penguins_tb |> select(species) |> pull()
-```
-
-<details>
-<summary>`typeof()` and `class()`</summary>
-```{r}
-typeof(species_vector_df)
-class(species_vector_df)
-
-typeof(species_unlist_df)
-class(species_unlist_df)
-
-typeof(species_pull_df)
-class(species_pull_df)
-
-typeof(species_vector_tb)
-class(species_vector_tb)
-
-typeof(species_unlist_tb)
-class(species_unlist_tb)
-
-typeof(species_pull_tb)
-class(species_pull_tb)
-```
-</details>
-
-### Column Names
-
-```{r}
-colnames(penguins_tb)
-```
-
-```{r}
-names(penguins_tb) == colnames(penguins_tb)
-```
-
-```{r}
-names(penguins_df) == names(penguins_tb)
-```
-
-* What if we only invoke a partial name of a column of a tibble?
-
-```{r, error = TRUE}
-penguins_tb$y 
-```
-
-
-
-* What if we only invoke a partial name of a column of a data frame?
-
-```{r}
-head(penguins_df$y) #instead of `year`
-```
-
-* Is this evaluation in alphabetical order or column order?
-
-```{r}
-penguins_df_se_sp <- penguins_df |> select(sex, species)
-penguins_df_sp_se <- penguins_df |> select(species, sex)
-```
-
-```{r}
-head(penguins_df_se_sp$s)
-```
-
-```{r}
-head(penguins_df_sp_se$s)
-```
-
-
-## Chapter Quiz
-
-1. What are the four common types of atomic vectors? What are the two rare types?
-
-<details><summary>Answer(s)</summary>
-The four common types of atomic vector are logical, integer, double and character. The two rarer types are complex and raw.
-</details>
-
-2. What are attributes? How do you get them and set them?
-
-<details><summary>Answer(s)</summary>
-Attributes allow you to associate arbitrary additional metadata to any object. You can get and set individual attributes with `attr(x, "y")` and `attr(x, "y") <- value`; or you can get and set all attributes at once with `attributes()`.
-</details>
-
-3. How is a list different from an atomic vector? How is a matrix different from a data frame?
-
-<details><summary>Answer(s)</summary>
-The elements of a list can be any type (even a list); the elements of an atomic vector are all of the same type. Similarly, every element of a matrix must be the same type; in a data frame, different columns can have different types.
-</details>
-
-4. Can you have a list that is a matrix? Can a data frame have a column that is a matrix?
-
-<details><summary>Answer(s)</summary>
-You can make a list-array by assigning dimensions to a list. You can make a matrix a column of a data frame with `df$x <- matrix()`, or by using `I()` when creating a new data frame `data.frame(x = I(matrix()))`.
-</details>
-
-5. How do tibbles behave differently from data frames?
-
-<details><summary>Answer(s)</summary>
-Tibbles have an enhanced print method, never coerce strings to factors, and provide stricter subsetting methods.
-</details>
diff --git a/slides/03.qmd b/slides/03.qmd
@@ -0,0 +1,1975 @@
+---
+engine: knitr
+title: Vectors
+---
+
+## Learning objectives:
+
+-   Learn about different types of vectors and their attributes
+-   Navigate through vector types and their value types
+-   Venture into factors and date-time objects
+-   Discuss the differences between data frames and tibbles
+-   Do not get absorbed by the `NA` and `NULL` black hole
+
+
+## Session Info
+
+```{r ch9_setup, message = FALSE, warning = FALSE}
+library("dplyr")
+library("gt")
+library("palmerpenguins")
+```
+
+
+<details>
+<summary>Session Info</summary>
+```{r}
+utils::sessionInfo()
+```
+</details>
+
+## Aperitif
+
+
+
+## Counting Penguins
+
+Consider this code to count the number of Gentoo penguins in the `penguins` data set. We see that there are 124 Gentoo penguins.
+
+```{r, eval = FALSE}
+sum("Gentoo" == penguins$species)
+# output: 124
+```
+
+## In
+
+One subtle error can arise in trying out `%in%` here instead.
+
+```{r, results = 'hide'}
+species_vector <- penguins |> select(species)
+print("Gentoo" %in% species_vector)
+# output: FALSE
+```
+
+
+
+## Fix: base R 
+
+```{r, results = 'hide'}
+species_unlist <- penguins |> select(species) |> unlist()
+print("Gentoo" %in% species_unlist)
+# output: TRUE
+```
+
+## Fix: dplyr
+
+```{r, results = 'hide'}
+species_pull <- penguins |> select(species) |> pull()
+print("Gentoo" %in% species_pull)
+# output: TRUE
+```
+
+## Motivation
+
+* What are the different types of vectors?
+* How does this affect accessing vectors?
+
+<details>
+<summary>Side Quest: Looking up the `%in%` operator</summary>
+If you want to look up the manual pages for the `%in%` operator with the `?`, use backticks:
+
+```{r, eval = FALSE}
+?`%in%`
+```
+
+and we find that `%in%` is a wrapper for the `match()` function.
+
+</details>
+
+
+## Types of Vectors
+
+ 
+
+Two main types:
+
+-   **Atomic**: Elements all the same type.
+-   **List**: Elements are different Types.
+
+Closely related but not technically a vector:
+
+-   **NULL**: Null elements. Often length zero.
+
+
+## Types of Atomic Vectors (1/2)
+
+{width=50%} 
+
+## Types of Atomic Vectors (2/2)
+
+-   **Logical**: True/False
+-   **Integer**: Numeric (discrete, no decimals)
+-   **Double**: Numeric (continuous, decimals)
+-   **Character**: String
+
+## Vectors of Length One
+
+**Scalars** are vectors that consist of a single value.
+
+## Logicals
+
+```{r vec_lgl}
+lgl1 <- TRUE
+lgl2 <- T #abbreviation for TRUE
+lgl3 <- FALSE
+lgl4 <- F #abbreviation for FALSE
+```
+
+## Doubles
+
+```{r vec_dbl}
+# integer, decimal, scientific, or hexidecimal format
+dbl1 <- 1
+dbl2 <- 1.234 # decimal
+dbl3 <- 1.234e0 # scientific format
+dbl4 <- 0xcafe # hexidecimal format
+```
+
+## Integers
+
+Integers must be followed by L and cannot have fractional values
+
+```{r vec_int}
+int1 <- 1L
+int2 <- 1234L
+int3 <- 1234e0L
+int4 <- 0xcafeL
+```
+
+<details>
+<summary>Pop Quiz: Why "L" for integers?</summary>
+Wickham notes that the use of `L` dates back to the **C** programming language and its "long int" type for memory allocation.
+</details>
+
+## Strings
+
+Strings can use single or double quotes and special characters are escaped with \
+
+```{r vec_str}
+str1 <- "hello" # double quotes
+str2 <- 'hello' # single quotes
+str3 <- "مرحبًا" # Unicode
+str4 <- "\U0001f605" # sweaty_smile 😅
+```
+
+## Longer 1/2
+
+There are several ways to make longer vectors:
+
+**1. With single values** inside c() for combine.
+
+```{r long_single}
+lgl_var <- c(TRUE, FALSE)
+int_var <- c(1L, 6L, 10L)
+dbl_var <- c(1, 2.5, 4.5)
+chr_var <- c("these are", "some strings")
+```
+
+ 
+
+## Longer 2/2
+
+**2. With other vectors**
+
+```{r long_vec}
+c(c(1, 2), c(3, 4)) # output is not nested
+```
+
+## Type and Length
+
+We can determine the type of a vector with `typeof()` and its length with `length()`
+
+```{r type_length, echo = FALSE}
+# typeof(lgl_var)
+# typeof(int_var)
+# typeof(dbl_var)
+# typeof(chr_var)
+# 
+# length(lgl_var)
+# length(int_var)
+# length(dbl_var)
+# length(chr_var)
+
+var_names <- c("lgl_var", "int_var", "dbl_var", "chr_var")
+var_values <- c("TRUE, FALSE", "1L, 6L, 10L", "1, 2.5, 4.5", "'these are', 'some strings'")
+var_type <- c("logical", "integer", "double", "character")
+var_length <- c(2, 3, 3, 2)
+
+type_length_df <- data.frame(var_names, var_values, var_type, var_length)
+
+# make gt table
+type_length_df |>
+  gt() |>
+  cols_align(align = "center") |>
+  cols_label(
+    var_names ~ "name",
+    var_values ~ "value",
+    var_type ~ "typeof()",
+    var_length ~ "length()"
+  ) |>
+  tab_header(
+    title = "Types of Atomic Vectors",
+    subtitle = ""
+  ) |>
+  tab_footnote(
+    footnote = "Source: https://adv-r.hadley.nz/index.html",
+    locations = cells_title(groups = "title")
+  ) |>
+  tab_style(
+    style = list(cell_fill(color = "#F9E3D6")),
+    locations = cells_body(columns = var_type)
+  ) |>
+  tab_style(
+    style = list(cell_fill(color = "lightcyan")),
+    locations = cells_body(columns = var_length)
+  )
+```
+
+## Side Quest: Penguins
+
+<details>
+```{r}
+typeof(penguins$species)
+class(penguins$species)
+
+typeof(species_unlist)
+class(species_unlist)
+
+typeof(species_pull)
+class(species_pull)
+```
+
+</details>
+
+## Missing values: Contagion
+
+For most computations, an operation over values that includes a missing value yields a missing value (unless you're careful)
+
+```{r na_contagion}
+# contagion
+5*NA
+sum(c(1, 2, NA, 3))
+```
+
+## Missing values: Contagion Exceptions
+
+```{r na_exceptions, eval = FALSE}
+NA ^ 0
+#> [1] 1
+NA | TRUE
+#> [1] TRUE
+NA & FALSE
+#> [1] FALSE
+```
+
+
+#### Innoculation
+
+```{r na_innoculation, eval = FALSE}
+sum(c(1, 2, NA, 3), na.rm = TRUE)
+# output: 6
+```
+
+To search for missing values use `is.na()`
+
+```{r na_search, eval = FALSE}
+x <- c(NA, 5, NA, 10)
+x == NA
+# output: NA NA NA NA [BATMAN!]
+```
+
+```{r na_search_better, eval = FALSE}
+is.na(x)
+# output: TRUE FALSE TRUE FALSE
+```
+
+## Missing Values: NA Types 
+
+<details>
+Each type has its own NA type
+
+-   Logical: `NA`
+-   Integer: `NA_integer`
+-   Double: `NA_double`
+-   Character: `NA_character`
+
+This may not matter in many contexts.
+
+Can matter for operations where types matter like `dplyr::if_else()`.
+</details>
+
+
+## Testing (1/2)
+
+**What type of vector `is.*`() it?**
+
+Test data type:
+
+-   Logical: `is.logical()`
+-   Integer: `is.integer()`
+-   Double: `is.double()`
+-   Character: `is.character()`
+
+
+## Testing (2/2)
+
+**What type of object is it?**
+
+Don't test objects with these tools:
+
+-   `is.vector()`
+-   `is.atomic()`
+-   `is.numeric()` 
+
+They don’t test if you have a vector, atomic vector, or numeric vector; you’ll need to carefully read the documentation to figure out what they actually do (preview: *attributes*)
+
+## Side Quest: rlang `is_*()`
+
+<details>
+<summary>Maybe use `{rlang}`?</summary>
+
+-   `rlang::is_vector`
+-   `rlang::is_atomic`
+
+```{r test_rlang}
+# vector
+rlang::is_vector(c(1, 2))
+rlang::is_vector(list(1, 2))
+
+# atomic
+rlang::is_atomic(c(1, 2))
+rlang::is_atomic(list(1, "a"))
+
+```
+
+See more [here](https://rlang.r-lib.org/reference/type-predicates.html)
+</details>
+
+
+## Coercion
+
+* R follows rules for coercion: character → double → integer → logical
+
+* R can coerce either automatically or explicitly
+
+#### **Automatic**
+
+Two contexts for automatic coercion:
+
+1.  Combination
+2.  Mathematical
+
+
+
+## Coercion by Combination:
+
+```{r coerce_c}
+str(c(TRUE, "TRUE"))
+```
+
+## Coercion by Mathematical operations:
+
+```{r coerce_math}
+# imagine a logical vector about whether an attribute is present
+has_attribute <- c(TRUE, FALSE, TRUE, TRUE)
+
+# number with attribute
+sum(has_attribute)
+```
+
+## **Explicit**
+
+<!--
+
+Use `as.*()`
+
+-   Logical: `as.logical()`
+-   Integer: `as.integer()`
+-   Double: `as.double()`
+-   Character: `as.character()`
+
+-->
+
+```{r explicit_coercion, echo = FALSE}
+# dbl_var
+# as.integer(dbl_var)
+# lgl_var
+# as.character(lgl_var)
+
+var_names <- c("lgl_var", "int_var", "dbl_var", "chr_var")
+var_values <- c("TRUE, FALSE", "1L, 6L, 10L", "1, 2.5, 4.5", "'these are', 'some strings'")
+as_logical <- c("TRUE FALSE", "TRUE TRUE TRUE", "TRUE TRUE TRUE", "NA NA")
+as_integer <- c("1 0", "1 6 10", "1 2 4", 'NA_integer')
+as_double <- c("1 0", "1 6 10", "1.0 2.5 4.5", 'NA_double')
+as_character <- c("'TRUE' 'FALSE'", "'1' '6' '10'", "'1' '2.5' '4.5'", "'these are', 'some strings'")
+
+coercion_df <- data.frame(var_names, var_values, as_logical, as_integer, as_double, as_character)
+
+coercion_df |>
+  gt() |>
+  cols_align(align = "center") |>
+  cols_label(
+    var_names ~ "name",
+    var_values ~ "value",
+    as_logical ~ "as.logical()",
+    as_integer ~ "as.integer()",
+    as_double ~ "as.double()",
+    as_character ~ "as.character()"
+  ) |>
+  tab_header(
+    title = "Coercion of Atomic Vectors",
+    subtitle = ""
+  ) |>
+  tab_footnote(
+    footnote = "Source: https://adv-r.hadley.nz/index.html",
+    locations = cells_title(groups = "title")
+  ) |>
+  tab_style(
+    style = list(cell_fill(color = "#F9E3D6")),
+    locations = cells_body(columns = c(as_logical, as_double))
+  ) |>
+  tab_style(
+    style = list(cell_fill(color = "lightcyan")),
+    locations = cells_body(columns = c(as_integer, as_character))
+  )
+```
+
+But note that coercion may fail in one of two ways, or both:
+
+-   With warning/error
+-   NAs
+
+```{r coerce_error}
+as.integer(c(1, 2, "three"))
+```
+
+## Exercises 1/5
+
+1. How do you create raw and complex scalars?
+
+<details><summary>Answer(s)</summary>
+```{r, eval = FALSE}
+as.raw(42)
+#> [1] 2a
+charToRaw("A")
+#> [1] 41
+complex(length.out = 1, real = 1, imaginary = 1)
+#> [1] 1+1i
+```
+</details>
+
+## Exercises 2/5
+
+2. Test your knowledge of the vector coercion rules by predicting the output of the following uses of c():
+
+```{r, eval = FALSE}
+c(1, FALSE)
+c("a", 1)
+c(TRUE, 1L)
+```
+
+<details><summary>Answer(s)</summary>
+```{r, eval = FALSE}
+c(1, FALSE)      # will be coerced to double    -> 1 0
+c("a", 1)        # will be coerced to character -> "a" "1"
+c(TRUE, 1L)      # will be coerced to integer   -> 1 1
+```
+</details>
+
+## Exercises 3/5
+
+3. Why is `1 == "1"` true? Why is `-1 < FALSE` true? Why is `"one" < 2` false?
+
+<details><summary>Answer(s)</summary>
+These comparisons are carried out by operator-functions (==, <), which coerce their arguments to a common type. In the examples above, these types will be character, double and character: 1 will be coerced to "1", FALSE is represented as 0 and 2 turns into "2" (and numbers precede letters in lexicographic order (may depend on locale)).
+
+</details>
+
+## Exercises 4/5
+
+4. Why is the default missing value, NA, a logical vector? What’s special about logical vectors?
+
+<details><summary>Answer(s)</summary>
+The presence of missing values shouldn’t affect the type of an object. Recall that there is a type-hierarchy for coercion from character → double → integer → logical. When combining `NA`s with other atomic types, the `NA`s will be coerced to integer (`NA_integer_`), double (`NA_real_`) or character (`NA_character_`) and not the other way round. If `NA` were a character and added to a set of other values all of these would be coerced to character as well.
+</details>
+
+## Exercises 5/5
+
+5. Precisely what do `is.atomic()`, `is.numeric()`, and `is.vector()` test for?
+
+<details><summary>Answer(s)</summary>
+
+* `is.atomic()` tests if an object is an atomic vector or is `NULL` (!). Atomic vectors are objects of type logical, integer, double, complex, character or raw.
+* `is.numeric()` tests if an object has type integer or double and is not of class `factor`, `Date`, `POSIXt` or `difftime`.
+* `is.vector()` tests if an object is a vector or an expression and has no attributes, apart from names. Vectors are atomic vectors or lists.
+ 
+</details>
+
+
+## Attributes
+
+Attributes are name-value pairs that attach metadata to an object (vector).
+
+* **Name-value pairs**: attributes have a name and a value
+* **Metadata**: not data itself, but data about the data
+ 
+## Getting and Setting
+
+Three functions:
+
+1. retrieve and modify single attributes with `attr()`
+2. retrieve en masse with `attributes()`
+3. set en masse with `structure()`
+
+## Single attribute
+
+Use `attr()`
+
+```{r attr_single}
+# some object
+a <- c(1, 2, 3)
+
+# set attribute
+attr(x = a, which = "attribute_name") <- "some attribute"
+
+# get attribute
+attr(a, "attribute_name")
+```
+
+## Multiple attributes
+
+`structure()`: set multiple attributes, `attributes()`: get multiple attributes
+
+:::: columns
+::: column
+```{r attr_multiple}
+a <- 1:3
+attr(a, "x") <- "abcdef"
+attr(a, "x")
+
+attr(a, "y") <- 4:6
+str(attributes(a))
+
+b <- structure(
+  1:3, 
+  x = "abcdef",
+  y = 4:6
+)
+identical(a, b)
+```
+:::
+
+::: column
+ 
+:::
+::::
+
+
+## Why
+
+Three particularly important attributes: 
+
+1. **names** - a character vector giving each element a name
+2. **dimension** - (or dim) turns vectors into matrices and arrays 
+3. **class** - powers the S3 object system (we'll learn more about this in chapter 13)
+
+Most attributes are lost by most operations.  Only two attributes are routinely preserved: names and dimension.
+
+## Names
+
+~~Three~~ Four ways to name:
+
+:::: columns
+
+::: {.column width="50%"}
+```{r names}
+# (1) On creation: 
+x <- c(A = 1, B = 2, C = 3)
+x
+
+# (2) Assign to names():
+y <- 1:3
+names(y) <- c("a", "b", "c")
+y
+
+# (3) Inline:
+z <- setNames(1:3, c("a", "b", "c"))
+z
+```
+:::
+
+::: {.column width="50%"}
+ 
+:::
+
+::::
+
+## rlang Names
+
+:::: columns
+
+::: {.column width="50%"}
+
+```{r names_via_rlang}
+# (4) Inline with {rlang}:
+a <- 1:3
+rlang::set_names(
+  a,
+  c("a", "b", "c")
+)
+```
+
+:::
+
+::: {.column width="50%"}
+ 
+:::
+
+::::
+
+
+## Removing names
+
+* `x <- unname(x)` or `names(x) <- NULL`
+* Thematically but not directly related: labelled class vectors with `haven::labelled()`
+
+
+## Dimensions: `matrix()` and `array()`
+
+```{r dimensions}
+# Two scalar arguments specify row and column sizes
+x <- matrix(1:6, nrow = 2, ncol = 3)
+x
+# One vector argument to describe all dimensions
+y <- array(1:12, c(2, 3, 2)) # rows, columns, no of arrays
+y
+```
+
+## Dimensions: assign to `dim()`
+
+```{r dimensions2}
+# You can also modify an object in place by setting dim()
+z <- 1:6
+dim(z) <- c(2, 3) # rows, columns
+z
+a <- 1:12
+dim(a) <- c(2, 3, 2) # rows, columns, no of arrays
+a
+```
+
+
+## Functions for working with vectors, matrices and arrays (1/2):
+
+Vector | Matrix	| Array
+:----- | :---------- | :-----
+`names()` | `rownames()`, `colnames()` | `dimnames()`
+`length()` | `nrow()`, `ncol()` | `dim()`
+`c()` | `rbind()`, `cbind()` | `abind::abind()`
+— | `t()` | `aperm()`
+`is.null(dim(x))` | `is.matrix()` | `is.array()`
+
+* **Caution**: Vector without `dim` set has `NULL` dimensions, not `1`.
+* One dimension?
+
+## Functions for working with vectors, matrices and arrays (2/2):
+
+```{r examples_of_1D, eval = FALSE}
+str(1:3)                   # 1d vector
+#>  int [1:3] 1 2 3
+str(matrix(1:3, ncol = 1)) # column vector
+#>  int [1:3, 1] 1 2 3
+str(matrix(1:3, nrow = 1)) # row vector
+#>  int [1, 1:3] 1 2 3
+str(array(1:3, 3))         # "array" vector
+#>  int [1:3(1d)] 1 2 3
+```
+
+
+## Exercises 1/4
+
+1. How is `setNames()` implemented? Read the source code.
+
+<details><summary>Answer(s)</summary>
+
+```{r, eval = FALSE}
+setNames <- function(object = nm, nm) {
+  names(object) <- nm
+  object
+}
+```
+
+- Data arg 1st = works well with pipe.
+- 1st arg is optional
+
+```{r, eval = FALSE}
+setNames( , c("a", "b", "c"))
+#>   a   b   c 
+#> "a" "b" "c"
+```
+</details>
+
+## Exercises 1/4 (cont)
+
+1. How is `unname()` implemented? Read the source code.
+
+<details><summary>Answer(s)</summary>
+
+```{r, eval = FALSE}
+unname <- function(obj, force = FALSE) {
+  if (!is.null(names(obj))) 
+    names(obj) <- NULL
+  if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) 
+    dimnames(obj) <- NULL
+  obj
+}
+```
+`unname()` sets existing `names` or `dimnames` to `NULL`.
+</details>
+
+## Exercises 2/4
+
+2. What does `dim()` return when applied to a 1-dimensional vector? When might you use `NROW()` or `NCOL()`?
+
+<details><summary>Answer(s)</summary>
+
+> `dim()` returns `NULL` when applied to a 1d vector.
+
+`NROW()` and `NCOL()` treats `NULL` and vectors like they have dimensions:
+
+```{r, eval = FALSE}
+x <- 1:10
+nrow(x)
+#> NULL
+ncol(x)
+#> NULL
+NROW(x)
+#> [1] 10
+NCOL(x)
+#> [1] 1
+```
+
+</details>
+
+## Exercises 3/4
+
+3. How would you describe the following three objects? What makes them different from `1:5`?
+
+```{r}
+x1 <- array(1:5, c(1, 1, 5))
+x2 <- array(1:5, c(1, 5, 1))
+x3 <- array(1:5, c(5, 1, 1))
+```
+
+<details><summary>Answer(s)</summary>
+```{r, eval = FALSE}
+x1 <- array(1:5, c(1, 1, 5))  # 1 row,  1 column,  5 in third dim.
+x2 <- array(1:5, c(1, 5, 1))  # 1 row,  5 columns, 1 in third dim.
+x3 <- array(1:5, c(5, 1, 1))  # 5 rows, 1 column,  1 in third dim.
+```
+</details>
+
+## Exercises 4/4
+
+4. An early draft used this code to illustrate `structure()`:
+
+```{r, eval = FALSE}
+structure(1:5, comment = "my attribute")
+#> [1] 1 2 3 4 5
+```
+
+Why don't you see the comment attribute on print? Is the attribute missing, or is there something else special about it?
+
+<details><summary>Answer(s)</summary>
+The documentation states (see `?comment`):
+
+> Contrary to other attributes, the comment is not printed (by print or print.default).
+
+## Exercises 4/4 (cont)
+
+<details><summary>Answer(s)</summary>
+Also, from `?attributes:`
+
+> Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set.
+
+Retrieve comment attributes with `attr()`:
+
+```{r, eval = FALSE}
+foo <- structure(1:5, comment = "my attribute")
+
+attributes(foo)
+#> $comment
+#> [1] "my attribute"
+attr(foo, which = "comment")
+#> [1] "my attribute"
+```
+
+</details>
+
+
+
+## **Class** - S3 atomic vectors
+
+ 
+
+Credit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham
+
+**Having a class attribute turns an object into an S3 object.**
+
+What makes S3 atomic vectors different?
+
+1. behave differently from a regular vector when passed to a generic function 
+2. often store additional information in other attributes
+
+
+## Four important S3 vectors used in base R:
+
+1. **Factors** (categorical data)
+2. **Dates**
+3. **Date-times** (POSIXct)
+4. **Durations** (difftime)
+
+## Factors
+
+A factor is a vector used to store categorical data that can contain only predefined values.
+
+Factors are integer vectors with:
+
+-   Class: "factor"
+-   Attributes: "levels", or the set of allowed values
+
+## Factors examples
+
+```{r factor}
+colors = c('red', 'blue', 'green','red','red', 'green')
+colors_factor <- factor(
+  x = colors, levels = c('red', 'blue', 'green', 'yellow')
+)
+```
+
+:::: columns
+
+::: column
+
+```{r factor_table}
+table(colors)
+table(colors_factor)
+```
+:::
+
+::: column
+```{r factor_type}
+typeof(colors_factor)
+class(colors_factor)
+
+attributes(colors_factor)
+```
+:::
+::::
+
+## Custom Order
+
+Factors can be ordered. This can be useful for models or visualizations where order matters.
+
+```{r factor_ordered}
+
+values <- c('high', 'med', 'low', 'med', 'high', 'low', 'med', 'high')
+ordered_factor <- ordered(
+  x = values,
+  levels = c('low', 'med', 'high') # in order
+)
+ordered_factor
+
+table(values)
+table(ordered_factor)
+```
+
+## Dates
+
+Dates are:
+
+-   Double vectors
+-   With class "Date"
+-   No other attributes
+
+```{r dates}
+notes_date <- Sys.Date()
+
+# type
+typeof(notes_date)
+
+# class
+attributes(notes_date)
+```
+
+## Dates Unix epoch
+
+The double component represents the number of days since since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time) `1970-01-01`
+
+```{r days_since_1970}
+date <- as.Date("1970-02-01")
+unclass(date)
+```
+
+## Date-times
+
+There are 2 Date-time representations in base R:
+
+-   POSIXct, where "ct" denotes *calendar time*
+-   POSIXlt, where "lt" designates *local time*
+
+<!--
+
+Just for fun:
+"How to pronounce 'POSIXct'?"
+https://www.howtopronounce.com/posixct
+
+-->
+
+## Dates-times: POSIXct
+
+We'll focus on POSIXct because:
+
+-   Simplest
+-   Built on an atomic (double) vector
+-   Most appropriate for use in a data frame
+
+Let's now build and deconstruct a Date-time
+
+```{r date_time}
+# Build
+note_date_time <- as.POSIXct(
+  x = Sys.time(), # time
+  tz = "America/New_York" # time zone, used only for formatting
+)
+
+# Inspect
+note_date_time
+
+# - type
+typeof(note_date_time)
+
+# - attributes
+attributes(note_date_time)
+
+structure(note_date_time, tzone = "Europe/Paris")
+```
+
+```{r date_time_format}
+date_time <- as.POSIXct("2024-02-22 12:34:56", tz = "EST")
+unclass(date_time)
+```
+
+
+## Durations
+
+Durations represent the amount of time between pairs of dates or date-times.
+
+-   Double vectors
+-   Class: "difftime"
+-   Attributes: "units", or the unit of duration (e.g., weeks, hours, minutes, seconds, etc.)
+
+```{r durations}
+# Construct
+one_minute <- as.difftime(1, units = "mins")
+# Inspect
+one_minute
+
+# Dissect
+# - type
+typeof(one_minute)
+# - attributes
+attributes(one_minute)
+```
+
+```{r durations_math}
+time_since_01_01_1970 <- notes_date - date
+time_since_01_01_1970
+```
+
+
+See also:
+
+-   [`lubridate::make_difftime()`](https://lubridate.tidyverse.org/reference/make_difftime.html)
+-   [`clock::date_time_build()`](https://clock.r-lib.org/reference/date_time_build.html)
+
+
+## Exercises 1/3
+
+1. What sort of object does `table()` return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables?
+
+<details><summary>Answer(s)</summary>
+
+`table()` returns a contingency table of its input variables. It is implemented as an integer vector with class table and dimensions (which makes it act like an array). Its attributes are dim (dimensions) and dimnames (one name for each input column). The dimensions correspond to the number of unique values (factor levels) in each input variable.
+
+```{r, eval = FALSE}
+x <- table(mtcars[c("vs", "cyl", "am")])
+
+typeof(x)
+#> [1] "integer"
+attributes(x)
+#> $dim
+#> [1] 2 3 2
+#> 
+#> $dimnames
+#> $dimnames$vs
+#> [1] "0" "1"
+#> 
+#> $dimnames$cyl
+#> [1] "4" "6" "8"
+#> 
+#> $dimnames$am
+#> [1] "0" "1"
+#> 
+#> 
+#> $class
+#> [1] "table"
+```
+</details>
+
+## Exercises 2/3
+
+2. What happens to a factor when you modify its levels?
+
+```{r, eval = FALSE}
+f1 <- factor(letters)
+levels(f1) <- rev(levels(f1))
+```
+
+<details><summary>Answer(s)</summary>
+The underlying integer values stay the same, but the levels are changed, making it look like the data has changed.
+
+```{r, eval = FALSE}
+f1 <- factor(letters)
+f1
+#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
+#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
+as.integer(f1)
+#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+#> [26] 26
+
+levels(f1) <- rev(levels(f1))
+f1
+#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
+#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
+as.integer(f1)
+#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+#> [26] 26
+```
+</details>
+
+## Exercises 3/3
+
+3. What does this code do? How do `f2` and `f3` differ from `f1`?
+
+```{r, eval = FALSE}
+f2 <- rev(factor(letters))
+f3 <- factor(letters, levels = rev(letters))
+```
+
+<details><summary>Answer(s)</summary>
+For `f2` and `f3` either the order of the factor elements or its levels are being reversed. For `f1` both transformations are occurring.
+
+```{r, eval = FALSE}
+# Reverse element order
+(f2 <- rev(factor(letters)))
+#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
+#> Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
+as.integer(f2)
+#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2
+#> [26]  1
+
+# Reverse factor levels (when creating factor)
+(f3 <- factor(letters, levels = rev(letters)))
+#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
+#> Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
+as.integer(f3)
+#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2
+#> [26]  1
+```
+</details>
+
+
+## Lists
+
+* sometimes called a generic vector or recursive vector
+* Recall ([section 2.3.3](https://adv-r.hadley.nz/names-values.html#list-references)): each element is really a *reference* to another object
+* an be composed of elements of different types (as opposed to atomic vectors which must be of only one type)
+
+## Constructing
+
+Simple lists:
+
+```{r list_simple}
+# Construct
+simple_list <- list(
+  c(TRUE, FALSE),   # logicals
+  1:20,             # integers
+  c(1.2, 2.3, 3.4), # doubles
+  c("primo", "secundo", "tercio") # characters
+)
+
+simple_list
+
+# Inspect
+# - type
+typeof(simple_list)
+# - structure
+str(simple_list)
+
+# Accessing
+simple_list[1]
+simple_list[2]
+simple_list[3]
+simple_list[4]
+
+simple_list[[1]][2]
+simple_list[[2]][8]
+simple_list[[3]][2]
+simple_list[[4]][3]
+```
+
+## Even Simpler List
+
+```{r list_simpler}
+# Construct
+simpler_list <- list(TRUE, FALSE, 
+                    1, 2, 3, 4, 5, 
+                    1.2, 2.3, 3.4, 
+                    "primo", "secundo", "tercio")
+
+# Accessing
+simpler_list[1]
+simpler_list[5]
+simpler_list[9]
+simpler_list[11]
+```
+
+## Nested lists:
+
+```{r list_nested}
+nested_list <- list(
+  # first level
+  list(
+    # second level
+    list(
+      # third level
+      list(1)
+    )
+  )
+)
+
+str(nested_list)
+```
+
+Like JSON.
+
+## Combined lists
+
+```{r list_combined}
+list_comb1 <- list(list(1, 2), list(3, 4)) # with list()
+list_comb2 <- c(list(1, 2), list(3, 4)) # with c()
+
+# compare structure
+str(list_comb1)
+str(list_comb2)
+
+# does this work if they are different data types?
+list_comb3 <- c(list(1, 2), list(TRUE, FALSE))
+str(list_comb3)
+```
+
+## Testing
+
+Check that is a list:
+
+-   `is.list()`
+-   \`rlang::is_list()\`\`
+
+The two do the same, except that the latter can check for the number of elements
+
+```{r list_test}
+# is list
+base::is.list(list_comb2)
+rlang::is_list(list_comb2)
+
+# is list of 4 elements
+rlang::is_list(x = list_comb2, n = 4)
+
+# is a vector (of a special type)
+# remember the family tree?
+rlang::is_vector(list_comb2)
+```
+
+## Coercion
+
+Use `as.list()`
+
+```{r list_coercion}
+list(1:3)
+as.list(1:3)
+```
+
+## Matrices and arrays
+
+Although not often used, the dimension attribute can be added to create **list-matrices** or **list-arrays**.
+
+```{r list_matrices_arrays}
+l <- list(1:3, "a", TRUE, 1.0)
+dim(l) <- c(2, 2); l
+
+l[[1, 1]]
+```
+
+
+## Exercises 1/3
+
+1. List all the ways that a list differs from an atomic vector.
+
+<details><summary>Answer(s)</summary>
+
+* Atomic vectors are always homogeneous (all elements must be of the same type). Lists may be heterogeneous (the elements can be of different types) as described in the introduction of the vectors chapter.
+* Atomic vectors point to one address in memory, while lists contain a separate reference for each element. (This was described in the list sections of the vectors and the names and values chapters.)
+
+```{r, eval = FALSE}
+lobstr::ref(1:2)
+#> [1:0x7fcd936f6e80] <int>
+lobstr::ref(list(1:2, 2))
+#> █ [1:0x7fcd93d53048] <list> 
+#> ├─[2:0x7fcd91377e40] <int> 
+#> └─[3:0x7fcd93b41eb0] <dbl>
+```
+
+
+* Subsetting with out-of-bounds and NA values leads to different output. For example, [ returns NA for atomics and NULL for lists. (This is described in more detail within the subsetting chapter.)
+
+```{r, eval = FALSE}
+# Subsetting atomic vectors
+(1:2)[3]
+#> [1] NA
+(1:2)[NA]
+#> [1] NA NA
+
+# Subsetting lists
+as.list(1:2)[3]
+#> [[1]]
+#> NULL
+as.list(1:2)[NA]
+#> [[1]]
+#> NULL
+#> 
+#> [[2]]
+#> NULL
+```
+
+
+</details>
+
+## Exercises 2/3
+
+2. Why do you need to use `unlist()` to convert a list to an atomic vector? Why doesn’t `as.vector()` work?
+
+<details><summary>Answer(s)</summary>
+A list is already a vector, though not an atomic one! Note that as.vector() and is.vector() use different definitions of “vector!”
+
+```{r, eval = FALSE}
+is.vector(as.vector(mtcars))
+#> [1] FALSE
+```
+
+</details>
+
+## Exercises 3/3
+
+3. Compare and contrast `c()` and `unlist()` when combining a date and date-time into a single vector.
+
+<details><summary>Answer(s)</summary>
+Date and date-time objects are both built upon doubles. While dates store the number of days since the reference date 1970-01-01 (also known as “the Epoch”) in days, date-time-objects (POSIXct) store the time difference to this date in seconds.
+
+```{r, eval = FALSE}
+date    <- as.Date("1970-01-02")
+dttm_ct <- as.POSIXct("1970-01-01 01:00", tz = "UTC")
+
+# Internal representations
+unclass(date)
+#> [1] 1
+unclass(dttm_ct)
+#> [1] 3600
+#> attr(,"tzone")
+#> [1] "UTC"
+```
+
+As the c() generic only dispatches on its first argument, combining date and date-time objects via c() could lead to surprising results in older R versions (pre R 4.0.0):
+
+```{r, eval = FALSE}
+# Output in R version 3.6.2
+c(date, dttm_ct)  # equal to c.Date(date, dttm_ct) 
+#> [1] "1970-01-02" "1979-11-10"
+c(dttm_ct, date)  # equal to c.POSIXct(date, dttm_ct)
+#> [1] "1970-01-01 02:00:00 CET" "1970-01-01 01:00:01 CET"
+```
+
+In the first statement above c.Date() is executed, which incorrectly treats the underlying double of dttm_ct (3600) as days instead of seconds. Conversely, when c.POSIXct() is called on a date, one day is counted as one second only.
+
+We can highlight these mechanics by the following code:
+
+```{r, eval = FALSE}
+# Output in R version 3.6.2
+unclass(c(date, dttm_ct))  # internal representation
+#> [1] 1 3600
+date + 3599
+#> "1979-11-10"
+```
+
+As of R 4.0.0 these issues have been resolved and both methods now convert their input first into POSIXct and Date, respectively.
+
+```{r, eval = FALSE}
+c(dttm_ct, date)
+#> [1] "1970-01-01 01:00:00 UTC" "1970-01-02 00:00:00 UTC"
+unclass(c(dttm_ct, date))
+#> [1]  3600 86400
+
+c(date, dttm_ct)
+#> [1] "1970-01-02" "1970-01-01"
+unclass(c(date, dttm_ct))
+#> [1] 1 0
+```
+
+However, as c() strips the time zone (and other attributes) of POSIXct objects, some caution is still recommended.
+
+```{r, eval = FALSE}
+(dttm_ct <- as.POSIXct("1970-01-01 01:00", tz = "HST"))
+#> [1] "1970-01-01 01:00:00 HST"
+attributes(c(dttm_ct))
+#> $class
+#> [1] "POSIXct" "POSIXt"
+```
+
+A package that deals with these kinds of problems in more depth and provides a structural solution for them is the {vctrs} package9 which is also used throughout the tidyverse.10
+
+Let’s look at unlist(), which operates on list input.
+
+```{r, eval = FALSE}
+# Attributes are stripped
+unlist(list(date, dttm_ct))  
+#> [1]     1 39600
+```
+
+We see again that dates and date-times are internally stored as doubles. Unfortunately, this is all we are left with, when unlist strips the attributes of the list.
+
+To summarise: c() coerces types and strips time zones. Errors may have occurred in older R versions because of inappropriate method dispatch/immature methods. unlist() strips attributes.
+</details>
+
+
+## Data frames and tibbles
+
+ 
+
+Credit: [Advanced R](https://adv-r.hadley.nz/index.html) by Hadley Wickham
+
+## Data frame
+
+A data frame is a:
+
+-   Named list of vectors (i.e., column names)
+-   Attributes:
+    -   (column) `names`
+    -   `row.names`
+    -   Class: "data frame"
+
+## Data frame, examples 1/2:
+
+```{r data_frame}
+# Construct
+df <- data.frame(
+  col1 = c(1, 2, 3),              # named atomic vector
+  col2 = c("un", "deux", "trois") # another named atomic vector
+  # ,stringsAsFactors = FALSE # default for versions after R 4.1
+)
+# Inspect
+df
+
+# Deconstruct
+# - type
+typeof(df)
+# - attributes
+attributes(df)
+```
+
+
+## Data frame, examples 2/2:
+
+```{r df_functions}
+rownames(df)
+colnames(df)
+names(df) # Same as colnames(df)
+
+nrow(df) 
+ncol(df)
+length(df) # Same as ncol(df)
+```
+
+Unlike other lists, the length of each vector must be the same (i.e. as many vector elements as rows in the data frame).
+
+## Tibble
+
+Created to relieve some of the frustrations and pain points created by data frames, tibbles are data frames that are:
+
+-   Lazy (do less)
+-   Surly (complain more)
+
+## Lazy
+
+Tibbles do not:
+
+-   Coerce strings
+-   Transform non-syntactic names
+-   Recycle vectors of length greater than 1
+
+## ! Coerce strings
+
+```{r tbl_no_coerce}
+chr_col <- c("don't", "factor", "me", "bro")
+
+# data frame
+df <- data.frame(
+  a = chr_col,
+  # in R 4.1 and earlier, this was the default
+  stringsAsFactors = TRUE
+)
+
+# tibble
+tbl <- tibble::tibble(
+  a = chr_col
+)
+
+# contrast the structure
+str(df$a)
+str(tbl$a)
+
+```
+
+## ! Transform non-syntactic names
+
+```{r tbl_col_name}
+# data frame
+df <- data.frame(
+  `1` = c(1, 2, 3)
+)
+
+# tibble
+tbl <- tibble::tibble(
+  `1` = c(1, 2, 3)
+)
+
+# contrast the names
+names(df)
+names(tbl)
+```
+
+## ! Recycle vectors of length greater than 1
+
+```{r tbl_recycle, error=TRUE}
+# data frame
+df <- data.frame(
+  col1 = c(1, 2, 3, 4),
+  col2 = c(1, 2)
+)
+
+# tibble
+tbl <- tibble::tibble(
+  col1 = c(1, 2, 3, 4),
+  col2 = c(1, 2)
+)
+```
+
+## Surly
+
+Tibbles do only what they're asked and complain if what they're asked doesn't make sense:
+
+-   Subsetting always yields a tibble
+-   Complains if cannot find column
+
+## Subsetting always yields a tibble
+
+```{r tbl_subset}
+# data frame
+df <- data.frame(
+  col1 = c(1, 2, 3, 4)
+)
+
+# tibble
+tbl <- tibble::tibble(
+  col1 = c(1, 2, 3, 4)
+)
+
+# contrast
+df_col <- df[, "col1"]
+str(df_col)
+tbl_col <- tbl[, "col1"]
+str(tbl_col)
+
+# to select a vector, do one of these instead
+tbl_col_1 <- tbl[["col1"]]
+str(tbl_col_1)
+tbl_col_2 <- dplyr::pull(tbl, col1)
+str(tbl_col_2)
+```
+
+## Complains if cannot find column
+
+```{r tbl_col_match, warning=TRUE}
+names(df)
+df$col
+
+names(tbl)
+tbl$col
+```
+
+## One more difference
+
+**`tibble()` allows you to refer to variables created during construction**
+
+```{r df_tibble_diff}
+tibble::tibble(
+  x = 1:3,
+  y = x * 2 # x refers to the line above
+)
+```
+
+<details>
+<summary>Side Quest: Row Names</summary>
+
+- character vector containing only unique values
+- get and set with `rownames()`
+- can use them to subset rows
+
+```{r row_names}
+df3 <- data.frame(
+  age = c(35, 27, 18),
+  hair = c("blond", "brown", "black"),
+  row.names = c("Bob", "Susan", "Sam")
+)
+df3
+
+rownames(df3)
+df3["Bob", ]
+
+rownames(df3) <- c("Susan", "Bob", "Sam")
+rownames(df3)
+df3["Bob", ]
+```
+
+There are three reasons why row names are undesirable:
+
+3. Metadata is data, so storing it in a different way to the rest of the data is fundamentally a bad idea. 
+2. Row names are a poor abstraction for labelling rows because they only work when a row can be identified by a single string. This fails in many cases.
+3. Row names must be unique, so any duplication of rows (e.g. from bootstrapping) will create new row names.
+
+</details>
+
+
+## Tibles: Printing
+
+Data frames and tibbles print differently
+
+```{r df_tibble_print}
+df3
+tibble::as_tibble(df3)
+```
+
+
+## Tibles: Subsetting
+
+Two undesirable subsetting behaviours:
+
+1. When you subset columns with `df[, vars]`, you will get a vector if vars selects one variable, otherwise you’ll get a data frame, unless you always remember to use `df[, vars, drop = FALSE]`.
+2. When you attempt to extract a single column with `df$x` and there is no column `x`, a data frame will instead select any variable that starts with `x`. If no variable starts with `x`, `df$x` will return NULL.
+
+Tibbles tweak these behaviours so that a [ always returns a tibble, and a $ doesn’t do partial matching and warns if it can’t find a variable (*this is what makes tibbles surly*).
+
+## Tibles: Testing
+
+Whether data frame: `is.data.frame()`. Note: both data frame and tibble are data frames.
+
+Whether tibble: `tibble::is_tibble`. Note: only tibbles are tibbles. Vanilla data frames are not.
+
+## Tibles: Coercion
+
+-   To data frame: `as.data.frame()`
+-   To tibble: `tibble::as_tibble()`
+
+## Tibles: List Columns
+
+List-columns are allowed in data frames but you have to do a little extra work by either adding the list-column after creation or wrapping the list in `I()`
+
+```{r list_columns}
+df4 <- data.frame(x = 1:3)
+df4$y <- list(1:2, 1:3, 1:4)
+df4
+
+df5 <- data.frame(
+  x = 1:3, 
+  y = I(list(1:2, 1:3, 1:4))
+)
+df5
+```
+
+## Tibbles: Matrix and data frame columns
+
+- As long as the number of rows matches the data frame, it’s also possible to have a matrix or data frame as a column of a data frame.
+- same as list-columns, must either addi the list-column after creation or wrapping the list in `I()`
+
+```{r matrix_df_columns}
+dfm <- data.frame(
+  x = 1:3 * 10,
+  y = I(matrix(1:9, nrow = 3))
+)
+
+dfm$z <- data.frame(a = 3:1, b = letters[1:3], stringsAsFactors = FALSE)
+
+str(dfm)
+dfm$y
+dfm$z
+```
+
+
+## Exercises 1/4
+
+1. Can you have a data frame with zero rows? What about zero columns?
+
+<details><summary>Answer(s)</summary>
+Yes, you can create these data frames easily; either during creation or via subsetting. Even both dimensions can be zero. Create a 0-row, 0-column, or an empty data frame directly:
+
+```{r, eval = FALSE}
+data.frame(a = integer(), b = logical())
+#> [1] a b
+#> <0 rows> (or 0-length row.names)
+
+data.frame(row.names = 1:3)  # or data.frame()[1:3, ]
+#> data frame with 0 columns and 3 rows
+
+data.frame()
+#> data frame with 0 columns and 0 rows
+```
+
+Create similar data frames via subsetting the respective dimension with either 0, `NULL`, `FALSE` or a valid 0-length atomic (`logical(0)`, `character(0)`, `integer(0)`, `double(0)`). Negative integer sequences would also work. The following example uses a zero:
+
+```{r, eval = FALSE}
+mtcars[0, ]
+#>  [1] mpg  cyl  disp hp   drat wt   qsec vs   am   gear carb
+#> <0 rows> (or 0-length row.names)
+
+mtcars[ , 0]  # or mtcars[0]
+#> data frame with 0 columns and 32 rows
+
+mtcars[0, 0]
+#> data frame with 0 columns and 0 rows
+```
+
+
+</details>
+
+## Exercises 2/4
+
+2. What happens if you attempt to set rownames that are not unique?
+
+<details><summary>Answer(s)</summary>
+Matrices can have duplicated row names, so this does not cause problems.
+
+Data frames, however, require unique rownames and you get different results depending on how you attempt to set them. If you set them directly or via `row.names()`, you get an error:
+
+```{r, eval = FALSE}
+data.frame(row.names = c("x", "y", "y"))
+#> Error in data.frame(row.names = c("x", "y", "y")): duplicate row.names: y
+
+df <- data.frame(x = 1:3)
+row.names(df) <- c("x", "y", "y")
+#> Warning: non-unique value when setting 'row.names': 'y'
+#> Error in `.rowNamesDF<-`(x, value = value): duplicate 'row.names' are not allowed
+```
+
+If you use subsetting, `[` automatically deduplicates:
+
+```{r, eval = FALSE}
+row.names(df) <- c("x", "y", "z")
+df[c(1, 1, 1), , drop = FALSE]
+#>     x
+#> x   1
+#> x.1 1
+#> x.2 1
+```
+
+</details>
+
+## Exercises 3/4
+
+3. If `df` is a data frame, what can you say about `t(df)`, and `t(t(df))`? Perform some experiments, making sure to try different column types.
+
+<details><summary>Answer(s)</summary>
+Both of `t(df)` and `t(t(df))` will return matrices:
+
+```{r, eval = FALSE}
+df <- data.frame(x = 1:3, y = letters[1:3])
+is.matrix(df)
+#> [1] FALSE
+is.matrix(t(df))
+#> [1] TRUE
+is.matrix(t(t(df)))
+#> [1] TRUE
+```
+
+The dimensions will respect the typical transposition rules:
+
+```{r, eval = FALSE}
+dim(df)
+#> [1] 3 2
+dim(t(df))
+#> [1] 2 3
+dim(t(t(df)))
+#> [1] 3 2
+```
+
+Because the output is a matrix, every column is coerced to the same type. (It is implemented within `t.data.frame()` via `as.matrix()` which is described below).
+
+```{r, eval = FALSE}
+df
+#>   x y
+#> 1 1 a
+#> 2 2 b
+#> 3 3 c
+t(df)
+#>   [,1] [,2] [,3]
+#> x "1"  "2"  "3" 
+#> y "a"  "b"  "c"
+```
+
+</details>
+
+## Exercises 4/4
+
+4. What does `as.matrix()` do when applied to a data frame with columns of different types? How does it differ from `data.matrix()`?
+
+<details><summary>Answer(s)</summary>
+The type of the result of as.matrix depends on the types of the input columns (see `?as.matrix`):
+
+> The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g. all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.
+
+On the other hand, `data.matrix` will always return a numeric matrix (see `?data.matrix()`).
+
+> Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. […] Character columns are first converted to factors and then to integers.
+
+We can illustrate and compare the mechanics of these functions using a concrete example. `as.matrix()` makes it possible to retrieve most of the original information from the data frame but leaves us with characters. To retrieve all information from `data.matrix()`’s output, we would need a lookup table for each column.
+
+```{r, eval = FALSE}
+df_coltypes <- data.frame(
+  a = c("a", "b"),
+  b = c(TRUE, FALSE),
+  c = c(1L, 0L),
+  d = c(1.5, 2),
+  e = factor(c("f1", "f2"))
+)
+
+as.matrix(df_coltypes)
+#>      a   b       c   d     e   
+#> [1,] "a" "TRUE"  "1" "1.5" "f1"
+#> [2,] "b" "FALSE" "0" "2.0" "f2"
+data.matrix(df_coltypes)
+#>      a b c   d e
+#> [1,] 1 1 1 1.5 1
+#> [2,] 2 0 0 2.0 2
+```
+
+</details>
+
+
+## `NULL`
+
+Special type of object that:
+
+-   Length 0
+-   Cannot have attributes
+
+```{r null, results = 'hide'}
+typeof(NULL)
+#> [1] "NULL"
+
+length(NULL)
+#> [1] 0
+```
+
+```{r null_attr, error=TRUE}
+x <- NULL
+attr(x, "y") <- 1
+```
+
+```{r null_check}
+is.null(NULL)
+```
+
+
+## Digestif
+
+Let is use some of this chapter's skills on the `penguins` data.
+
+## Attributes
+
+```{r}
+str(penguins_raw)
+```
+
+```{r}
+str(penguins_raw, give.attr = FALSE)
+```
+
+## Data Frames vs Tibbles
+
+```{r}
+penguins_df <- data.frame(penguins)
+penguins_tb <- penguins #i.e. penguins was already a tibble
+```
+
+## Printing
+
+* Tip: print out these results in RStudio under different editor themes
+
+```{r, eval = FALSE}
+print(penguins_df) #don't run this
+```
+
+```{r}
+head(penguins_df)
+```
+
+```{r}
+penguins_tb
+```
+
+## Atomic Vectors
+
+```{r}
+species_vector_df <- penguins_df |> select(species)
+species_unlist_df <- penguins_df |> select(species) |> unlist()
+species_pull_df   <- penguins_df |> select(species) |> pull()
+
+species_vector_tb <- penguins_tb |> select(species)
+species_unlist_tb <- penguins_tb |> select(species) |> unlist()
+species_pull_tb   <- penguins_tb |> select(species) |> pull()
+```
+
+<details>
+<summary>`typeof()` and `class()`</summary>
+```{r}
+typeof(species_vector_df)
+class(species_vector_df)
+
+typeof(species_unlist_df)
+class(species_unlist_df)
+
+typeof(species_pull_df)
+class(species_pull_df)
+
+typeof(species_vector_tb)
+class(species_vector_tb)
+
+typeof(species_unlist_tb)
+class(species_unlist_tb)
+
+typeof(species_pull_tb)
+class(species_pull_tb)
+```
+
+</details>
+
+## Column Names
+
+```{r}
+colnames(penguins_tb)
+```
+
+```{r}
+names(penguins_tb) == colnames(penguins_tb)
+```
+
+```{r}
+names(penguins_df) == names(penguins_tb)
+```
+
+## What if we only invoke a partial name of a column of a tibble?
+
+```{r, error = TRUE}
+penguins_tb$y 
+```
+
+
+
+* What if we only invoke a partial name of a column of a data frame?
+
+```{r}
+head(penguins_df$y) #instead of `year`
+```
+
+* Is this evaluation in alphabetical order or column order?
+
+```{r}
+penguins_df_se_sp <- penguins_df |> select(sex, species)
+penguins_df_sp_se <- penguins_df |> select(species, sex)
+```
+
+```{r}
+head(penguins_df_se_sp$s)
+```
+
+```{r}
+head(penguins_df_sp_se$s)
+```
+
+
+## Chapter Quiz 1/5
+
+1. What are the four common types of atomic vectors? What are the two rare types?
+
+<details><summary>Answer(s)</summary>
+The four common types of atomic vector are logical, integer, double and character. The two rarer types are complex and raw.
+</details>
+
+## Chapter Quiz 2/5
+
+2. What are attributes? How do you get them and set them?
+
+<details><summary>Answer(s)</summary>
+Attributes allow you to associate arbitrary additional metadata to any object. You can get and set individual attributes with `attr(x, "y")` and `attr(x, "y") <- value`; or you can get and set all attributes at once with `attributes()`.
+</details>
+
+## Chapter Quiz 3/5
+
+3. How is a list different from an atomic vector? How is a matrix different from a data frame?
+
+<details><summary>Answer(s)</summary>
+The elements of a list can be any type (even a list); the elements of an atomic vector are all of the same type. Similarly, every element of a matrix must be the same type; in a data frame, different columns can have different types.
+</details>
+
+## Chapter Quiz 4/5
+
+4. Can you have a list that is a matrix? Can a data frame have a column that is a matrix?
+
+<details><summary>Answer(s)</summary>
+You can make a list-array by assigning dimensions to a list. You can make a matrix a column of a data frame with `df$x <- matrix()`, or by using `I()` when creating a new data frame `data.frame(x = I(matrix()))`.
+</details>
+
+## Chapter Quiz 5/5
+
+5. How do tibbles behave differently from data frames?
+
+<details><summary>Answer(s)</summary>
+Tibbles have an enhanced print method, never coerce strings to factors, and provide stricter subsetting methods.
+</details>
diff --git a/slides/04.qmd b/slides/04.qmd
@@ -195,9 +195,9 @@ tbl[, 1]
 
 ## `[[` selects a single element
 
-:::: {.columns}
+:::: columns
 
-::: {.column}
+::: column
 ```{r}
 x <- list(1:3, "a", 4:6)
 x[1]
@@ -208,7 +208,7 @@ x[[1]][[1]]
 ```
 :::
 
-::: {.column}
+::: column
 
 
 :::
diff --git a/slides/_metadata.yml b/slides/_metadata.yml
@@ -5,6 +5,7 @@ format:
     link-external-newwindow: true
     transition: slide
     incremental: false
+    scrollable: true
 execute:
   eval: true
   echo: true
diff --git a/videos/01/08.qmd b/videos/01/08.qmd
@@ -0,0 +1,21 @@
+---
+title: Cohort 8
+---
+
+{{< video https://www.youtube.com/embed/Eh3X4ixdM2g >}}
+
+<details>
+
+<summary>Meeting chat log</summary>
+```
+00:11:53	Federica Gazzelloni (she/her):	start
+00:16:39	bdair:	Hello Just listening for now happy to be here
+00:18:21	Betsy:	Reacted to "Hello Just listening..." with 👍
+00:24:52	Federica Gazzelloni (she/her):	https://r4ds.github.io/bookclub-advr/introduction.html
+00:25:02	Federica Gazzelloni (she/her):	https://adv-r.hadley.nz/index.html
+00:28:57	Federica Gazzelloni (she/her):	https://docs.google.com/spreadsheets/d/1loyBrLdfbBl2NDc-eoXKTJSnaQKm5Z-D_X1hx_GF8Q8/edit
+00:35:40	Federica Gazzelloni (she/her):	https://advanced-r-solutions.rbind.io/
+00:53:25	Silvana Acosta:	You are on mute Federica
+01:06:31	Betsy:	https://adv-r.hadley.nz/names-values.html#copy-on-modify
+```
+</details>