class: center middle bg-navy white .pull-left[ <img style="border-radius: 50%;" src="assets/undraw/undraw_Hiking_re_k0bc.png"/> ] .pull-right[ .title[ # This Month I Learned... <https://cwickham.github.io/this-month-I-learned/> ] Charlotte Wickham @ Rladies DC, April 2021 ] --- class: middle .left-column[ <img style="border-radius: 50%;" src="assets/headshot_2019.jpg" width="150px"/> [<svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;fill:#F44B70;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> @cwickham](https://github.com/cwickham) [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;fill:#F44B70;" xmlns="http://www.w3.org/2000/svg"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg> @cvwickham](https://twitter.com/cvwickham) [cwick.co.nz](https://cwick.co.nz) ] .right-column[ ## Hello, my name is ## .navy[Charlotte Wickham] * Part-time @ Oregon State University, Statistics Department * Independent R Trainer and Consultant ] --- class: middle center .pull-left[ # Everyone is Still Learning ] .pull-right[ <img style="border-radius: 50%;" src="assets/undraw/undraw_community_8nwl.png"/> ] --- #### The way I learn -- ## 1. Do an example with guidance -- ## 2. Apply it to something I care about -- ## 3. Teach it to someone else -- --- class: middle .pull-left[ ### But this all requires ## 0\. I Know it Exists ] .pull-right[ ![](assets/undraw/undraw_map_1r69.png) ] --- ## Things I Learned ~~This Month~~ in January -- * Using Visual Editor Mode in RStudio -- * Add a Snippet to RStudio -- * Raw Character Strings -- * Storing GitHub Credentials (better) -- * `dplyr::rowwise` -- #### You get: exposure, and ideas for exposure #### I get: to teach --- layout: false class: inverse center middle # Visual Editor Mode in RStudio --- .pull-left[ ## Visual Editor Mode #### Activate in any RMarkdown file ![](assets/visual-editor-button-zoom.png) ] .pull-right[ ![](assets/visual-editor-button.png) ] --- .pull-left[ ## Visual Editor Mode #### What You See is What You Get ] .pull-right[ ![](assets/visual-editor-mode.png) ] --- class: middle .pull-left[ ## One Reason To Try It: Tables! ] .pull-right[ <video width="400" height="400" controls> <source src="assets/visual-editor-tables.mp4" type="video/mp4"> </video> ] --- ## Learning Journey .pull-left[ I saw a tweet during `rstudio::global(2021)`. I had no idea what they were talking about. New in RStudio 1.4. More at: https://rstudio.github.io/visual-markdown-editing ] .pull-right[ ![](assets/visual-editor-tweet.png) ] --- class: inverse center middle # Add a Snippet to RStudio --- ## Snippets Add Chunks of Code .pull-left[ #### Insert a Snippet 1. Type a keyword 2. Hit Shift + Tab 3. Hit Tab to cycle through placeholders #### Example: `shinyapp` ] .pull-right[ <video width="400" height="400" controls><source src="assets/snippet-shinyapp.mp4" type="video/mp4"></video> ] --- ## Snippets Add Chunks of Code .pull-left[ #### Insert a Snippet 1. Type a keyword 2. Hit Shift + Tab 3. Hit Tab to cycle through placeholders #### Example: `fun` ] .pull-right[ <video width="400" height="400" controls> <source src="assets/snippet-fun.mp4" type="video/mp4"> </video> ] --- .pull-left[ ## Add Your Own Snippet #### Tools -> Global Options ] .pull-right[ ![](assets/snippets-edit.png) ] --- .pull-left[ ## Add Your Own Snippet #### Edit Snippets snippet tidy ```{r setup, message = FALSE} library(tidyverse) ``` ] .pull-right[ ![](assets/snippets-editor.png) ] --- .pull-left[ ## Add Your Own Snippet #### Try it Out 1. Open new Rmarkdown file 2. Type `tidy` 3. Hit Shift + Tab ] .middle[.pull-right[ <video width="400" height="400" controls> <source src="assets/snippet-tidy.mp4" type="video/mp4"> </video> ]] --- ## Learning Journey I'd seen recommendations to use snippets in other learning resources: * `shinyapp` in [Mastering Shiny](https://mastering-shiny.org/basic-app.html#create-app) * `fun` in ? I recognized one day a use case for myself Led me to: https://support.rstudio.com/hc/en-us/articles/204463668-Code-Snippets --- class: inverse center middle # Raw Strings --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ] --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ```r 'Or single "quotes"' ``` ``` ## [1] "Or single \"quotes\"" ``` ] --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ```r 'Or single "quotes"' ``` ``` ## [1] "Or single \"quotes\"" ``` Slashes signify an escape, so if you need a literal `\`, you need to escape it: ```r writeLines("One: \\, Two: \\\\") ``` ``` ## One: \, Two: \\ ``` ] --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ```r 'Or single "quotes"' ``` ``` ## [1] "Or single \"quotes\"" ``` Slashes signify an escape, so if you need a literal `\`, you need to escape it: ```r writeLines("One: \\, Two: \\\\") ``` ``` ## One: \, Two: \\ ``` ] .pull-right[ ## Raw Strings ```r r"(Defined with special syntax)" ``` ``` ## [1] "Defined with special syntax" ``` ] --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ```r 'Or single "quotes"' ``` ``` ## [1] "Or single \"quotes\"" ``` Slashes signify an escape, so if you need a literal `\`, you need to escape it: ```r writeLines("One: \\, Two: \\\\") ``` ``` ## One: \, Two: \\ ``` ] .pull-right[ ## Raw Strings ```r r"(Defined with special syntax)" ``` ``` ## [1] "Defined with special syntax" ``` ```r r"(Or single "quotes")" ``` ``` ## [1] "Or single \"quotes\"" ``` ] --- .pull-left[ ## Character Strings ```r "Defined with double quotes" ``` ``` ## [1] "Defined with double quotes" ``` ```r 'Or single "quotes"' ``` ``` ## [1] "Or single \"quotes\"" ``` Slashes signify an escape, so if you need a literal `\`, you need to escape it: ```r writeLines("One: \\, Two: \\\\") ``` ``` ## One: \, Two: \\ ``` ] .pull-right[ ## Raw Strings ```r r"(Defined with special syntax)" ``` ``` ## [1] "Defined with special syntax" ``` ```r r"(Or single "quotes")" ``` ``` ## [1] "Or single \"quotes\"" ``` Everything inside the delimiter, `"(`, is taken literally ```r writeLines(r"(One: \, Two: \\)") ``` ``` ## One: \, Two: \\ ``` ] --- layout:true ## Copy a Regular Expression --- [How to validate an email address using a regular expression?](https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression) - Stack Overflow Copy this long messy regular expression: ```txt (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]) ``` -- Straight into a raw string: ```r email_pattern <- r"((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))" email_pattern ``` ``` ## [1] "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" ``` --- ```r stringr::str_detect( c("cwickham@gmail.com", "A-malformed.address"), email_pattern ) ``` ``` ## [1] TRUE FALSE ``` -- ### Other details `r"{}"`, `r"[]"`, or `r"-()-"` (with any number of dashes) `?Quotes` --- layout: false ## Learning Journey New in R 4.0.0 - So I could have read about it in NEWS, but I hadn't. Tweet: someone mentioned native pipes coming in R Led me to [keynotes at useR!2020](https://user2020.r-project.org/program/agenda/) Ended up watching [New Developments in R 4.0.0 and Beyond (Part 1 and Part 2)](https://youtu.be/X_eDHNVceCU) --- class: inverse center middle # `gitcreds::gitcreds_set()` --- .pull-left[ ## Storing GitHub Credentials I had `GITHUB_PAT` stored in `.Renviron` Works great with [usethis](https://usethis.r-lib.org/) and [gh](https://gh.r-lib.org/) Downside: sensitive information stored in a plain text file ] .pull-right[ ![](assets/renviron-github-pat.png) ] --- .pull-left[ ## Storing GitHub Credentials #### A better way: `gitcreds::gitcreds_set()` Uses your operating system credential manager: * Mac: Keychain Access * Windows: Credential Manager Works seamlessly with usethis and gh ] .pull-right[ ![](assets/gitcreds-set.png) ] --- ## Learning Journey I use `usethis::git_sitrep()` a lot when working on projects I had been ignoring this for awhile: ``` x Token may be mis-scoped: 'repo' and 'user' are highly recommended scopes The 'workflow' scope is needed to manage GitHub Actions workflow files If you are troubleshooting, consider this ``` One day I decided to fix it. Led me to: https://usethis.r-lib.org/articles/articles/git-credentials.html --- class: inverse center middle # `dplyr::rowwise()` --- ### An simple table for illustration ```r library(tidyverse) simple <- tibble( group = rep(c(1, 2), each = 3), x = 1:6 ) simple ``` ``` ## # A tibble: 6 x 2 ## group x ## <dbl> <int> ## 1 1 1 ## 2 1 2 ## 3 1 3 ## 4 2 4 ## 5 2 5 ## 6 2 6 ``` --- ### Functions in `mutate()` get the whole column .pull-left[ ```r simple %>% mutate(rank = rank(x)) ``` ``` ## # A tibble: 6 x 3 ## group x rank ## <dbl> <int> <dbl> ## 1 1 1 1 ## 2 1 2 2 ## 3 1 3 3 ## 4 2 4 4 ## 5 2 5 5 ## 6 2 6 6 ``` ] .pull-right[ <center><img src="assets/simple-mutate.png" height="400px" /></center> ] --- ### Adding `group_by`, splits that column up .pull-left[ ```r simple %>% group_by(group) %>% mutate(rank = rank(x)) ``` ``` ## # A tibble: 6 x 3 ## # Groups: group [2] ## group x rank ## <dbl> <int> <dbl> ## 1 1 1 1 ## 2 1 2 2 ## 3 1 3 3 ## 4 2 4 1 ## 5 2 5 2 ## 6 2 6 3 ``` ] .pull-right[ <center><img src="assets/simple-groupby.png" height="400px" /></center> ] --- ### With `rowwise()`, one element at a time .pull-left[ ```r simple %>% rowwise() %>% mutate(rank = rank(x)) ``` ``` ## # A tibble: 6 x 3 ## # Rowwise: ## group x rank ## <dbl> <int> <dbl> ## 1 1 1 1 ## 2 1 2 1 ## 3 1 3 1 ## 4 2 4 1 ## 5 2 5 1 ## 6 2 6 1 ``` ] .pull-right[ <center><img src="assets/simple-rowwise.png" height="400px" /></center> ] --- layout:true ## Working with List Columns --- .pull-left[ ```r starwars %>% select(name, films) ``` ] .pull-right[ ``` ## # A tibble: 87 x 2 ## name films ## <chr> <list> ## 1 Luke Skywalker <chr [5]> ## 2 C-3PO <chr [6]> ## 3 R2-D2 <chr [7]> ## 4 Darth Vader <chr [4]> ## 5 Leia Organa <chr [5]> ## 6 Owen Lars <chr [3]> ## 7 Beru Whitesun lars <chr [3]> ## 8 R5-D4 <chr [1]> ## 9 Biggs Darklighter <chr [1]> ## 10 Obi-Wan Kenobi <chr [6]> ## # … with 77 more rows ``` ] --- .pull-left[ ```r starwars %>% select(name, films) %>% rowwise() %>% mutate(n_films = length(films)) ``` ] .pull-right[ ``` ## # A tibble: 87 x 3 ## # Rowwise: ## name films n_films ## <chr> <list> <int> ## 1 Luke Skywalker <chr [5]> 5 ## 2 C-3PO <chr [6]> 6 ## 3 R2-D2 <chr [7]> 7 ## 4 Darth Vader <chr [4]> 4 ## 5 Leia Organa <chr [5]> 5 ## 6 Owen Lars <chr [3]> 3 ## 7 Beru Whitesun lars <chr [3]> 3 ## 8 R5-D4 <chr [1]> 1 ## 9 Biggs Darklighter <chr [1]> 1 ## 10 Obi-Wan Kenobi <chr [6]> 6 ## # … with 77 more rows ``` ] --- layout: true ## Creating List Columns --- ```r starwars %>% filter(species %in% c("Human", "Droid")) %>% group_by(species) %>% nest() ``` ``` ## # A tibble: 2 x 2 ## # Groups: species [2] ## species data ## <chr> <list> ## 1 Human <tibble[,13] [35 × 13]> ## 2 Droid <tibble[,13] [6 × 13]> ``` --- --- ```r starwars %>% filter(species %in% c("Human", "Droid")) %>% group_by(species) %>% nest() %>% rowwise() %>% mutate( model = list(lm(height ~ mass, data = data)), slope = coefficients(model)[2] ) ``` ``` ## # A tibble: 2 x 4 ## # Rowwise: species ## species data model slope ## <chr> <list> <list> <dbl> ## 1 Human <tibble[,13] [35 × 13]> <lm> 0.382 ## 2 Droid <tibble[,13] [6 × 13]> <lm> 0.978 ``` --- layout: false ## Learning Journey I've known about `rowwise()` for awhile from [dplyr 1.0.0 previews](https://www.tidyverse.org/blog/2020/04/dplyr-1-0-0-rowwise/) I hadn't really taken time to learn about it I needed to teach iteration to novice R learners: Could I do it without using purrr? Still figuring out best way to teach... Read more at: https://dplyr.tidyverse.org/articles/rowwise.html --- class: inverse center middle # Getting Exposure --- .pull-left[ ## Read Release News * [RStudio IDE blog](https://blog.rstudio.com/categories/rstudio-ide/) * [Tidyverse blog](https://www.tidyverse.org/blog/) * [R News](https://cran.r-project.org/doc/manuals/r-release/NEWS.html) ] .pull-right[ ] --- .pull-left[ ## Read Release News * [RStudio IDE blog](https://blog.rstudio.com/categories/rstudio-ide/) * [Tidyverse blog](https://www.tidyverse.org/blog/) * [R News](https://cran.r-project.org/doc/manuals/r-release/NEWS.html) ## Conferences * Attend * Skim abstracts, track down GitHub repos * If available, watch streams or recordings ] .pull-right[ ] --- .pull-left[ ## Read Release News * [RStudio IDE blog](https://blog.rstudio.com/categories/rstudio-ide/) * [Tidyverse blog](https://www.tidyverse.org/blog/) * [R News](https://cran.r-project.org/doc/manuals/r-release/NEWS.html) ## Conferences * Attend * Skim abstracts, track down GitHub repos * If available, watch streams or recordings ] .pull-right[ ## Stay connected * [rstats twitter](https://twitter.com/search?q=%23rstats) * meetups * blogs ] --- class: inverse center middle # Final Thoughts --- class: center middle .pull-left[ ## Find what works for you For me, sporadic, intensive "R time" usually built around conferences ] .pull-right[ <img src="assets/undraw/undraw_adventure_map_hnin.png"/> ] --- class: center middle .pull-left[ ## Give yourself permission and time to learn ] .pull-right[ <img src="assets/undraw/undraw_dev_productivity_umsq.png"/> ] --- class: center middle .pull-left[ ## Pick your battles ] .pull-right[ <img src="assets/undraw/undraw_heavy_box_agqi.png"/> ] --- class: center middle bg-navy white ## .white[Thank You] <https://cwickham.github.io/this-month-I-learned/> .title[[<svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;fill:#ffffff;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> @cwickham](https://github.com/cwickham) [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;fill:#ffffff;" xmlns="http://www.w3.org/2000/svg"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg> @cvwickham](https://twitter.com/cvwickham)] .footnote[ Images: [undraw](https://undraw.co/) ]