diff --git a/.renvignore b/.renvignore new file mode 100644 index 0000000..a0dedb0 --- /dev/null +++ b/.renvignore @@ -0,0 +1,2 @@ +blogs/ +presentations/ \ No newline at end of file diff --git a/blogs/posts/2023-03-24_hotfix-with-git.qmd b/blogs/posts/2023-03-24_hotfix-with-git/index.qmd similarity index 95% rename from blogs/posts/2023-03-24_hotfix-with-git.qmd rename to blogs/posts/2023-03-24_hotfix-with-git/index.qmd index 6807262..c238c64 100644 --- a/blogs/posts/2023-03-24_hotfix-with-git.qmd +++ b/blogs/posts/2023-03-24_hotfix-with-git/index.qmd @@ -1,159 +1,165 @@ ---- -title: "Creating a hotfix with git" -author: "Tom Jemmett" -date: "2023-03-24" -categories: [git, tutorial] -knitr: - opts_chunk: - fig.height: 2.5 - echo: false - dev: png - dev.args: - bg: transparent ---- -```{r setup} -library(ggplot2) -theme_set( - theme_void() %+replace% - theme( - legend.position = "none", - panel.background = element_rect(fill = "transparent", colour = NA), - panel.grid.minor = element_blank(), - panel.grid.major = element_blank(), - plot.background = element_rect(fill = "transparent", colour = NA) - ) -) - -colours <- c( - "tag" = "#f9bf07", - "main" = "#5881c1", - "branch" = "#ec6555" -) -``` - -I recently discovered a bug in a code-base which needed to be fixed and deployed back to production A.S.A.P., but since the last release the code has moved on significantly. The history looks something a bit like: - -```{r} -data.frame( - x = 1:6, - y = c(1, 1, 1, 1, 2, 2), - g = c(1, 1, 1, 1, 1, 1), - t = c("tag", "main", "main", "main", "branch", "branch"), - l = c("v0.2.0", "main", "main", "main", "branch", "branch") -) |> - ggplot(aes(x, y, group = g)) + - geom_line() + - geom_label(aes(label = l, fill = t)) + - scale_fill_manual(values = colours) -``` - -That is, we have a tag which is the code that is currently in production (which we need to patch), a number of commits after that tag to main (which were separate branches merged via pull requests), and a current development branch. - -I need to somehow: 1. go back to the tagged release, 2. check that code out, 3. patch that code, 4. commit this change, but insert the commit before all of the new commits after the tag - -There are at least two ways that I know to do this, one would be with an [interactive rebase](https://about.gitlab.com/blog/2020/11/23/keep-git-history-clean-with-interactive-rebase/), but I used a slightly longer method, but one I feel is a little less likely to get wrong. - -Below are the step's that I took. One thing I should note is this worked well for my particular issue because the change didn't cause any merge conflicts later on. - -## Fixing my codebase - -First, we need to checkout the tag - -``` -git checkout -b hotfix v0.2.0 -``` - -This creates a new branch called `hotfix` off of the tag `v0.2.0`. - -Now that I have the code base checked out at the point I need to fix, I can make the change that is needed, and commit the change - -``` -git add [FILENAME] -git commit -m "fixes the code" -``` - -*(Obviously, I used the actual file name and gave a better commit message. I Promise 😝)* - -Now my code is fixed, I create a new tag for this "release", as well as push the code to production (this step is omitted here) - -``` -git tag v0.2.1 -m "version 0.2.0" -``` - -At this point, our history looks something like - -```{r} -#| fig.height: 2.5 -#| echo: false -data.frame( - x = c(1, 2, 2:6), - y = c(1, 2, 1, 1, 1, 2, 2), - g = c(1, 1, 2, 2, 2, 2, 2), - t = c("tag", "tag", "main", "main", "main", "branch", "branch"), - l = c("v0.2.0", "v0.2.1", "main", "main", "main", "branch", "branch") -) |> - ggplot(aes(x, y, group = g)) + - geom_line() + - geom_line( - data = data.frame(x = c(1, 2), y = c(1, 1), g = c(1, 1)) - ) + - geom_label(aes(label = l, fill = t)) + - scale_fill_manual(values = colours) -``` - -What we want to do is break the link between main and `v0.2.0`, instead attaching to`v0.2.1`. First though, I want to make sure that if I make a mistake, I'm not making it on the main branch. - -``` -git checkout main -git checkout -b apply-hotfix -``` - -Then we can fix our history using the rebase command - -``` -git rebase hotfix -``` - -What this does is it rolls back to the point where the branch that we are rebasing (`apply-hotfix`) and the `hotfix` branch both share a common commit (`v0.2.0` tag). It then applies the commits in the `hotfix` branch, before reapplying the commits from `apply-hotfix` (a.k.a. the `main` branch). - -One thing to note, if you have any merge conflicts created by your fix, then the rebase will stop and ask you to fix the merge conflicts. There is some information in the GitHub doc's for \[resolving merge conflicts after a Git rebase\]\[2\]. - -\[2\]: https://docs.github.com/en/get-started/using-git/resolving-merge-conflicts-after-a-git-rebase - -At this point, we can check that the commit history looks correct - -``` -git log v0.2.0..HEAD -``` - -If we are happy, then we can apply this to the `main` branch. I do this by renaming the `apply-hotfix` branch as `main`. First, you have to delete the `main` branch to allow us to rename the branch. - -``` -git branch -D main -git branch -m main -``` - -We also need to update the other branches to use the new main branch - -``` -git checkout branch -git rebase main -``` - -Now, we should have a history like - -```{r} -#| fig.height: 2.5 -#| echo: false -data.frame( - x = 1:7, - y = c(1, 1, 1, 1, 1, 2, 2), - g = c(1, 1, 1, 1, 1, 1, 1), - t = c("tag", "tag", "main", "main", "main", "branch", "branch"), - l = c("v0.2.0", "v0.2.1", "main", "main", "main", "branch", "branch") -) |> - ggplot(aes(x, y, group = g)) + - geom_line() + - geom_label(aes(label = l, fill = t)) + - scale_fill_manual(values = colours) -``` +--- +title: "Creating a hotfix with git" +author: "Tom Jemmett" +date: "2023-03-24" +categories: [git, tutorial] +knitr: + opts_chunk: + fig.height: 2.5 + echo: false + dev: png + dev.args: + bg: transparent +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + +```{r setup} +library(ggplot2) +theme_set( + theme_void() %+replace% + theme( + legend.position = "none", + panel.background = element_rect(fill = "transparent", colour = NA), + panel.grid.minor = element_blank(), + panel.grid.major = element_blank(), + plot.background = element_rect(fill = "transparent", colour = NA) + ) +) + +colours <- c( + "tag" = "#f9bf07", + "main" = "#5881c1", + "branch" = "#ec6555" +) +``` + +I recently discovered a bug in a code-base which needed to be fixed and deployed back to production A.S.A.P., but since the last release the code has moved on significantly. The history looks something a bit like: + +```{r} +data.frame( + x = 1:6, + y = c(1, 1, 1, 1, 2, 2), + g = c(1, 1, 1, 1, 1, 1), + t = c("tag", "main", "main", "main", "branch", "branch"), + l = c("v0.2.0", "main", "main", "main", "branch", "branch") +) |> + ggplot(aes(x, y, group = g)) + + geom_line() + + geom_label(aes(label = l, fill = t)) + + scale_fill_manual(values = colours) +``` + +That is, we have a tag which is the code that is currently in production (which we need to patch), a number of commits after that tag to main (which were separate branches merged via pull requests), and a current development branch. + +I need to somehow: 1. go back to the tagged release, 2. check that code out, 3. patch that code, 4. commit this change, but insert the commit before all of the new commits after the tag + +There are at least two ways that I know to do this, one would be with an [interactive rebase](https://about.gitlab.com/blog/2020/11/23/keep-git-history-clean-with-interactive-rebase/), but I used a slightly longer method, but one I feel is a little less likely to get wrong. + +Below are the step's that I took. One thing I should note is this worked well for my particular issue because the change didn't cause any merge conflicts later on. + +## Fixing my codebase + +First, we need to checkout the tag + +``` +git checkout -b hotfix v0.2.0 +``` + +This creates a new branch called `hotfix` off of the tag `v0.2.0`. + +Now that I have the code base checked out at the point I need to fix, I can make the change that is needed, and commit the change + +``` +git add [FILENAME] +git commit -m "fixes the code" +``` + +*(Obviously, I used the actual file name and gave a better commit message. I Promise 😝)* + +Now my code is fixed, I create a new tag for this "release", as well as push the code to production (this step is omitted here) + +``` +git tag v0.2.1 -m "version 0.2.0" +``` + +At this point, our history looks something like + +```{r} +#| fig.height: 2.5 +#| echo: false +data.frame( + x = c(1, 2, 2:6), + y = c(1, 2, 1, 1, 1, 2, 2), + g = c(1, 1, 2, 2, 2, 2, 2), + t = c("tag", "tag", "main", "main", "main", "branch", "branch"), + l = c("v0.2.0", "v0.2.1", "main", "main", "main", "branch", "branch") +) |> + ggplot(aes(x, y, group = g)) + + geom_line() + + geom_line( + data = data.frame(x = c(1, 2), y = c(1, 1), g = c(1, 1)) + ) + + geom_label(aes(label = l, fill = t)) + + scale_fill_manual(values = colours) +``` + +What we want to do is break the link between main and `v0.2.0`, instead attaching to`v0.2.1`. First though, I want to make sure that if I make a mistake, I'm not making it on the main branch. + +``` +git checkout main +git checkout -b apply-hotfix +``` + +Then we can fix our history using the rebase command + +``` +git rebase hotfix +``` + +What this does is it rolls back to the point where the branch that we are rebasing (`apply-hotfix`) and the `hotfix` branch both share a common commit (`v0.2.0` tag). It then applies the commits in the `hotfix` branch, before reapplying the commits from `apply-hotfix` (a.k.a. the `main` branch). + +One thing to note, if you have any merge conflicts created by your fix, then the rebase will stop and ask you to fix the merge conflicts. There is some information in the GitHub doc's for \[resolving merge conflicts after a Git rebase\]\[2\]. + +\[2\]: https://docs.github.com/en/get-started/using-git/resolving-merge-conflicts-after-a-git-rebase + +At this point, we can check that the commit history looks correct + +``` +git log v0.2.0..HEAD +``` + +If we are happy, then we can apply this to the `main` branch. I do this by renaming the `apply-hotfix` branch as `main`. First, you have to delete the `main` branch to allow us to rename the branch. + +``` +git branch -D main +git branch -m main +``` + +We also need to update the other branches to use the new main branch + +``` +git checkout branch +git rebase main +``` + +Now, we should have a history like + +```{r} +#| fig.height: 2.5 +#| echo: false +data.frame( + x = 1:7, + y = c(1, 1, 1, 1, 1, 2, 2), + g = c(1, 1, 1, 1, 1, 1, 1), + t = c("tag", "tag", "main", "main", "main", "branch", "branch"), + l = c("v0.2.0", "v0.2.1", "main", "main", "main", "branch", "branch") +) |> + ggplot(aes(x, y, group = g)) + + geom_line() + + geom_label(aes(label = l, fill = t)) + + scale_fill_manual(values = colours) +``` diff --git a/blogs/posts/2023-03-24_hotfix-with-git/renv.lock b/blogs/posts/2023-03-24_hotfix-with-git/renv.lock new file mode 100644 index 0000000..62d832c --- /dev/null +++ b/blogs/posts/2023-03-24_hotfix-with-git/renv.lock @@ -0,0 +1,651 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2023-04-26_alternative_remotes.qmd b/blogs/posts/2023-04-26_alternative_remotes/index.qmd similarity index 94% rename from blogs/posts/2023-04-26_alternative_remotes.qmd rename to blogs/posts/2023-04-26_alternative_remotes/index.qmd index 5fcaade..49a2dc3 100644 --- a/blogs/posts/2023-04-26_alternative_remotes.qmd +++ b/blogs/posts/2023-04-26_alternative_remotes/index.qmd @@ -1,71 +1,77 @@ ---- -title: "Alternative remote repositories" -author: "Tom Jemmett" -date: "2023-04-26" -categories: [git, tutorial] -knitr: - opts_chunk: - fig.height: 2.5 - echo: false - dev: png - dev.args: - bg: transparent ---- - -It's great when someone send's you a pull request on GitHub to fix bugs or add new features to your project, but you probably always want to check the other persons work in someway before merging that pull request. - -All of the steps below are intended to be entered via a terminal. - -Let's imagine that we have a GitHub account called example and a repository called test, and we use https rather than ssh. - -``` -$ git remote get-url origin -# https://github.com/example/test.git -``` - -Now, let's say we have someone who has submitted a Pull Request (PR), and their username is friend. We can add a new remote for their fork with - -``` -$ git remote add friend https://github.com/friend/test.git -``` - -Here, I name the remote exactly as per the persons GitHub username for no other reason than making it easier to track things later on. You could name this remote whatever you like, but you will need to make sure that the remote url matches their repository correctly. - -We are now able to checkout their remote branch. First, we will want to fetch their work: - -``` -# make sure to replace the remote name to what you set it to before -$ git fetch friend -``` - -Now, hopefully they have commited to a branch with a name that you haven't used. Let's say they created a branch called `my_work`. You can then simply run - -``` -$ git switch friend/my_work -``` - -This should checkout the `my_work` branch locally for you. - -Now, if they have happened to use a branch name that you are already using, or more likely, directly commited to their own `main` branch, you will need to do checkout to a new branch: - -``` -# replace friend as above to be the name of the remote, and main to be the branch -# that they have used -# replace their_work with whatever you want to call this branch locally -$ git checkout friend/main -b their_work -``` - -You are now ready to run their code and check everything is good to merge! - -Finally, If you want to clean up your local repository you can remove the new branch that you checked out and the new remote with the following steps: - -``` -# switch back to one of your branches, e.g. main -$ git checkout main - -# then remove the branch that you created above -$ git branch -D their_work - -# you can remove the remote -$ git remote remove friend -``` \ No newline at end of file +--- +title: "Alternative remote repositories" +author: "Tom Jemmett" +date: "2023-04-26" +categories: [git, tutorial] +knitr: + opts_chunk: + fig.height: 2.5 + echo: false + dev: png + dev.args: + bg: transparent +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +It's great when someone send's you a pull request on GitHub to fix bugs or add new features to your project, but you probably always want to check the other persons work in someway before merging that pull request. + +All of the steps below are intended to be entered via a terminal. + +Let's imagine that we have a GitHub account called example and a repository called test, and we use https rather than ssh. + +``` +$ git remote get-url origin +# https://github.com/example/test.git +``` + +Now, let's say we have someone who has submitted a Pull Request (PR), and their username is friend. We can add a new remote for their fork with + +``` +$ git remote add friend https://github.com/friend/test.git +``` + +Here, I name the remote exactly as per the persons GitHub username for no other reason than making it easier to track things later on. You could name this remote whatever you like, but you will need to make sure that the remote url matches their repository correctly. + +We are now able to checkout their remote branch. First, we will want to fetch their work: + +``` +# make sure to replace the remote name to what you set it to before +$ git fetch friend +``` + +Now, hopefully they have commited to a branch with a name that you haven't used. Let's say they created a branch called `my_work`. You can then simply run + +``` +$ git switch friend/my_work +``` + +This should checkout the `my_work` branch locally for you. + +Now, if they have happened to use a branch name that you are already using, or more likely, directly commited to their own `main` branch, you will need to do checkout to a new branch: + +``` +# replace friend as above to be the name of the remote, and main to be the branch +# that they have used +# replace their_work with whatever you want to call this branch locally +$ git checkout friend/main -b their_work +``` + +You are now ready to run their code and check everything is good to merge! + +Finally, If you want to clean up your local repository you can remove the new branch that you checked out and the new remote with the following steps: + +``` +# switch back to one of your branches, e.g. main +$ git checkout main + +# then remove the branch that you created above +$ git branch -D their_work + +# you can remove the remote +$ git remote remove friend +``` diff --git a/blogs/posts/2023-04-26_alternative_remotes/renv.lock b/blogs/posts/2023-04-26_alternative_remotes/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/blogs/posts/2023-04-26_alternative_remotes/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2023-04-26-reinstalling-r-packages.qmd b/blogs/posts/2023-04-26_reinstalling-r-packages/index.qmd similarity index 92% rename from blogs/posts/2023-04-26-reinstalling-r-packages.qmd rename to blogs/posts/2023-04-26_reinstalling-r-packages/index.qmd index 3a19450..197b3b1 100644 --- a/blogs/posts/2023-04-26-reinstalling-r-packages.qmd +++ b/blogs/posts/2023-04-26_reinstalling-r-packages/index.qmd @@ -1,31 +1,37 @@ ---- -title: "Reinstalling R Packages" -author: "Tom Jemmett" -date: "2023-04-26" -categories: [git, tutorial] -knitr: - opts_chunk: - fig.height: 2.5 - echo: false - dev: png - dev.args: - bg: transparent ---- - -[R 4.3.0 was released](https://stat.ethz.ch/pipermail/r-announce/2023/000691.html) last week. Anytime you update R you will probably find yourself in the position where no packages are installed. This is by design - the packages that you have installed may need to be updated and recompiled to work under new versions of R. - -You may find yourself wanting to have all of the packages that you previously used, so one approach that some people take is to copy the previous library folder to the new versions folder. This isn't a good idea and could potentially break your R install. - -Another approach would be to export the list of packages in R before updating and then using that list after you have updated R. This can cause issues though if you install from places other than CRAN, e.g. bioconductor, or from GitHub. - -Some of these approaches are discussed on the [RStudio Community Forum](https://community.rstudio.com/t/reinstalling-packages-on-new-version-of-r/7670/4). But I prefer an approach of having a "spring clean", instead only installing the packages that I know that I need. - -I maintain a [list of the packages that I used](https://gist.github.com/tomjemmett/c105d3e0fbea7558088f68c65e68e1ed/) as a gist. Using this, I can then simply run this script on any new R install. In fact, if you click the "raw" button on the gist, and copy that url, you can simply run - -``` r -source("https://gist.githubusercontent.com/tomjemmett/c105d3e0fbea7558088f68c65e68e1ed/raw/a1db4b5fa0d24562d16d3f57fe8c25fb0d8aa53e/setup.R") -``` - -Generally, sourcing a url is a bad idea - the reason for this is if it's not a link that you control, then someone could update the contents and run arbritary code on your machine. In this case, I'm happy to run this as it's my own gist, but you should be mindful if running it yourself! - -If you look at the script I first install a number of packages from CRAN, then I install packages that only exist on GitHub. \ No newline at end of file +--- +title: "Reinstalling R Packages" +author: "Tom Jemmett" +date: "2023-04-26" +categories: [git, tutorial] +knitr: + opts_chunk: + fig.height: 2.5 + echo: false + dev: png + dev.args: + bg: transparent +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[R 4.3.0 was released](https://stat.ethz.ch/pipermail/r-announce/2023/000691.html) last week. Anytime you update R you will probably find yourself in the position where no packages are installed. This is by design - the packages that you have installed may need to be updated and recompiled to work under new versions of R. + +You may find yourself wanting to have all of the packages that you previously used, so one approach that some people take is to copy the previous library folder to the new versions folder. This isn't a good idea and could potentially break your R install. + +Another approach would be to export the list of packages in R before updating and then using that list after you have updated R. This can cause issues though if you install from places other than CRAN, e.g. bioconductor, or from GitHub. + +Some of these approaches are discussed on the [RStudio Community Forum](https://community.rstudio.com/t/reinstalling-packages-on-new-version-of-r/7670/4). But I prefer an approach of having a "spring clean", instead only installing the packages that I know that I need. + +I maintain a [list of the packages that I used](https://gist.github.com/tomjemmett/c105d3e0fbea7558088f68c65e68e1ed/) as a gist. Using this, I can then simply run this script on any new R install. In fact, if you click the "raw" button on the gist, and copy that url, you can simply run + +``` r +source("https://gist.githubusercontent.com/tomjemmett/c105d3e0fbea7558088f68c65e68e1ed/raw/a1db4b5fa0d24562d16d3f57fe8c25fb0d8aa53e/setup.R") +``` + +Generally, sourcing a url is a bad idea - the reason for this is if it's not a link that you control, then someone could update the contents and run arbritary code on your machine. In this case, I'm happy to run this as it's my own gist, but you should be mindful if running it yourself! + +If you look at the script I first install a number of packages from CRAN, then I install packages that only exist on GitHub. diff --git a/blogs/posts/2023-04-26_reinstalling-r-packages/renv.lock b/blogs/posts/2023-04-26_reinstalling-r-packages/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/blogs/posts/2023-04-26_reinstalling-r-packages/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-01-10-advent-of-code-and-test-driven-development.qmd b/blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/index.qmd similarity index 93% rename from blogs/posts/2024-01-10-advent-of-code-and-test-driven-development.qmd rename to blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/index.qmd index 695c6b8..dfedb1a 100644 --- a/blogs/posts/2024-01-10-advent-of-code-and-test-driven-development.qmd +++ b/blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/index.qmd @@ -1,24 +1,30 @@ ---- -title: "Advent of Code and Test Driven Development" -author: "YiWen Hon" -date: "2024-01-10" -categories: [learning] ---- - -[Advent of Code]( https://adventofcode.com/) is an annual event, where daily coding puzzles are released from 1st – 24th December. -We ran one of our fortnightly Coffee & Coding sessions introducing Advent of Code to people who code in the Strategy Unit, as well as the concept of test-driven development as a potential way of approaching the puzzles. - -[Test-driven development](https://developer.ibm.com/articles/5-steps-of-test-driven-development/) (TDD) is an approach to coding which involves writing the test for a function BEFORE we write the function. -This might seem quite counterintuitive, but it makes it easier to identify bugs 🐛 when they are introduced to our code, and ensures that our functions meet all necessary criteria. -From my experience, this takes quite a long time to implement and can be quite tedious, but it is definitely worth it overall, especially as your project develops. -Testing is also recommended in the [NHS Reproducible Analytical Pipeline (RAP)](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) guidelines. - -An interesting thing to note about TDD is that we're always expecting our first test to fail, and indeed failing tests are useful and important! -If we wrote tests that just passed all the time, this would not be useful at all for our code. - -The way that Advent of Code is structured, with test data for each puzzle and an expected test result, makes it very amenable to a test-driven approach. -In order to support this, Matt and I created template repositories for a test-driven approach to Advent of Code, in [Python](https://github.com/yiwen-h/aoc_python_template) and in [R]( https://github.com/matt-dray/aoc.rstats.template). - -Our goal when setting this up was to introduce others in the Strategy Unit to both TDD and Advent of Code. -Advent of code can be challenging and I personally struggle to get past the first week, but it encourages creative (and maybe even fun?!) approaches to coding problems. -I'm glad that we had the chance to explore some of the puzzles together in Coffee & Coding – it was interesting to see so many different approaches to the same problem, and hopefully it also gave us all the chance to practice writing tests. +--- +title: "Advent of Code and Test Driven Development" +author: "YiWen Hon" +date: "2024-01-10" +categories: [learning] +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[Advent of Code]( https://adventofcode.com/) is an annual event, where daily coding puzzles are released from 1st – 24th December. +We ran one of our fortnightly Coffee & Coding sessions introducing Advent of Code to people who code in the Strategy Unit, as well as the concept of test-driven development as a potential way of approaching the puzzles. + +[Test-driven development](https://developer.ibm.com/articles/5-steps-of-test-driven-development/) (TDD) is an approach to coding which involves writing the test for a function BEFORE we write the function. +This might seem quite counterintuitive, but it makes it easier to identify bugs 🐛 when they are introduced to our code, and ensures that our functions meet all necessary criteria. +From my experience, this takes quite a long time to implement and can be quite tedious, but it is definitely worth it overall, especially as your project develops. +Testing is also recommended in the [NHS Reproducible Analytical Pipeline (RAP)](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) guidelines. + +An interesting thing to note about TDD is that we're always expecting our first test to fail, and indeed failing tests are useful and important! +If we wrote tests that just passed all the time, this would not be useful at all for our code. + +The way that Advent of Code is structured, with test data for each puzzle and an expected test result, makes it very amenable to a test-driven approach. +In order to support this, Matt and I created template repositories for a test-driven approach to Advent of Code, in [Python](https://github.com/yiwen-h/aoc_python_template) and in [R]( https://github.com/matt-dray/aoc.rstats.template). + +Our goal when setting this up was to introduce others in the Strategy Unit to both TDD and Advent of Code. +Advent of code can be challenging and I personally struggle to get past the first week, but it encourages creative (and maybe even fun?!) approaches to coding problems. +I'm glad that we had the chance to explore some of the puzzles together in Coffee & Coding – it was interesting to see so many different approaches to the same problem, and hopefully it also gave us all the chance to practice writing tests. \ No newline at end of file diff --git a/blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/renv.lock b/blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/renv.lock new file mode 100644 index 0000000..2f77bb4 --- /dev/null +++ b/blogs/posts/2024-01-10_advent-of-code-and-test-driven-development/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-01-17_nearest_neighbour.qmd b/blogs/posts/2024-01-17_nearest_neighbour/index.qmd similarity index 75% rename from blogs/posts/2024-01-17_nearest_neighbour.qmd rename to blogs/posts/2024-01-17_nearest_neighbour/index.qmd index 8c55e66..9013244 100644 --- a/blogs/posts/2024-01-17_nearest_neighbour.qmd +++ b/blogs/posts/2024-01-17_nearest_neighbour/index.qmd @@ -1,191 +1,196 @@ ---- -title: "Nearest neighbour imputation" -author: - - Jacqueline Grout -date: "2024-01-17" -categories: [learning] -reference-location: margin -citation-location: margin ---- - -Recently I have been gathering data by GP practice, from a variety of different sources. The ultimate purpose of my project is to be able to report at an ICB/sub-ICB level[^1]. The various datasets cover different timescales and consequently changes in GP practices over time have left me with mismatching datasets. - -[^1]: An ICB (Integrated Care Board) is a statutory NHS organisation responsible for planning health services for their local populations - -My approach has been to take as the basis of my project a recent GP List. Later in my project I want to perform calculations at a GP practice level based on an underlying health need and the data for this need is a CHD prevalence value from a dataset that is around 8 years old, and for which there is no update or alternative. From my recent list of 6454 practices, when I match to the need dataset, I am left with 151 practices without a value for need. If I remove these practices from the analysis then this could impact the analysis by sub-ICB since often a group of practices in the same area could be subject to changes, mergers and reorganisation. - -Here's the packages and some demo objects to work with to create an example for two practices: - -```{r} -#| code-fold: true -#| warning: false -#| message: false -#| output: false - - -# Packages -library(tidyverse) -library(sf) -library(tidygeocoder) -library(leaflet) -library(viridisLite) -library(gt) - -# Create some data with two practices with no need data -# and a selection of practices locally with need data -practices <- tribble( - ~practice_code, ~postcode, ~has_orig_need, ~value, - "P1","CV1 4FS", 0, NA, - "P2","CV1 3GB", 1, 7.3, - "P3","CV11 5TW", 1, 6.9, - "P4","CV6 3HZ", 1, 7.1, - "P5","CV6 1HS", 1, 7.7, - "P6","CV6 5DF", 1, 8.2, - "P7","CV6 3FA", 1, 7.9, - "P8","CV1 2DL", 1, 7.5, - "P9","CV1 4JH", 1, 7.7, - "P10","CV10 0GQ", 1, 7.5, - "P11","CV10 0JH", 1, 7.8, - "P12","CV11 5QT", 0, NA, - "P13","CV11 6AB", 1, 7.6, - "P14","CV6 4DD", 1,7.9 -) - -# get domain of numeric data -(domain <- range(practices$has_orig_need)) - -# make a colour palette -pal <- colorNumeric(palette = viridis(2), domain = domain) - -``` - -To provide a suitable estimate of need for the newer practices without values, all the practices in the dataset were geocoded[^2] using the `geocode` function from the {tidygeocoder} package. - -[^2]: Geocoding is the process of converting addresses (often the postcode) into geographic coordinates (such as latitude and longitude) that can be plotted on a map. - -```{r} -#| output: false -practices <- practices |> - mutate(id = row_number()) |> - geocode(postalcode = postcode) |> - st_as_sf(coords = c("long", "lat"), crs = 4326) - - -``` - -```{r} -#| code-fold: true -#| warning: false -#| message: false -practices |> - gt() -``` - -This map shows the practices, purple are the practices with no need data and yellow are practices with need data available. - -```{r} -#| code-fold: true -#| warning: false -#| message: false -# make map to display practices -leaflet(practices) |> - addTiles() |> - addCircleMarkers(color = ~pal(has_orig_need)) - -``` - -The data was split into those with, and without, a value for need. Using `st_join` from the {sf} package to join those without, and those with, a value for need, using the geometry to find all those within 1500m (1.5km). - -```{r} - -no_need <- practices |> - filter(has_orig_need == 0) - -with_need <- practices |> - filter(has_orig_need == 1) - - -neighbours <- no_need |> - select(no_need_postcode = postcode,no_need_prac_code=practice_code) |> - st_join(with_need, st_is_within_distance, 1500) |> - st_drop_geometry() |> - select(id, no_need_postcode,no_need_prac_code) |> - inner_join(x = with_need, by = join_by("id")) - - -``` - -```{r} -#| code-fold: true -#| warning: false -#| message: false - - -leaflet(neighbours) |> - addTiles() |> - addCircleMarkers(color = "purple") |> - addMarkers( -1.50686326666667, 52.4141089666667, popup = "Practice with no data" -) |> - addCircles(-1.50686326666667, 52.4141089666667,radius=1500) |> - addMarkers(-1.46927, 52.51899, popup = "Practice with no data" -) |> -addCircles(-1.46927, 52.51899,radius=1500) -``` - -The data for the "neighbours" was grouped by the practice code of those without need data and a mean value was calculated for each practice to generate an estimated value. - -```{r} - -neighbours_estimate <- neighbours |> - group_by(no_need_prac_code) |> - summarise(need_est=mean(value)) |> - st_drop_geometry(select(no_need_prac_code,need_est)) - - - -``` - -The original data was joined back to the "neighbours". - -```{r} - - practices_with_neighbours_estimate <- practices |> - left_join(neighbours_estimate, join_by(practice_code==no_need_prac_code)) |> - st_drop_geometry(select(practice_code,need_est)) -``` - -```{r} -#| code-fold: true -#| warning: false -#| message: false - practices_with_neighbours_estimate |> - select(-has_orig_need,-id) |> - gt() - -``` - -Finally, an updated data frame was created of the need data using the actual need for the practice where available, otherwise using estimated need. - -```{r} - -practices_with_neighbours_estimate <- practices_with_neighbours_estimate |> - mutate(need_to_use = case_when(value>=0 ~ value, - .default = need_est)) |> - select(practice_code,need_to_use) - -``` - -```{r} -#| echo: false -#| warning: false -#| message: false -practices_with_neighbours_estimate |> - gt() -``` - -For my project, this method has successfully generated a prevalence for 125 of the 151 practices without a need value, leaving just 26 practices without a need. This is using a 1.5 km radius. In each use case there will be a decision to make regarding a more accurate estimate (smaller radius) and therefore fewer matches versus a less accurate estimate (using a larger radius) and therefore more matches. - -This approach could be replicated for other similar uses/purposes. A topical example from an SU project is the need to assign population prevalence for hypertension and compare it to current QOF[^3] data. Again, the prevalence data is a few years old so we have to move the historical data to fit with current practices and this leaves missing data that can be estimated using this method. - - -[^3]: QOF (Quality and Outcomes Framework) is a voluntary annual reward and incentive programme for all GP practices in England, detailing practice achievement results. +--- +title: "Nearest neighbour imputation" +author: + - Jacqueline Grout +date: "2024-01-17" +categories: [learning] +reference-location: margin +citation-location: margin +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +Recently I have been gathering data by GP practice, from a variety of different sources. The ultimate purpose of my project is to be able to report at an ICB/sub-ICB level[^1]. The various datasets cover different timescales and consequently changes in GP practices over time have left me with mismatching datasets. + +[^1]: An ICB (Integrated Care Board) is a statutory NHS organisation responsible for planning health services for their local populations + +My approach has been to take as the basis of my project a recent GP List. Later in my project I want to perform calculations at a GP practice level based on an underlying health need and the data for this need is a CHD prevalence value from a dataset that is around 8 years old, and for which there is no update or alternative. From my recent list of 6454 practices, when I match to the need dataset, I am left with 151 practices without a value for need. If I remove these practices from the analysis then this could impact the analysis by sub-ICB since often a group of practices in the same area could be subject to changes, mergers and reorganisation. + +Here's the packages and some demo objects to work with to create an example for two practices: + +```{r} +#| code-fold: true +#| warning: false +#| message: false +#| output: false + + +# Packages +library(tidyverse) +library(sf) +library(tidygeocoder) +library(leaflet) +library(viridisLite) +library(gt) + +# Create some data with two practices with no need data +# and a selection of practices locally with need data +practices <- tribble( + ~practice_code, ~postcode, ~has_orig_need, ~value, + "P1", "CV1 4FS", 0, NA, + "P2", "CV1 3GB", 1, 7.3, + "P3", "CV11 5TW", 1, 6.9, + "P4", "CV6 3HZ", 1, 7.1, + "P5", "CV6 1HS", 1, 7.7, + "P6", "CV6 5DF", 1, 8.2, + "P7", "CV6 3FA", 1, 7.9, + "P8", "CV1 2DL", 1, 7.5, + "P9", "CV1 4JH", 1, 7.7, + "P10", "CV10 0GQ", 1, 7.5, + "P11", "CV10 0JH", 1, 7.8, + "P12", "CV11 5QT", 0, NA, + "P13", "CV11 6AB", 1, 7.6, + "P14", "CV6 4DD", 1, 7.9 +) + +# get domain of numeric data +(domain <- range(practices$has_orig_need)) + +# make a colour palette +pal <- colorNumeric(palette = viridis(2), domain = domain) + +``` + +To provide a suitable estimate of need for the newer practices without values, all the practices in the dataset were geocoded[^2] using the `geocode` function from the {tidygeocoder} package. + +[^2]: Geocoding is the process of converting addresses (often the postcode) into geographic coordinates (such as latitude and longitude) that can be plotted on a map. + +```{r} +#| output: false +practices <- practices |> + mutate(id = row_number()) |> + geocode(postalcode = postcode) |> + st_as_sf(coords = c("long", "lat"), crs = 4326) + + +``` + +```{r} +#| code-fold: true +#| warning: false +#| message: false +practices |> + gt() +``` + +This map shows the practices, purple are the practices with no need data and yellow are practices with need data available. + +```{r} +#| code-fold: true +#| warning: false +#| message: false +# make map to display practices +leaflet(practices) |> + addTiles() |> + addCircleMarkers(color = ~ pal(has_orig_need)) + +``` + +The data was split into those with, and without, a value for need. Using `st_join` from the {sf} package to join those without, and those with, a value for need, using the geometry to find all those within 1500m (1.5km). + +```{r} + +no_need <- practices |> + filter(has_orig_need == 0) + +with_need <- practices |> + filter(has_orig_need == 1) + + +neighbours <- no_need |> + select(no_need_postcode = postcode, no_need_prac_code = practice_code) |> + st_join(with_need, st_is_within_distance, 1500) |> + st_drop_geometry() |> + select(id, no_need_postcode, no_need_prac_code) |> + inner_join(x = with_need, by = join_by("id")) + + +``` + +```{r} +#| code-fold: true +#| warning: false +#| message: false + + +leaflet(neighbours) |> + addTiles() |> + addCircleMarkers(color = "purple") |> + addMarkers(-1.50686326666667, 52.4141089666667, popup = "Practice with no data") |> + addCircles(-1.50686326666667, 52.4141089666667, radius = 1500) |> + addMarkers(-1.46927, 52.51899, popup = "Practice with no data") |> + addCircles(-1.46927, 52.51899, radius = 1500) +``` + +The data for the "neighbours" was grouped by the practice code of those without need data and a mean value was calculated for each practice to generate an estimated value. + +```{r} + +neighbours_estimate <- neighbours |> + group_by(no_need_prac_code) |> + summarise(need_est = mean(value)) |> + st_drop_geometry(select(no_need_prac_code, need_est)) + + + +``` + +The original data was joined back to the "neighbours". + +```{r} + +practices_with_neighbours_estimate <- practices |> + left_join(neighbours_estimate, join_by(practice_code == no_need_prac_code)) |> + st_drop_geometry(select(practice_code, need_est)) +``` + +```{r} +#| code-fold: true +#| warning: false +#| message: false +practices_with_neighbours_estimate |> + select(-has_orig_need, -id) |> + gt() + +``` + +Finally, an updated data frame was created of the need data using the actual need for the practice where available, otherwise using estimated need. + +```{r} + +practices_with_neighbours_estimate <- practices_with_neighbours_estimate |> + mutate(need_to_use = case_when(value >= 0 ~ value, + .default = need_est + )) |> + select(practice_code, need_to_use) + +``` + +```{r} +#| echo: false +#| warning: false +#| message: false +practices_with_neighbours_estimate |> + gt() +``` + +For my project, this method has successfully generated a prevalence for 125 of the 151 practices without a need value, leaving just 26 practices without a need. This is using a 1.5 km radius. In each use case there will be a decision to make regarding a more accurate estimate (smaller radius) and therefore fewer matches versus a less accurate estimate (using a larger radius) and therefore more matches. + +This approach could be replicated for other similar uses/purposes. A topical example from an SU project is the need to assign population prevalence for hypertension and compare it to current QOF[^3] data. Again, the prevalence data is a few years old so we have to move the historical data to fit with current practices and this leaves missing data that can be estimated using this method. + + +[^3]: QOF (Quality and Outcomes Framework) is a voluntary annual reward and incentive programme for all GP practices in England, detailing practice achievement results. diff --git a/blogs/posts/2024-01-17_nearest_neighbour/renv.lock b/blogs/posts/2024-01-17_nearest_neighbour/renv.lock new file mode 100644 index 0000000..1c9502e --- /dev/null +++ b/blogs/posts/2024-01-17_nearest_neighbour/renv.lock @@ -0,0 +1,1898 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "DBI": { + "Package": "DBI", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "065ae649b05f1ff66bb0c793107508f5" + }, + "KernSmooth": { + "Package": "KernSmooth", + "Version": "2.23-24", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats" + ], + "Hash": "9f33a1ee37bbe8919eb2ec4b9f2473a5" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "6b868847b365672d6c1677b1608da9ed" + }, + "V8": { + "Package": "V8", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "curl", + "jsonlite", + "utils" + ], + "Hash": "6603bfcbc7883a5fed41fb13042a3899" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bigD": { + "Package": "bigD", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "93637e906f3fe962413912c956eb44db" + }, + "bit": { + "Package": "bit", + "Version": "4.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" + }, + "bit64": { + "Package": "bit64", + "Version": "4.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "e84984bf5f12a18628d9a02322128dfd" + }, + "bitops": { + "Package": "bitops", + "Version": "1.0-9", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d972ef991d58c19e6efa71b21f5e144b" + }, + "blob": { + "Package": "blob", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "rlang", + "vctrs" + ], + "Hash": "40415719b5a479b87949f3aa0aee737c" + }, + "broom": { + "Package": "broom", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "8fcc818f3b9887aebaf206f141437cc9" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "class": { + "Package": "class", + "Version": "7.3-22", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "stats", + "utils" + ], + "Hash": "f91f6b29f38b8c280f2b9477787d4bb2" + }, + "classInt": { + "Package": "classInt", + "Version": "0.4-10", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "KernSmooth", + "R", + "class", + "e1071", + "grDevices", + "graphics", + "stats" + ], + "Hash": "f5a40793b1ae463a7ffb3902a95bf864" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "commonmark": { + "Package": "commonmark", + "Version": "1.9.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "14eb0596f987c71535d07c3aff814742" + }, + "conflicted": { + "Package": "conflicted", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "memoise", + "rlang" + ], + "Hash": "bb097fccb22d156624fd07cd2894ddb6" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "ab12c7b080a57475248a30f4db6298c0" + }, + "curl": { + "Package": "curl", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ff51697d9205fe715f29e7171e874c6e" + }, + "data.table": { + "Package": "data.table", + "Version": "1.16.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "2e00b378fc3be69c865120d9f313039a" + }, + "dbplyr": { + "Package": "dbplyr", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "DBI", + "R", + "R6", + "blob", + "cli", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "utils", + "vctrs", + "withr" + ], + "Hash": "39b2e002522bfd258039ee4e889e0fd1" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "dtplyr": { + "Package": "dtplyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "data.table", + "dplyr", + "glue", + "lifecycle", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "54ed3ea01b11e81a86544faaecfef8e2" + }, + "e1071": { + "Package": "e1071", + "Version": "1.7-16", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "class", + "grDevices", + "graphics", + "methods", + "proxy", + "stats", + "utils" + ], + "Hash": "27a09ca40266a1066d62ef5402dd51d6" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "forcats": { + "Package": "forcats", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "tibble" + ], + "Hash": "1a0a9a3d5083d0d573c4214576f1e690" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "gargle": { + "Package": "gargle", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "fs", + "glue", + "httr", + "jsonlite", + "lifecycle", + "openssl", + "rappdirs", + "rlang", + "stats", + "utils", + "withr" + ], + "Hash": "fc0b272e5847c58cd5da9b20eedbd026" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "googledrive": { + "Package": "googledrive", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gargle", + "glue", + "httr", + "jsonlite", + "lifecycle", + "magrittr", + "pillar", + "purrr", + "rlang", + "tibble", + "utils", + "uuid", + "vctrs", + "withr" + ], + "Hash": "e99641edef03e2a5e87f0a0b1fcc97f4" + }, + "googlesheets4": { + "Package": "googlesheets4", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cli", + "curl", + "gargle", + "glue", + "googledrive", + "httr", + "ids", + "lifecycle", + "magrittr", + "methods", + "purrr", + "rematch2", + "rlang", + "tibble", + "utils", + "vctrs", + "withr" + ], + "Hash": "d6db1667059d027da730decdc214b959" + }, + "gt": { + "Package": "gt", + "Version": "0.11.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "bigD", + "bitops", + "cli", + "commonmark", + "dplyr", + "fs", + "glue", + "htmltools", + "htmlwidgets", + "juicyjuice", + "magrittr", + "markdown", + "reactable", + "rlang", + "sass", + "scales", + "tidyselect", + "vctrs", + "xml2" + ], + "Hash": "3170d1f0f45e531c241179ab57cd30bd" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "haven": { + "Package": "haven", + "Version": "2.5.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "forcats", + "hms", + "lifecycle", + "methods", + "readr", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "9171f898db9d9c4c1b2c745adc2c1ef1" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "ids": { + "Package": "ids", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "openssl", + "uuid" + ], + "Hash": "99df65cfef20e525ed38c3d2577f7190" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "juicyjuice": { + "Package": "juicyjuice", + "Version": "0.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "V8" + ], + "Hash": "3bcd11943da509341838da9399e18bce" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "leaflet": { + "Package": "leaflet", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RColorBrewer", + "crosstalk", + "htmltools", + "htmlwidgets", + "jquerylib", + "leaflet.providers", + "magrittr", + "methods", + "png", + "raster", + "scales", + "sp", + "stats", + "viridisLite", + "xfun" + ], + "Hash": "ca012d4a706e21ce217ba15f22d402b2" + }, + "leaflet.providers": { + "Package": "leaflet.providers", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools" + ], + "Hash": "c0b81ad9d5d932772f7a457ac398cf36" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "markdown": { + "Package": "markdown", + "Version": "1.13", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "commonmark", + "utils", + "xfun" + ], + "Hash": "074efab766a9d6360865ad39512f2a7e" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "modelr": { + "Package": "modelr", + "Version": "0.1.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "magrittr", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "vctrs" + ], + "Hash": "4f50122dc256b1b6996a4703fecea821" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "openssl": { + "Package": "openssl", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "d413e0fef796c9401a4419485f709ca1" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "png": { + "Package": "png", + "Version": "0.1-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "bd54ba8a0a5faded999a7aab6e46b374" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "proxy": { + "Package": "proxy", + "Version": "0.4-27", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "e0ef355c12942cf7a6b91a6cfaea8b3e" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "ragg": { + "Package": "ragg", + "Version": "1.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "0595fe5e47357111f29ad19101c7d271" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "raster": { + "Package": "raster", + "Version": "3.6-30", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "methods", + "sp", + "terra" + ], + "Hash": "0e2829df8cb74a98179c886b023ffea8" + }, + "reactR": { + "Package": "reactR", + "Version": "0.6.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "b8e3d93f508045812f47136c7c44c251" + }, + "reactable": { + "Package": "reactable", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "digest", + "htmltools", + "htmlwidgets", + "jsonlite", + "reactR" + ], + "Hash": "6069eb2a6597963eae0605c1875ff14c" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "reprex": { + "Package": "reprex", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "callr", + "cli", + "clipr", + "fs", + "glue", + "knitr", + "lifecycle", + "rlang", + "rmarkdown", + "rstudioapi", + "utils", + "withr" + ], + "Hash": "97b1d5361a24d9fb588db7afe3e5bcbf" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.17.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5f90cd73946d706cfe26024294236113" + }, + "rvest": { + "Package": "rvest", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "httr", + "lifecycle", + "magrittr", + "rlang", + "selectr", + "tibble", + "xml2" + ], + "Hash": "0bcf0c6f274e90ea314b812a6d19a519" + }, + "s2": { + "Package": "s2", + "Version": "1.1.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "wk" + ], + "Hash": "3c8013cdd7f1d20de5762e3f97e5e274" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "selectr": { + "Package": "selectr", + "Version": "0.4-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "methods", + "stringr" + ], + "Hash": "3838071b66e0c566d55cc26bd6e27bf4" + }, + "sf": { + "Package": "sf", + "Version": "1.0-19", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DBI", + "R", + "Rcpp", + "classInt", + "grDevices", + "graphics", + "grid", + "magrittr", + "methods", + "s2", + "stats", + "tools", + "units", + "utils" + ], + "Hash": "fe02eec2f6b3ba0e24afe83d5ccfb528" + }, + "sp": { + "Package": "sp", + "Version": "2.1-4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "75940133cca2e339afce15a586f85b11" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "sys": { + "Package": "sys", + "Version": "3.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "de342ebfebdbf40477d0758d05426646" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle" + ], + "Hash": "213b6b8ed5afbf934843e6c3b090d418" + }, + "terra": { + "Package": "terra", + "Version": "1.7-83", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "methods" + ], + "Hash": "fbeffe988419d292225a57cf9c284802" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle", + "systemfonts" + ], + "Hash": "5142f8bc78ed3d819d26461b641627ce" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidygeocoder": { + "Package": "tidygeocoder", + "Version": "1.0.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "dplyr", + "httr", + "jsonlite", + "lifecycle", + "progress", + "tibble" + ], + "Hash": "44fd552dae5b20c4224e895449e3827a" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tidyverse": { + "Package": "tidyverse", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "cli", + "conflicted", + "dbplyr", + "dplyr", + "dtplyr", + "forcats", + "ggplot2", + "googledrive", + "googlesheets4", + "haven", + "hms", + "httr", + "jsonlite", + "lubridate", + "magrittr", + "modelr", + "pillar", + "purrr", + "ragg", + "readr", + "readxl", + "reprex", + "rlang", + "rstudioapi", + "rvest", + "stringr", + "tibble", + "tidyr", + "xml2" + ], + "Hash": "c328568cd14ea89a83bd4ca7f54ae07e" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "units": { + "Package": "units", + "Version": "0.8-5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp" + ], + "Hash": "119d19da480e873f72241ff6962ffd83" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "uuid": { + "Package": "uuid", + "Version": "1.2-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "34e965e62a41fcafb1ca60e9b142085b" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "wk": { + "Package": "wk", + "Version": "0.9.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "37be35d733130f1de1ef51672cf7cdc0" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-02-28_sankey_plot.qmd b/blogs/posts/2024-02-28_sankey_plot/index.qmd similarity index 80% rename from blogs/posts/2024-02-28_sankey_plot.qmd rename to blogs/posts/2024-02-28_sankey_plot/index.qmd index 45a4a21..420d9fd 100644 --- a/blogs/posts/2024-02-28_sankey_plot.qmd +++ b/blogs/posts/2024-02-28_sankey_plot/index.qmd @@ -1,587 +1,593 @@ ---- -title: "Visualising participant recruitment in R using Sankey plots" -author: - - Craig Parylo -date: "2024-02-28" -categories: [learning, tutorial, visualisation, R] -reference-location: margin -citation-location: margin ---- - -# Introduction - -Sankey diagrams are great tools to visualise flows through a system. They show connections between the steps of a process where the width of the arrows is proportional to the flow. - -I'm working on an evaluation of a risk screening process for people aged between 55-74 years and a history of smoking. In this Targeted Lung Health Check (TLHC) programme[^1] eligible people are invited to attend a free lung check where those assessed at high risk of lung cancer are then offered low-dose CT screening scans. - -[^1]: Please visit the [NHS England](https://www.england.nhs.uk/contact-us/privacy-notice/how-we-use-your-information/our-services/evaluation-of-the-targeted-lung-health-check-programme/) site for for more background. - -We used Sankey diagrams to visualise how people have engaged with the programme, from recruitment, attendance at appointments, their outcome from risk assessment, attendance at CT scans and will eventually be extended to cover the impact of the screening on early detection of those diagnosed with lung cancer. - -This blog post is about the technical process of preparing record-level data for visualisation in a Sankey plot using `R` and customising it to enhance look and feel. Here is how the finished product will look: - -```{r} -#| echo: false -# here's one I prepared earlier -readRDS(file = here::here('blogs', 'posts', 'assets', 'sankey_example.rds')) -``` - -# Data wrangling - -First we'll attach some packages. I'll be using [plotly](https://plotly.com/r/sankey-diagram/) for the visualisation of the Sankey chart, [tidygraph](https://tidygraph.data-imaginist.com/) for graph manipulation and [scales](https://scales.r-lib.org/) to handle colour transformation and rescaling values. We will also be using the [tidyverse](https://www.tidyverse.org/) and [glue](https://glue.tidyverse.org/) packages for general data wrangling and [reactable](https://glin.github.io/reactable/index.html) to preview our data as we go along. - -```{r} -#| code-fold: false -#| warning: false -#| message: false -#| output: false - -# libraries -library(tidyverse) # 'tidy' data wrangling -library(plotly) # sankey visualisation framework -library(reactable) # viewing interactive datatables -library(glue) # concatenating strings -library(tidygraph) # api for graph / network manipulation -library(scales) # used for colour transformation -``` - -## Get the data - -In this example we will work with a simplified set of data focused on invitations. - -The invites table holds details of when people were sent a letter or message inviting them to take part, how many times they were invited and how the person responded. - -The people eligible for the programme are identified up-front and are represented by a unique ID with one row per person. Let's assume each person receives at least one invitation to take part, they can have one of three outcomes: - -1. They accept the invitation and agree to take part, - -2. They decline the invitation, - -3. They do not respond to the invitation. - -If the person doesn't respond to the first invitation they may be sent a second invitation and could be offered a third invitation if they didn't respond to the second. - -Here is the specification for our simplified invites table: - -+----------------+---------+-------------------------------------------------------------------------------------+ -| Field | Type | Description | -+================+=========+=====================================================================================+ -| Participant ID | Integer | A unique identifier for each person. | -+----------------+---------+-------------------------------------------------------------------------------------+ -| Invite date 1 | Date | The date the person was first invited to participate. | -| | | | -| | | Every person will have a date in this field. | -+----------------+---------+-------------------------------------------------------------------------------------+ -| Invite date 2 | Date | The date a second invitation was sent. | -+----------------+---------+-------------------------------------------------------------------------------------+ -| Invite date 3 | Date | The date a third invitation was sent. | -+----------------+---------+-------------------------------------------------------------------------------------+ -| Invite outcome | Text | The outcome from the invite, one of either 'Accepted', 'Declined' or 'No response'. | -+----------------+---------+-------------------------------------------------------------------------------------+ - -: Invites specification - -Everyone receives at least one invite. Assuming a third of these respond (to either accept or decline) then two-thirds receive a follow-up invite. Of these, we assume half respond, meaning the remaining participants receive a third invite. - -Here we generate 100 rows of example data to populate our table. - -```{r} -#| code-fold: true -#| fig-cap: Generated invite table -#| fig-cap-location: top - -# set a randomisation seed for reproducibility -set.seed(seed = 1234) - -# define some parameters -start_date = as.Date('2019-01-01') -end_date = as.Date('2021-01-01') -rows = 100 - -df_invites_1 <- tibble( - # create a unique id for each participant - participant_id = 1:rows, - - # create a random initial invite date between our start and end dates - invite_1_date = sample( - seq(start_date, end_date, by = 'day'), - size = rows, replace = T - ), - - # create a random outcome for this participant - invite_outcome = sample( - x = c('Accepted', 'Declined', 'No response'), - size = rows, replace = T - ) -) - -# take a sample of participants and allocate them a second invite date -df_invites_2 <- df_invites_1 |> - # sample two thirds of participants to get a second invite - slice_sample(prop = 2/3) |> - # allocate a date between 10 and 30 days following the first - mutate( - invite_2_date = invite_1_date + sample(10:30, size = n(), replace = T) - ) |> - # keep just id and second date - select(participant_id, invite_2_date) - - -# take a sample of those with a second invite and allocate them a third invite date -df_invites_3 <- df_invites_2 |> - # sample half of these to get a third invite - slice_sample(prop = 1/2) |> - # allocate a date between 10 to 30 days following the second - mutate( - invite_3_date = invite_2_date + sample(10:30, size = n(), replace = T) - ) |> - # keep just id and second date - select(participant_id, invite_3_date) - -# combine the 2nd and 3rd invites with the first table -df_invites <- df_invites_1 |> - left_join( - y = df_invites_2, - by = 'participant_id' - ) |> - left_join( - y = df_invites_3, - by = 'participant_id' - ) |> - # move the outcome field after the third invite - relocate(invite_outcome, .after = invite_3_date) - -# housekeeping -rm(df_invites_1, df_invites_2, df_invites_3, start_date, end_date, rows) - -# view our data -df_invites |> - reactable(defaultPageSize = 5) -``` - -## Determine milestone outcomes - -The next step is to take our source table and convert the data into a series of milestones (and associated outcomes) that represents how our invited participants moved through the pathway. - -In our example we have five milestones to represent in our Sankey plot: - -- Our eligible population (everyone in our invites table), - -- The result from the first invitation, - -- The result from the second invitation, - -- The result from the third invitation, - -- The overall invite outcome. - -Aside from the eligible population, where everyone starts with the same value, participants will have one of several outcomes at each milestone. This step is about naming these milestones and the outcomes. - -It is important that each milestone-outcome has unique values. An outcome of 'No response' can be recorded against the first, second and third invite, and we wish to see these outcomes separately represented on the Sankey (rather than just one 'No response'), so each outcome must be made unique. In this example we prefix the outcome from each invite with the number of the invite, e.g. 'Invite 1 No response'. - -The reason for this will become clearer when we come to plot the Sankey, but for now we produce these milestone-outcomes from our invites table. - -```{r} -#| code-fold: true -#| fig-cap: Milestone-outcomes for participants -#| fig-cap-location: top - -df_milestones <- df_invites |> - mutate( - # everyone starts in the eligible population - start_population = 'Eligible population', - - # work out what happened following the first invite - invite_1_outcome = case_when( - # if a second invite was sent we assume there was no outcome from the first - !is.na(invite_2_date) ~ 'Invitation 1 No response', - # otherwise the overall outcome resulted from the first invite - .default = glue('Invitation 1 {invite_outcome}') - ), - - # work out what happened following the second invite - invite_2_outcome = case_when( - # if a third invite was sent we assume there was no outcome from the second - !is.na(invite_3_date) ~ 'Invitation 2 No response', - # if a second invite was sent but no third then - !is.na(invite_2_date) ~ glue('Invitation 2 {invite_outcome}'), - # default to NA if neither of the above are true - .default = NA - ), - - # work out what happened following the third invite - invite_3_outcome = case_when( - # if a third invite was sent then the outcome is the overall outcome - !is.na(invite_3_date) ~ glue('Invitation 3 {invite_outcome}'), - # otherwise mark as NA - .default = NA - ) - ) |> - # exclude the dates as they are no longer needed - select(-contains('_date')) |> - # move the overall invite outcome to the end - relocate(invite_outcome, .after = invite_3_outcome) - -# view our data -df_milestones |> - reactable(defaultPageSize = 5) -``` - -## Calculate flows - -Next we take pairs of milestone-outcomes and calculate the number of participants that moved between them. - -Here we utilise the power of `dplyr::summarise` with an argument `.by` to group by our data before counting the number of unique participants who move between our start and end groups. - -For invites 2 and 3 we perform two sets of summaries: - -1. The first where the values in the `to` and `from` fields contain details. - -2. The second to capture cases where the `to` destination is NULL. This is because the participant responded at the previous invite so there was no subsequent invite. In these cases we flow the participant to the overall invite outcome.[^2] - -[^2]: If you are thinking there is a lot of repetition here, you're right. In practice I abstracted both steps to a function and passed in the parameters for the `from` and `to` variables and simplified my workflow a little, however, I'm showing it in plain form here for simplification. - -```{r} -#| code-fold: true -#| fig-cap: Flows of participants between milestones -#| fig-cap-location: top - -df_flows <- bind_rows( - - # flow from population to invite 1 - df_milestones |> - filter(!is.na(start_population) & !is.na(invite_1_outcome)) |> - rename(from = start_population, to = invite_1_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ), - - # flow from invite 1 to invite 2 (where not NA) - df_milestones |> - filter(!is.na(invite_1_outcome) & !is.na(invite_2_outcome)) |> - rename(from = invite_1_outcome, to = invite_2_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ), - - # flow from invite 1 to overall invite outcome (where invite 2 is NA) - df_milestones |> - filter(!is.na(invite_1_outcome) & is.na(invite_2_outcome)) |> - rename(from = invite_1_outcome, to = invite_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ), - - # flow from invite 2 to invite 3 (where not NA) - df_milestones |> - filter(!is.na(invite_2_outcome) & !is.na(invite_3_outcome)) |> - rename(from = invite_2_outcome, to = invite_3_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ), - - # flow from invite 2 to overall invite outcome (where invite 3 is NA) - df_milestones |> - filter(!is.na(invite_2_outcome) & is.na(invite_3_outcome)) |> - rename(from = invite_2_outcome, to = invite_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ), - - # final flow - invite 3 to overall outcome (where both are not NA) - df_milestones |> - filter(!is.na(invite_3_outcome) & !is.na(invite_outcome)) |> - rename(from = invite_3_outcome, to = invite_outcome) |> - summarise( - flow = n_distinct(participant_id, na.rm = T), - .by = c(from, to) - ) -) - -# view our data -df_flows |> - reactable(defaultPageSize = 5) -``` - -# Sankey plot - -We now have a neat little summary of movements of participants between the milestones in our recruitment pathway. However, this 'tidy' data isn't the format required by [plotly](https://plotly.com/r/sankey-diagram/), so the next steps are to prepare it ready for plotting. - -## Preparing for plotly - -Plotly expects to be fed two sets of data: - -1. Nodes - these are the milestones we have in our `from` and `to` fields, - -2. Edges - these are the flows that occur between nodes, the `flow` in our table. - -It is possible to extract this data by hand but I found using the [tidygraph](https://tidygraph.data-imaginist.com) package was much easier and more convenient. - -```{r} -df_sankey <- df_flows |> - # convert our flows data to a tidy graph object - as_tbl_graph() - -``` - -The tidygraph package splits our data into nodes and edges. We can selectively work on each by 'activating' them - here is the nodes list: - -```{r} -df_sankey |> - activate(what = 'nodes') |> - as_tibble() |> - reactable(defaultPageSize = 5) -``` - -You can see each unique node name listed. The row numbers for these nodes are used as reference IDs in the edges object: - -```{r} -df_sankey |> - activate(what = 'edges') |> - as_tibble() |> - reactable(defaultPageSize = 5) -``` - -We now have enough information to generate our Sankey. - -First we extract our nodes and edges to separate data frames then convert the ID values to be zero-based (starts at 0) as this is what plotly is expecting. To do this is as simple as subtracting 1 from the value of the IDs. - -Finally we pass these two dataframes to plotly's `node` and `link` function inputs to generate the plot. - -```{r} -#| code-fold: true -#| fig-cap: Our first sankey -#| fig-cap-location: top - -# extract the nodes to a dataframe -nodes <- df_sankey |> - activate(nodes) |> - data.frame() |> - mutate( - id = row_number() -1 - ) - -# extract the edges to a dataframe -edges <- df_sankey |> - activate(edges) |> - data.frame() |> - mutate( - from = from - 1, - to = to - 1 - ) - -# plot our sankey -plot_ly( - # setup - type = 'sankey', - orientation = 'h', - arrangement = 'snap', - - # use our node data - node = list( - label = nodes$name - ), - - # use our link data - link = list( - source = edges$from, - target = edges$to, - value = edges$flow - ) -) -``` - -Not bad! - -We can see the structure of our Sankey now. Can you see the relative proportions of participants who did or didn't respond to our first invite? Marvel at how those who responded to the first invite flow into our final outcome. How about those who didn't respond to the first invitation go on to receive a second invite? - -Plotly's charts are interactive. Try hovering your cursor over the nodes and edges to highlight them and a pop-up box will appear giving you additional details. You can reorder the vertical position of the nodes by dragging them above or below an adjacent node. - -This looks functional. - -## Styling our Sankey - -Now we have the foundations of our Sankey I'd like to move on to its presentation. Specifically I'd like to: - -- use colour coding to clearly group those who accept or decline the invite, - -- improve the readability of the node titles, - -- add additional information to the pop-up boxes when you hover over nodes and edges, and - -- control the positioning of the nodes in the plot. - -As our `nodes` and `edges` objects are dataframes it is straightforward to add this styling information directly to them. - -For the nodes object we define colours based on the name of each node and manually position them in the plot - -```{r} -#| code-fold: true -#| fig-cap: Styling the nodes dataframe -#| fig-cap-location: top - -# get the eligible population as a single value -# NB, will be used to work out % amounts in each node and edge -temp_eligible_pop <- df_flows |> - filter(from == 'Eligible population') |> - summarise(total = sum(flow, na.rm = T)) |> - pull(total) - -# style our nodes object -nodes <- nodes |> - mutate( - # colour ---- - # add colour definitions, green for accepted, red for declined - colour = case_when( - str_detect(name, 'Accepted') ~ '#44bd32', - str_detect(name, 'Declined') ~ '#c23616', - str_detect(name, 'No response') ~ '#7f8fa6', - str_detect(name, 'Eligible population') ~ '#7f8fa6' - ), - - # add a semi-transparent colour for the edges based on node colours - colour_fade = col2hcl(colour = colour, alpha = 0.3), - - # positioning ---- - # NB, I found that to position nodes you need to supply both - # horizontal and vertical positions - # NNB, it was a bit of trial and error to get the these positions just - # right - - # horizontal positions (0 = left, 1 = right) - x = case_when( - str_detect(name, 'Eligible population') ~ 1, - str_detect(name, 'Invitation 1') ~ 2, - str_detect(name, 'Invitation 2') ~ 3, - str_detect(name, 'Invitation 3') ~ 4, - .default = 5 - ) |> rescale(to = c(0.001, 0.9)), - - # vertical position (1 = bottom, 0 = top) - y = case_when( - str_detect(name, 'Eligible population') ~ 5, - # invite 1 - str_detect(name, 'Invitation 1 Accepted') ~ 1, - str_detect(name, 'Invitation 1 No response') ~ 5, - str_detect(name, 'Invitation 1 Declined') ~ 8.5, - # invite 2 - str_detect(name, 'Invitation 2 Accepted') ~ 2, - str_detect(name, 'Invitation 2 No response') ~ 5, - str_detect(name, 'Invitation 2 Declined') ~ 7.8, - # invite 3 - str_detect(name, 'Invitation 3 Accepted') ~ 2.7, - str_detect(name, 'Invitation 3 No response') ~ 5.8, - str_detect(name, 'Invitation 3 Declined') ~ 7.2, - # final outcomes - str_detect(name, 'Accepted') ~ 1, - str_detect(name, 'No response') ~ 5, - str_detect(name, 'Declined') ~ 8, - .default = 5 - ) |> rescale(to = c(0.001, 0.999)) - ) |> - # add in a custom field to show the percentage flow - left_join( - y = df_flows |> - group_by(to) |> - summarise( - flow = sum(flow, na.rm = T), - flow_perc = percent(flow / temp_eligible_pop, accuracy = 0.1), - ) |> - select(name = to, flow_perc), - by = 'name' - ) - -# view our nodes data -nodes |> - reactable(defaultPageSize = 5) -``` - -Next we move to styling the edges, which is a much simpler prospect: - -```{r} -#| code-fold: true -#| fig-cap: Styling the edges dataframe -#| fig-cap-location: top - -edges <- edges |> - mutate( - # add a label for each flow to tell us how many people are in each - label = number(flow, big.mark = ','), - # add a percentage flow figure - flow_perc = percent(flow / temp_eligible_pop, accuracy = 0.1) - ) |> - # add the faded colour from our nodes object to match the destinations - left_join( - y = nodes |> select(to = id, colour_fade), - by = 'to' - ) - -# view our edges data -edges |> - reactable(defaultPageSize = 5) -``` - -We now have stylised node and edge tables ready and can bring it all together. Note the use of `customdata` and `hovertemplate` help to bring in additional information and styling to the pop-up boxes that appear when you hover over each flow and node. - -```{r} -#| code-fold: true -#| fig-cap: A stylish Sankey -#| fig-cap-location: top - -# plot our stylised sankey -plot_ly( - # setup - type = 'sankey', - orientation = 'h', - arrangement = 'snap', - - # use our node data - node = list( - label = nodes$name, - color = nodes$colour, - x = nodes$x, - y = nodes$y, - customdata = nodes$flow_perc, - hovertemplate = '%{label}
%{value} participants
%{customdata} of eligible population' - ), - - # use our edge data - link = list( - source = edges$from, - target = edges$to, - value = edges$flow, - label = edges$label, - color = edges$colour_fade, - customdata = edges$flow_perc, - hovertemplate = '%{source.label} → %{target.label}
%{value} participants
%{customdata} of eligible population' - ) -) |> - layout( - font = list( - family = 'Arial, Helvetica, sans-serif', - size = 12 - ), - # make the background transparent (also removes the text shadow) - paper_bgcolor = 'rgba(0,0,0,0)' - ) |> - config(responsive = T) -``` - -# Conclusion - -Creating Sankey plots in R using plotly is an effective way to visualise patient pathways. - -In our project we embedded Sankey plots within an interactive [Shiny](https://shiny.posit.co/) app which allows for selective filters that update the resulting plot. This allowed us to quickly compare the effects of different models of delivering the screening programme, geography, deprivation levels, patient demographic, or any combination of these. - -Their use has helped the programme team better understand patient flows through the pathway, where the points of drop-off are and compare / contrast the effects of different models of delivering the screening programme on patient engagement. - -Feedback from external stakeholders has been positive too, noting how easy it is to engage with and understand this style of presentation. - -In this blog post we have wrangled a dataset to describe how people flow between steps in a process and then produced a Sankey diagram with some stylistic touches to make an effective visualisation. - -I hope this post helps you feel better prepared to use Sankeys in your work. +--- +title: "Visualising participant recruitment in R using Sankey plots" +author: + - Craig Parylo +date: "2024-02-28" +categories: [learning, tutorial, visualisation, R] +reference-location: margin +citation-location: margin +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +# Introduction + +Sankey diagrams are great tools to visualise flows through a system. They show connections between the steps of a process where the width of the arrows is proportional to the flow. + +I'm working on an evaluation of a risk screening process for people aged between 55-74 years and a history of smoking. In this Targeted Lung Health Check (TLHC) programme[^1] eligible people are invited to attend a free lung check where those assessed at high risk of lung cancer are then offered low-dose CT screening scans. + +[^1]: Please visit the [NHS England](https://www.england.nhs.uk/contact-us/privacy-notice/how-we-use-your-information/our-services/evaluation-of-the-targeted-lung-health-check-programme/) site for for more background. + +We used Sankey diagrams to visualise how people have engaged with the programme, from recruitment, attendance at appointments, their outcome from risk assessment, attendance at CT scans and will eventually be extended to cover the impact of the screening on early detection of those diagnosed with lung cancer. + +This blog post is about the technical process of preparing record-level data for visualisation in a Sankey plot using `R` and customising it to enhance look and feel. Here is how the finished product will look: + +```{r} +#| echo: false +# here's one I prepared earlier +readRDS(file = here::here("blogs", "posts", "2024-02-28_sankey_plot", "sankey_example.rds")) +``` + +# Data wrangling + +First we'll attach some packages. I'll be using [plotly](https://plotly.com/r/sankey-diagram/) for the visualisation of the Sankey chart, [tidygraph](https://tidygraph.data-imaginist.com/) for graph manipulation and [scales](https://scales.r-lib.org/) to handle colour transformation and rescaling values. We will also be using the [tidyverse](https://www.tidyverse.org/) and [glue](https://glue.tidyverse.org/) packages for general data wrangling and [reactable](https://glin.github.io/reactable/index.html) to preview our data as we go along. + +```{r} +#| code-fold: false +#| warning: false +#| message: false +#| output: false + +# libraries +library(tidyverse) # 'tidy' data wrangling +library(plotly) # sankey visualisation framework +library(reactable) # viewing interactive datatables +library(glue) # concatenating strings +library(tidygraph) # api for graph / network manipulation +library(scales) # used for colour transformation +``` + +## Get the data + +In this example we will work with a simplified set of data focused on invitations. + +The invites table holds details of when people were sent a letter or message inviting them to take part, how many times they were invited and how the person responded. + +The people eligible for the programme are identified up-front and are represented by a unique ID with one row per person. Let's assume each person receives at least one invitation to take part, they can have one of three outcomes: + +1. They accept the invitation and agree to take part, + +2. They decline the invitation, + +3. They do not respond to the invitation. + +If the person doesn't respond to the first invitation they may be sent a second invitation and could be offered a third invitation if they didn't respond to the second. + +Here is the specification for our simplified invites table: + ++----------------+---------+-------------------------------------------------------------------------------------+ +| Field | Type | Description | ++================+=========+=====================================================================================+ +| Participant ID | Integer | A unique identifier for each person. | ++----------------+---------+-------------------------------------------------------------------------------------+ +| Invite date 1 | Date | The date the person was first invited to participate. | +| | | | +| | | Every person will have a date in this field. | ++----------------+---------+-------------------------------------------------------------------------------------+ +| Invite date 2 | Date | The date a second invitation was sent. | ++----------------+---------+-------------------------------------------------------------------------------------+ +| Invite date 3 | Date | The date a third invitation was sent. | ++----------------+---------+-------------------------------------------------------------------------------------+ +| Invite outcome | Text | The outcome from the invite, one of either 'Accepted', 'Declined' or 'No response'. | ++----------------+---------+-------------------------------------------------------------------------------------+ + +: Invites specification + +Everyone receives at least one invite. Assuming a third of these respond (to either accept or decline) then two-thirds receive a follow-up invite. Of these, we assume half respond, meaning the remaining participants receive a third invite. + +Here we generate 100 rows of example data to populate our table. + +```{r} +#| code-fold: true +#| fig-cap: Generated invite table +#| fig-cap-location: top + +# set a randomisation seed for reproducibility +set.seed(seed = 1234) + +# define some parameters +start_date <- as.Date("2019-01-01") +end_date <- as.Date("2021-01-01") +rows <- 100 + +df_invites_1 <- tibble( + # create a unique id for each participant + participant_id = 1:rows, + + # create a random initial invite date between our start and end dates + invite_1_date = sample( + seq(start_date, end_date, by = "day"), + size = rows, replace = T + ), + + # create a random outcome for this participant + invite_outcome = sample( + x = c("Accepted", "Declined", "No response"), + size = rows, replace = T + ) +) + +# take a sample of participants and allocate them a second invite date +df_invites_2 <- df_invites_1 |> + # sample two thirds of participants to get a second invite + slice_sample(prop = 2 / 3) |> + # allocate a date between 10 and 30 days following the first + mutate( + invite_2_date = invite_1_date + sample(10:30, size = n(), replace = T) + ) |> + # keep just id and second date + select(participant_id, invite_2_date) + + +# take a sample of those with a second invite and allocate them a third invite date +df_invites_3 <- df_invites_2 |> + # sample half of these to get a third invite + slice_sample(prop = 1 / 2) |> + # allocate a date between 10 to 30 days following the second + mutate( + invite_3_date = invite_2_date + sample(10:30, size = n(), replace = T) + ) |> + # keep just id and second date + select(participant_id, invite_3_date) + +# combine the 2nd and 3rd invites with the first table +df_invites <- df_invites_1 |> + left_join( + y = df_invites_2, + by = "participant_id" + ) |> + left_join( + y = df_invites_3, + by = "participant_id" + ) |> + # move the outcome field after the third invite + relocate(invite_outcome, .after = invite_3_date) + +# housekeeping +rm(df_invites_1, df_invites_2, df_invites_3, start_date, end_date, rows) + +# view our data +df_invites |> + reactable(defaultPageSize = 5) +``` + +## Determine milestone outcomes + +The next step is to take our source table and convert the data into a series of milestones (and associated outcomes) that represents how our invited participants moved through the pathway. + +In our example we have five milestones to represent in our Sankey plot: + +- Our eligible population (everyone in our invites table), + +- The result from the first invitation, + +- The result from the second invitation, + +- The result from the third invitation, + +- The overall invite outcome. + +Aside from the eligible population, where everyone starts with the same value, participants will have one of several outcomes at each milestone. This step is about naming these milestones and the outcomes. + +It is important that each milestone-outcome has unique values. An outcome of 'No response' can be recorded against the first, second and third invite, and we wish to see these outcomes separately represented on the Sankey (rather than just one 'No response'), so each outcome must be made unique. In this example we prefix the outcome from each invite with the number of the invite, e.g. 'Invite 1 No response'. + +The reason for this will become clearer when we come to plot the Sankey, but for now we produce these milestone-outcomes from our invites table. + +```{r} +#| code-fold: true +#| fig-cap: Milestone-outcomes for participants +#| fig-cap-location: top + +df_milestones <- df_invites |> + mutate( + # everyone starts in the eligible population + start_population = "Eligible population", + + # work out what happened following the first invite + invite_1_outcome = case_when( + # if a second invite was sent we assume there was no outcome from the first + !is.na(invite_2_date) ~ "Invitation 1 No response", + # otherwise the overall outcome resulted from the first invite + .default = glue("Invitation 1 {invite_outcome}") + ), + + # work out what happened following the second invite + invite_2_outcome = case_when( + # if a third invite was sent we assume there was no outcome from the second + !is.na(invite_3_date) ~ "Invitation 2 No response", + # if a second invite was sent but no third then + !is.na(invite_2_date) ~ glue("Invitation 2 {invite_outcome}"), + # default to NA if neither of the above are true + .default = NA + ), + + # work out what happened following the third invite + invite_3_outcome = case_when( + # if a third invite was sent then the outcome is the overall outcome + !is.na(invite_3_date) ~ glue("Invitation 3 {invite_outcome}"), + # otherwise mark as NA + .default = NA + ) + ) |> + # exclude the dates as they are no longer needed + select(-contains("_date")) |> + # move the overall invite outcome to the end + relocate(invite_outcome, .after = invite_3_outcome) + +# view our data +df_milestones |> + reactable(defaultPageSize = 5) +``` + +## Calculate flows + +Next we take pairs of milestone-outcomes and calculate the number of participants that moved between them. + +Here we utilise the power of `dplyr::summarise` with an argument `.by` to group by our data before counting the number of unique participants who move between our start and end groups. + +For invites 2 and 3 we perform two sets of summaries: + +1. The first where the values in the `to` and `from` fields contain details. + +2. The second to capture cases where the `to` destination is NULL. This is because the participant responded at the previous invite so there was no subsequent invite. In these cases we flow the participant to the overall invite outcome.[^2] + +[^2]: If you are thinking there is a lot of repetition here, you're right. In practice I abstracted both steps to a function and passed in the parameters for the `from` and `to` variables and simplified my workflow a little, however, I'm showing it in plain form here for simplification. + +```{r} +#| code-fold: true +#| fig-cap: Flows of participants between milestones +#| fig-cap-location: top + +df_flows <- bind_rows( + + # flow from population to invite 1 + df_milestones |> + filter(!is.na(start_population) & !is.na(invite_1_outcome)) |> + rename(from = start_population, to = invite_1_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ), + + # flow from invite 1 to invite 2 (where not NA) + df_milestones |> + filter(!is.na(invite_1_outcome) & !is.na(invite_2_outcome)) |> + rename(from = invite_1_outcome, to = invite_2_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ), + + # flow from invite 1 to overall invite outcome (where invite 2 is NA) + df_milestones |> + filter(!is.na(invite_1_outcome) & is.na(invite_2_outcome)) |> + rename(from = invite_1_outcome, to = invite_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ), + + # flow from invite 2 to invite 3 (where not NA) + df_milestones |> + filter(!is.na(invite_2_outcome) & !is.na(invite_3_outcome)) |> + rename(from = invite_2_outcome, to = invite_3_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ), + + # flow from invite 2 to overall invite outcome (where invite 3 is NA) + df_milestones |> + filter(!is.na(invite_2_outcome) & is.na(invite_3_outcome)) |> + rename(from = invite_2_outcome, to = invite_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ), + + # final flow - invite 3 to overall outcome (where both are not NA) + df_milestones |> + filter(!is.na(invite_3_outcome) & !is.na(invite_outcome)) |> + rename(from = invite_3_outcome, to = invite_outcome) |> + summarise( + flow = n_distinct(participant_id, na.rm = T), + .by = c(from, to) + ) +) + +# view our data +df_flows |> + reactable(defaultPageSize = 5) +``` + +# Sankey plot + +We now have a neat little summary of movements of participants between the milestones in our recruitment pathway. However, this 'tidy' data isn't the format required by [plotly](https://plotly.com/r/sankey-diagram/), so the next steps are to prepare it ready for plotting. + +## Preparing for plotly + +Plotly expects to be fed two sets of data: + +1. Nodes - these are the milestones we have in our `from` and `to` fields, + +2. Edges - these are the flows that occur between nodes, the `flow` in our table. + +It is possible to extract this data by hand but I found using the [tidygraph](https://tidygraph.data-imaginist.com) package was much easier and more convenient. + +```{r} +df_sankey <- df_flows |> + # convert our flows data to a tidy graph object + as_tbl_graph() + +``` + +The tidygraph package splits our data into nodes and edges. We can selectively work on each by 'activating' them - here is the nodes list: + +```{r} +df_sankey |> + activate(what = "nodes") |> + as_tibble() |> + reactable(defaultPageSize = 5) +``` + +You can see each unique node name listed. The row numbers for these nodes are used as reference IDs in the edges object: + +```{r} +df_sankey |> + activate(what = "edges") |> + as_tibble() |> + reactable(defaultPageSize = 5) +``` + +We now have enough information to generate our Sankey. + +First we extract our nodes and edges to separate data frames then convert the ID values to be zero-based (starts at 0) as this is what plotly is expecting. To do this is as simple as subtracting 1 from the value of the IDs. + +Finally we pass these two dataframes to plotly's `node` and `link` function inputs to generate the plot. + +```{r} +#| code-fold: true +#| fig-cap: Our first sankey +#| fig-cap-location: top + +# extract the nodes to a dataframe +nodes <- df_sankey |> + activate(nodes) |> + data.frame() |> + mutate( + id = row_number() - 1 + ) + +# extract the edges to a dataframe +edges <- df_sankey |> + activate(edges) |> + data.frame() |> + mutate( + from = from - 1, + to = to - 1 + ) + +# plot our sankey +plot_ly( + # setup + type = "sankey", + orientation = "h", + arrangement = "snap", + + # use our node data + node = list( + label = nodes$name + ), + + # use our link data + link = list( + source = edges$from, + target = edges$to, + value = edges$flow + ) +) +``` + +Not bad! + +We can see the structure of our Sankey now. Can you see the relative proportions of participants who did or didn't respond to our first invite? Marvel at how those who responded to the first invite flow into our final outcome. How about those who didn't respond to the first invitation go on to receive a second invite? + +Plotly's charts are interactive. Try hovering your cursor over the nodes and edges to highlight them and a pop-up box will appear giving you additional details. You can reorder the vertical position of the nodes by dragging them above or below an adjacent node. + +This looks functional. + +## Styling our Sankey + +Now we have the foundations of our Sankey I'd like to move on to its presentation. Specifically I'd like to: + +- use colour coding to clearly group those who accept or decline the invite, + +- improve the readability of the node titles, + +- add additional information to the pop-up boxes when you hover over nodes and edges, and + +- control the positioning of the nodes in the plot. + +As our `nodes` and `edges` objects are dataframes it is straightforward to add this styling information directly to them. + +For the nodes object we define colours based on the name of each node and manually position them in the plot + +```{r} +#| code-fold: true +#| fig-cap: Styling the nodes dataframe +#| fig-cap-location: top + +# get the eligible population as a single value +# NB, will be used to work out % amounts in each node and edge +temp_eligible_pop <- df_flows |> + filter(from == "Eligible population") |> + summarise(total = sum(flow, na.rm = T)) |> + pull(total) + +# style our nodes object +nodes <- nodes |> + mutate( + # colour ---- + # add colour definitions, green for accepted, red for declined + colour = case_when( + str_detect(name, "Accepted") ~ "#44bd32", + str_detect(name, "Declined") ~ "#c23616", + str_detect(name, "No response") ~ "#7f8fa6", + str_detect(name, "Eligible population") ~ "#7f8fa6" + ), + + # add a semi-transparent colour for the edges based on node colours + colour_fade = col2hcl(colour = colour, alpha = 0.3), + + # positioning ---- + # NB, I found that to position nodes you need to supply both + # horizontal and vertical positions + # NNB, it was a bit of trial and error to get the these positions just + # right + + # horizontal positions (0 = left, 1 = right) + x = case_when( + str_detect(name, "Eligible population") ~ 1, + str_detect(name, "Invitation 1") ~ 2, + str_detect(name, "Invitation 2") ~ 3, + str_detect(name, "Invitation 3") ~ 4, + .default = 5 + ) |> rescale(to = c(0.001, 0.9)), + + # vertical position (1 = bottom, 0 = top) + y = case_when( + str_detect(name, "Eligible population") ~ 5, + # invite 1 + str_detect(name, "Invitation 1 Accepted") ~ 1, + str_detect(name, "Invitation 1 No response") ~ 5, + str_detect(name, "Invitation 1 Declined") ~ 8.5, + # invite 2 + str_detect(name, "Invitation 2 Accepted") ~ 2, + str_detect(name, "Invitation 2 No response") ~ 5, + str_detect(name, "Invitation 2 Declined") ~ 7.8, + # invite 3 + str_detect(name, "Invitation 3 Accepted") ~ 2.7, + str_detect(name, "Invitation 3 No response") ~ 5.8, + str_detect(name, "Invitation 3 Declined") ~ 7.2, + # final outcomes + str_detect(name, "Accepted") ~ 1, + str_detect(name, "No response") ~ 5, + str_detect(name, "Declined") ~ 8, + .default = 5 + ) |> rescale(to = c(0.001, 0.999)) + ) |> + # add in a custom field to show the percentage flow + left_join( + y = df_flows |> + group_by(to) |> + summarise( + flow = sum(flow, na.rm = T), + flow_perc = percent(flow / temp_eligible_pop, accuracy = 0.1), + ) |> + select(name = to, flow_perc), + by = "name" + ) + +# view our nodes data +nodes |> + reactable(defaultPageSize = 5) +``` + +Next we move to styling the edges, which is a much simpler prospect: + +```{r} +#| code-fold: true +#| fig-cap: Styling the edges dataframe +#| fig-cap-location: top + +edges <- edges |> + mutate( + # add a label for each flow to tell us how many people are in each + label = number(flow, big.mark = ","), + # add a percentage flow figure + flow_perc = percent(flow / temp_eligible_pop, accuracy = 0.1) + ) |> + # add the faded colour from our nodes object to match the destinations + left_join( + y = nodes |> select(to = id, colour_fade), + by = "to" + ) + +# view our edges data +edges |> + reactable(defaultPageSize = 5) +``` + +We now have stylised node and edge tables ready and can bring it all together. Note the use of `customdata` and `hovertemplate` help to bring in additional information and styling to the pop-up boxes that appear when you hover over each flow and node. + +```{r} +#| code-fold: true +#| fig-cap: A stylish Sankey +#| fig-cap-location: top + +# plot our stylised sankey +plot_ly( + # setup + type = "sankey", + orientation = "h", + arrangement = "snap", + + # use our node data + node = list( + label = nodes$name, + color = nodes$colour, + x = nodes$x, + y = nodes$y, + customdata = nodes$flow_perc, + hovertemplate = "%{label}
%{value} participants
%{customdata} of eligible population" + ), + + # use our edge data + link = list( + source = edges$from, + target = edges$to, + value = edges$flow, + label = edges$label, + color = edges$colour_fade, + customdata = edges$flow_perc, + hovertemplate = "%{source.label} → %{target.label}
%{value} participants
%{customdata} of eligible population" + ) +) |> + layout( + font = list( + family = "Arial, Helvetica, sans-serif", + size = 12 + ), + # make the background transparent (also removes the text shadow) + paper_bgcolor = "rgba(0,0,0,0)" + ) |> + config(responsive = T) +``` + +# Conclusion + +Creating Sankey plots in R using plotly is an effective way to visualise patient pathways. + +In our project we embedded Sankey plots within an interactive [Shiny](https://shiny.posit.co/) app which allows for selective filters that update the resulting plot. This allowed us to quickly compare the effects of different models of delivering the screening programme, geography, deprivation levels, patient demographic, or any combination of these. + +Their use has helped the programme team better understand patient flows through the pathway, where the points of drop-off are and compare / contrast the effects of different models of delivering the screening programme on patient engagement. + +Feedback from external stakeholders has been positive too, noting how easy it is to engage with and understand this style of presentation. + +In this blog post we have wrangled a dataset to describe how people flow between steps in a process and then produced a Sankey diagram with some stylistic touches to make an effective visualisation. + +I hope this post helps you feel better prepared to use Sankeys in your work. diff --git a/blogs/posts/2024-02-28_sankey_plot/renv.lock b/blogs/posts/2024-02-28_sankey_plot/renv.lock new file mode 100644 index 0000000..3c45ea2 --- /dev/null +++ b/blogs/posts/2024-02-28_sankey_plot/renv.lock @@ -0,0 +1,1704 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "DBI": { + "Package": "DBI", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "065ae649b05f1ff66bb0c793107508f5" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "6b868847b365672d6c1677b1608da9ed" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" + }, + "bit64": { + "Package": "bit64", + "Version": "4.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "e84984bf5f12a18628d9a02322128dfd" + }, + "blob": { + "Package": "blob", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "rlang", + "vctrs" + ], + "Hash": "40415719b5a479b87949f3aa0aee737c" + }, + "broom": { + "Package": "broom", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "8fcc818f3b9887aebaf206f141437cc9" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "conflicted": { + "Package": "conflicted", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "memoise", + "rlang" + ], + "Hash": "bb097fccb22d156624fd07cd2894ddb6" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "ab12c7b080a57475248a30f4db6298c0" + }, + "curl": { + "Package": "curl", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ff51697d9205fe715f29e7171e874c6e" + }, + "data.table": { + "Package": "data.table", + "Version": "1.16.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "2e00b378fc3be69c865120d9f313039a" + }, + "dbplyr": { + "Package": "dbplyr", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "DBI", + "R", + "R6", + "blob", + "cli", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "utils", + "vctrs", + "withr" + ], + "Hash": "39b2e002522bfd258039ee4e889e0fd1" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "dtplyr": { + "Package": "dtplyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "data.table", + "dplyr", + "glue", + "lifecycle", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "54ed3ea01b11e81a86544faaecfef8e2" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "forcats": { + "Package": "forcats", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "tibble" + ], + "Hash": "1a0a9a3d5083d0d573c4214576f1e690" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "gargle": { + "Package": "gargle", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "fs", + "glue", + "httr", + "jsonlite", + "lifecycle", + "openssl", + "rappdirs", + "rlang", + "stats", + "utils", + "withr" + ], + "Hash": "fc0b272e5847c58cd5da9b20eedbd026" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "googledrive": { + "Package": "googledrive", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gargle", + "glue", + "httr", + "jsonlite", + "lifecycle", + "magrittr", + "pillar", + "purrr", + "rlang", + "tibble", + "utils", + "uuid", + "vctrs", + "withr" + ], + "Hash": "e99641edef03e2a5e87f0a0b1fcc97f4" + }, + "googlesheets4": { + "Package": "googlesheets4", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cli", + "curl", + "gargle", + "glue", + "googledrive", + "httr", + "ids", + "lifecycle", + "magrittr", + "methods", + "purrr", + "rematch2", + "rlang", + "tibble", + "utils", + "vctrs", + "withr" + ], + "Hash": "d6db1667059d027da730decdc214b959" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "haven": { + "Package": "haven", + "Version": "2.5.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "forcats", + "hms", + "lifecycle", + "methods", + "readr", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "9171f898db9d9c4c1b2c745adc2c1ef1" + }, + "here": { + "Package": "here", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "rprojroot" + ], + "Hash": "24b224366f9c2e7534d2344d10d59211" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "ids": { + "Package": "ids", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "openssl", + "uuid" + ], + "Hash": "99df65cfef20e525ed38c3d2577f7190" + }, + "igraph": { + "Package": "igraph", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "cli", + "cpp11", + "grDevices", + "graphics", + "lifecycle", + "magrittr", + "methods", + "pkgconfig", + "rlang", + "stats", + "utils", + "vctrs" + ], + "Hash": "c03878b48737a0e2da3b772d7b2e22da" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "later": { + "Package": "later", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "rlang" + ], + "Hash": "a3e051d405326b8b0012377434c62b37" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "modelr": { + "Package": "modelr", + "Version": "0.1.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "magrittr", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "vctrs" + ], + "Hash": "4f50122dc256b1b6996a4703fecea821" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "openssl": { + "Package": "openssl", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "d413e0fef796c9401a4419485f709ca1" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "plotly": { + "Package": "plotly", + "Version": "4.10.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RColorBrewer", + "base64enc", + "crosstalk", + "data.table", + "digest", + "dplyr", + "ggplot2", + "htmltools", + "htmlwidgets", + "httr", + "jsonlite", + "lazyeval", + "magrittr", + "promises", + "purrr", + "rlang", + "scales", + "tibble", + "tidyr", + "tools", + "vctrs", + "viridisLite" + ], + "Hash": "a1ac5c03ad5ad12b9d1597e00e23c3dd" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "promises": { + "Package": "promises", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "Rcpp", + "fastmap", + "later", + "magrittr", + "rlang", + "stats" + ], + "Hash": "434cd5388a3979e74be5c219bcd6e77d" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "ragg": { + "Package": "ragg", + "Version": "1.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "0595fe5e47357111f29ad19101c7d271" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "reactR": { + "Package": "reactR", + "Version": "0.6.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "b8e3d93f508045812f47136c7c44c251" + }, + "reactable": { + "Package": "reactable", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "digest", + "htmltools", + "htmlwidgets", + "jsonlite", + "reactR" + ], + "Hash": "6069eb2a6597963eae0605c1875ff14c" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "reprex": { + "Package": "reprex", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "callr", + "cli", + "clipr", + "fs", + "glue", + "knitr", + "lifecycle", + "rlang", + "rmarkdown", + "rstudioapi", + "utils", + "withr" + ], + "Hash": "97b1d5361a24d9fb588db7afe3e5bcbf" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.17.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5f90cd73946d706cfe26024294236113" + }, + "rvest": { + "Package": "rvest", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "httr", + "lifecycle", + "magrittr", + "rlang", + "selectr", + "tibble", + "xml2" + ], + "Hash": "0bcf0c6f274e90ea314b812a6d19a519" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "selectr": { + "Package": "selectr", + "Version": "0.4-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "methods", + "stringr" + ], + "Hash": "3838071b66e0c566d55cc26bd6e27bf4" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "sys": { + "Package": "sys", + "Version": "3.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "de342ebfebdbf40477d0758d05426646" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle" + ], + "Hash": "213b6b8ed5afbf934843e6c3b090d418" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle", + "systemfonts" + ], + "Hash": "5142f8bc78ed3d819d26461b641627ce" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidygraph": { + "Package": "tidygraph", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "cli", + "cpp11", + "dplyr", + "igraph", + "lifecycle", + "magrittr", + "pillar", + "rlang", + "stats", + "tibble", + "tidyr", + "tools", + "utils" + ], + "Hash": "2149824d406f233b57b087be72c5f163" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tidyverse": { + "Package": "tidyverse", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "cli", + "conflicted", + "dbplyr", + "dplyr", + "dtplyr", + "forcats", + "ggplot2", + "googledrive", + "googlesheets4", + "haven", + "hms", + "httr", + "jsonlite", + "lubridate", + "magrittr", + "modelr", + "pillar", + "purrr", + "ragg", + "readr", + "readxl", + "reprex", + "rlang", + "rstudioapi", + "rvest", + "stringr", + "tibble", + "tidyr", + "xml2" + ], + "Hash": "c328568cd14ea89a83bd4ca7f54ae07e" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "uuid": { + "Package": "uuid", + "Version": "1.2-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "34e965e62a41fcafb1ca60e9b142085b" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/assets/sankey_example.rds b/blogs/posts/2024-02-28_sankey_plot/sankey_example.rds similarity index 100% rename from blogs/posts/assets/sankey_example.rds rename to blogs/posts/2024-02-28_sankey_plot/sankey_example.rds diff --git a/blogs/posts/2023-03-21-rstudio-tips/appearance.png b/blogs/posts/2024-03-21_rstudio-tips/appearance.png similarity index 100% rename from blogs/posts/2023-03-21-rstudio-tips/appearance.png rename to blogs/posts/2024-03-21_rstudio-tips/appearance.png diff --git a/blogs/posts/2023-03-21-rstudio-tips/command-palette.png b/blogs/posts/2024-03-21_rstudio-tips/command-palette.png similarity index 100% rename from blogs/posts/2023-03-21-rstudio-tips/command-palette.png rename to blogs/posts/2024-03-21_rstudio-tips/command-palette.png diff --git a/blogs/posts/2023-03-21-rstudio-tips/index.qmd b/blogs/posts/2024-03-21_rstudio-tips/index.qmd similarity index 98% rename from blogs/posts/2023-03-21-rstudio-tips/index.qmd rename to blogs/posts/2024-03-21_rstudio-tips/index.qmd index b8f3e8e..fcb3079 100644 --- a/blogs/posts/2023-03-21-rstudio-tips/index.qmd +++ b/blogs/posts/2024-03-21_rstudio-tips/index.qmd @@ -5,6 +5,12 @@ date: "2024-03-21" categories: [learning, R] --- +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + ## Coffee & Coding In a recent Coffee & Coding session we chatted about tips and tricks for [RStudio](https://posit.co/products/open-source/rstudio/), the popular and free Integrated Development Environment (IDE) that many Strategy Unit analysts use to write R code. diff --git a/blogs/posts/2023-03-21-rstudio-tips/options.png b/blogs/posts/2024-03-21_rstudio-tips/options.png similarity index 100% rename from blogs/posts/2023-03-21-rstudio-tips/options.png rename to blogs/posts/2024-03-21_rstudio-tips/options.png diff --git a/blogs/posts/2024-03-21_rstudio-tips/renv.lock b/blogs/posts/2024-03-21_rstudio-tips/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/blogs/posts/2024-03-21_rstudio-tips/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/blogs/posts/2023-03-21-rstudio-tips/rstudio-sections.png b/blogs/posts/2024-03-21_rstudio-tips/rstudio-sections.png similarity index 100% rename from blogs/posts/2023-03-21-rstudio-tips/rstudio-sections.png rename to blogs/posts/2024-03-21_rstudio-tips/rstudio-sections.png diff --git a/blogs/posts/2023-03-21-rstudio-tips/wand.png b/blogs/posts/2024-03-21_rstudio-tips/wand.png similarity index 100% rename from blogs/posts/2023-03-21-rstudio-tips/wand.png rename to blogs/posts/2024-03-21_rstudio-tips/wand.png diff --git a/blogs/posts/2024-05-13_one-year-coffee-code.qmd b/blogs/posts/2024-05-13_one-year-coffee-code/index.qmd similarity index 94% rename from blogs/posts/2024-05-13_one-year-coffee-code.qmd rename to blogs/posts/2024-05-13_one-year-coffee-code/index.qmd index 01e5330..a1cfae1 100644 --- a/blogs/posts/2024-05-13_one-year-coffee-code.qmd +++ b/blogs/posts/2024-05-13_one-year-coffee-code/index.qmd @@ -1,29 +1,35 @@ ---- -title: "One year of coffee & coding" -author: - - Rhian Davies -date: "2024-05-13" -categories: [learning] -reference-location: margin -citation-location: margin ---- - -The data science team have been running coffee & coding sessions for just over a year now. When I joined that Strategy Unit, I was really pleased to see these sessions running as I think making time to discuss and share technical knowledge is highly valuable, especially as an organisation grows. - -Coffee and coding sessions run every two weeks and usually take the form of a short presentation, followed by a discussion. Although we have had a variety of different sessions including live coding demos and show and tell for projects. - -We figured it would be a good idea to do a quick survey of attendees to make sure that the sessions were beneficial and see if there were any suggestions for future sessions. We had 11 responses, all of which were really positive, with 90% agreeing that the sessions are interesting, and over 80% saying that they learn new things. Respondents said that the sessions were well varied across the technical spectrum and that they "almost always learn something useful". - -The two main themes of the results were that sessions were _inclusive_ and _sparked collaboration._ ✨ - -> I like that everyone can contribute - -> It's great seeing what else people are doing - -> I get more ideas for future projects - -Some of the main suggestions included more content for newer programmers and encouraging the wider analytical team to share real project examples. - -So with that, why not consider presenting? The sessions are informal and everyone is welcome to contribute. If you've got something to share, please let a member of the data science team know. - -As a reminder, materials for our previous sessions are available under [Presentations](https://the-strategy-unit.github.io/data_science/presentations/). +--- +title: "One year of coffee & coding" +author: + - Rhian Davies +date: "2024-05-13" +categories: [learning] +reference-location: margin +citation-location: margin +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +The data science team have been running coffee & coding sessions for just over a year now. When I joined that Strategy Unit, I was really pleased to see these sessions running as I think making time to discuss and share technical knowledge is highly valuable, especially as an organisation grows. + +Coffee and coding sessions run every two weeks and usually take the form of a short presentation, followed by a discussion. Although we have had a variety of different sessions including live coding demos and show and tell for projects. + +We figured it would be a good idea to do a quick survey of attendees to make sure that the sessions were beneficial and see if there were any suggestions for future sessions. We had 11 responses, all of which were really positive, with 90% agreeing that the sessions are interesting, and over 80% saying that they learn new things. Respondents said that the sessions were well varied across the technical spectrum and that they "almost always learn something useful". + +The two main themes of the results were that sessions were _inclusive_ and _sparked collaboration._ ✨ + +> I like that everyone can contribute + +> It's great seeing what else people are doing + +> I get more ideas for future projects + +Some of the main suggestions included more content for newer programmers and encouraging the wider analytical team to share real project examples. + +So with that, why not consider presenting? The sessions are informal and everyone is welcome to contribute. If you've got something to share, please let a member of the data science team know. + +As a reminder, materials for our previous sessions are available under [Presentations](https://the-strategy-unit.github.io/data_science/presentations/). diff --git a/blogs/posts/2024-05-13_one-year-coffee-code/renv.lock b/blogs/posts/2024-05-13_one-year-coffee-code/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/blogs/posts/2024-05-13_one-year-coffee-code/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-05-22-storing-data-safely/azure_python.ipynb b/blogs/posts/2024-05-22_storing-data-safely/azure_python.ipynb similarity index 100% rename from blogs/posts/2024-05-22-storing-data-safely/azure_python.ipynb rename to blogs/posts/2024-05-22_storing-data-safely/azure_python.ipynb diff --git a/blogs/posts/2024-05-22-storing-data-safely/index.qmd b/blogs/posts/2024-05-22_storing-data-safely/index.qmd similarity index 84% rename from blogs/posts/2024-05-22-storing-data-safely/index.qmd rename to blogs/posts/2024-05-22_storing-data-safely/index.qmd index f1e055a..540295b 100644 --- a/blogs/posts/2024-05-22-storing-data-safely/index.qmd +++ b/blogs/posts/2024-05-22_storing-data-safely/index.qmd @@ -1,227 +1,234 @@ ---- -title: "Storing data safely" -author: - - "[YiWen Hon](mailto:yiwen.hon1@nhs.net)" - - "[Matt Dray](mailto:matt.dray@nhs.net)" -date: "2024-05-22" -categories: [learning, R, Python] -notebook-view: - - notebook: azure_python.ipynb - title: "Interacting with Azure Storage with Python" - ---- - -## Coffee & Coding - -In a recent Coffee & Coding session we chatted about storing data safely for use in Reproducible Analytical Pipelines (RAP), and [the slides from the presentation are now available](https://the-strategy-unit.github.io/data_science/presentations/2024-05-16_store-data-safely/). We discussed the use of [Posit Connect Pins](https://docs.posit.co/connect/user/pins/) and [Azure Storage](https://azure.microsoft.com/en-gb/products/storage/blobs/). - -In order to avoid duplication, this blog post will not cover the pros and cons of each approach, and will instead focus on documenting the code that was used in our live demonstrations. I would recommend that you look through the slides before using the code in this blogpost and have them alongside, as they provide lots of useful context! - -## Posit Connect Pins - - -```{r} -#| eval: false - - -# A brief intro to using {pins} to store, version, share and protect a dataset -# on Posit Connect. Documentation: https://pins.rstudio.com/ - - -# Setup ------------------------------------------------------------------- - - -install.packages(c("pins","dplyr")) # if not yet installed - -suppressPackageStartupMessages({ - library(pins) - library(dplyr) # for wrangling and the 'starwars' demo dataset -}) - -board <- board_connect() # will error if you haven't authenticated before -# Error in `check_auth()`: ! auth = `auto` has failed to find a way to authenticate: -# • `server` and `key` not provided for `auth = 'manual'` -# • Can't find CONNECT_SERVER and CONNECT_API_KEY envvars for `auth = 'envvar'` -# • rsconnect package not installed for `auth = 'rsconnect'` -# Run `rlang::last_trace()` to see where the error occurred. - -# To authenticate -# In RStudio: Tools > Global Options > Publishing > Connect... > Posit Connect -# public URL of the Strategy Unit Posit Connect Server: connect.strategyunitwm.nhs.uk -# Your browser will open to the Posit Connect web page and you're prompted to -# for your password. Enter it and you'll be authenticated. - -# Once authenticated -board <- board_connect() -# Connecting to Posit Connect 2024.03.0 at -# - -board |> pin_list() # see all the pins on that board - - -# Create a pin ------------------------------------------------------------ - - -# Write a dataset to the board as a pin -board |> pin_write( - x = starwars, - name = "starwars_demo" -) -# Guessing `type = 'rds'` -# Writing to pin 'matt.dray/starwars_demo' - -board |> pin_exists("starwars_demo") -# ! Use a fully specified name including user name: "matt.dray/starwars_demo", -# not "starwars_demo". -# [1] TRUE - -pin_name <- "matt.dray/starwars_demo" - -board |> pin_exists(pin_name) # logical, TRUE/FALSE -board |> pin_meta(pin_name) # metadata, see also 'metadata' arg in pin_write() -board |> pin_browse(pin_name) # view the pin in the browser - - -# Permissions ------------------------------------------------------------- - - -# You can let people see and edit a pin. Log into Posit Connect and select the -# pin under 'Content'. In the 'Settings' panel on the right-hand side, adjust -# the 'sharing' options in the 'Access' tab. - - -# Overwrite and version --------------------------------------------------- - - -starwars_droids <- starwars |> - filter(species == "Droid") # beep boop - -board |> pin_write( - starwars_droids, - pin_name, - type = "rds" -) -# Writing to pin 'matt.dray/starwars_demo' - -board |> pin_versions(pin_name) # see version history -board |> pin_versions_prune(pin_name, n = 1) # remove history -board |> pin_versions(pin_name) - -# What if you try to overwrite the data but it hasn't changed? -board |> pin_write( - starwars_droids, - pin_name, - type = "rds" -) -# ! The hash of pin "matt.dray/starwars_demo" has not changed. -# • Your pin will not be stored. - - -# Use the pin ------------------------------------------------------------- - - -# You can read a pin to your local machine, or access it from a Quarto file -# or Shiny app hosted on Connect, for example. If the output and the pin are -# both on Connect, no authentication is required; the board is defaulted to -# the Posit Connect instance where they're both hosted. - -board |> - pin_read(pin_name) |> # like you would use e.g. read_csv - with(data = _, plot(mass, height)) # wow! - - -# Delete pin -------------------------------------------------------------- - - -board |> pin_exists(pin_name) # logical, good function for error handling -board |> pin_delete(pin_name) -board |> pin_exists(pin_name) - -``` - -## Azure Storage in R - -You will need an .Renviron file with the four environment variables listed below for the code to work. This .Renviron file should be ignored by git. You can share the contents of .Renviron files with other team members via Teams, email, or Sharepoint. - -Below is a sample .Renviron file - - -``` -AZ_STORAGE_EP=https://STORAGEACCOUNT.blob.core.windows.net/ -AZ_STORAGE_CONTAINER=container-name -AZ_TENANT_ID=long-sequence-of-numbers-and-letters -AZ_APP_ID=another-long-sequence-of-numbers-and-letters -``` - -```{r} -#| eval: false - -install.packages(c("AzureAuth","AzureStor", "arrow")) # if not yet installed - -# Load all environment variables -ep_uri <- Sys.getenv("AZ_STORAGE_EP") -app_id <- Sys.getenv("AZ_APP_ID") -container_name <- Sys.getenv("AZ_STORAGE_CONTAINER") -tenant <- Sys.getenv("AZ_TENANT_ID") - -# Authenticate -token <- AzureAuth::get_azure_token( - "https://storage.azure.com", - tenant = tenant, - app = app_id, - auth_type = "device_code", -) - -# If you have not authenticated before, you will be taken to an external page to -# authenticate!Use your mlcsu.nhs.uk account. - -# Connect to container -endpoint <- AzureStor::blob_endpoint(ep_uri, token = token) -container <- AzureStor::storage_container(endpoint, container_name) - -# List files in container -blob_list <- AzureStor::list_blobs(container) - -# If you get a 403 error when trying to interact with the container, you may -# have to clear your Azure token and re-authenticate using a different browser. -# Use AzureAuth::clean_token_directory() to clear your token, then repeat the -# AzureAuth::get_azure_token() step above. - -# Upload specific file to container -AzureStor::storage_upload(container, "data/ronald.jpeg", "newdir/ronald.jpeg") - -# Upload contents of a local directory to container -AzureStor::storage_multiupload(container, "data/*", "newdir") - -# Check files have uploaded -blob_list <- AzureStor::list_blobs(container) - -# Load file directly from Azure container -df_from_azure <- AzureStor::storage_read_csv( - container, - "newdir/cats.csv", - show_col_types = FALSE -) - -# Load file directly from Azure container (by temporarily downloading file -# and storing it in memory) -parquet_in_memory <- AzureStor::storage_download( - container, src = "newdir/cats.parquet", dest = NULL -) -parq_df <- arrow::read_parquet(parquet_in_memory) - -# Delete from Azure container (!!!) -for (blobfile in blob_list$name) { - AzureStor::delete_storage_file(container, blobfile) -} -``` - -## Azure Storage in Python - -This will use the same environment variables as the R version, just stored in a .env file instead. - -We didn't cover this in the presentation, so it's not in the slides, but the code should be self-explanatory. - -{{< embed azure_python.ipynb echo=true >}} - - +--- +title: "Storing data safely" +author: + - "[YiWen Hon](mailto:yiwen.hon1@nhs.net)" + - "[Matt Dray](mailto:matt.dray@nhs.net)" +date: "2024-05-22" +categories: [learning, R, Python] +notebook-view: + - notebook: azure_python.ipynb + title: "Interacting with Azure Storage with Python" + +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +## Coffee & Coding + +In a recent Coffee & Coding session we chatted about storing data safely for use in Reproducible Analytical Pipelines (RAP), and [the slides from the presentation are now available](https://the-strategy-unit.github.io/data_science/presentations/2024-05-16_store-data-safely/). We discussed the use of [Posit Connect Pins](https://docs.posit.co/connect/user/pins/) and [Azure Storage](https://azure.microsoft.com/en-gb/products/storage/blobs/). + +In order to avoid duplication, this blog post will not cover the pros and cons of each approach, and will instead focus on documenting the code that was used in our live demonstrations. I would recommend that you look through the slides before using the code in this blogpost and have them alongside, as they provide lots of useful context! + +## Posit Connect Pins + + +```{r} +#| eval: false + + +# A brief intro to using {pins} to store, version, share and protect a dataset +# on Posit Connect. Documentation: https://pins.rstudio.com/ + + +# Setup ------------------------------------------------------------------- + + +install.packages(c("pins", "dplyr")) # if not yet installed + +suppressPackageStartupMessages({ + library(pins) + library(dplyr) # for wrangling and the 'starwars' demo dataset +}) + +board <- board_connect() # will error if you haven't authenticated before +# Error in `check_auth()`: ! auth = `auto` has failed to find a way to authenticate: +# • `server` and `key` not provided for `auth = 'manual'` +# • Can't find CONNECT_SERVER and CONNECT_API_KEY envvars for `auth = 'envvar'` +# • rsconnect package not installed for `auth = 'rsconnect'` +# Run `rlang::last_trace()` to see where the error occurred. + +# To authenticate +# In RStudio: Tools > Global Options > Publishing > Connect... > Posit Connect +# public URL of the Strategy Unit Posit Connect Server: connect.strategyunitwm.nhs.uk +# Your browser will open to the Posit Connect web page and you're prompted to +# for your password. Enter it and you'll be authenticated. + +# Once authenticated +board <- board_connect() +# Connecting to Posit Connect 2024.03.0 at +# + +board |> pin_list() # see all the pins on that board + + +# Create a pin ------------------------------------------------------------ + + +# Write a dataset to the board as a pin +board |> pin_write( + x = starwars, + name = "starwars_demo" +) +# Guessing `type = 'rds'` +# Writing to pin 'matt.dray/starwars_demo' + +board |> pin_exists("starwars_demo") +# ! Use a fully specified name including user name: "matt.dray/starwars_demo", +# not "starwars_demo". +# [1] TRUE + +pin_name <- "matt.dray/starwars_demo" + +board |> pin_exists(pin_name) # logical, TRUE/FALSE +board |> pin_meta(pin_name) # metadata, see also 'metadata' arg in pin_write() +board |> pin_browse(pin_name) # view the pin in the browser + + +# Permissions ------------------------------------------------------------- + + +# You can let people see and edit a pin. Log into Posit Connect and select the +# pin under 'Content'. In the 'Settings' panel on the right-hand side, adjust +# the 'sharing' options in the 'Access' tab. + + +# Overwrite and version --------------------------------------------------- + + +starwars_droids <- starwars |> + filter(species == "Droid") # beep boop + +board |> pin_write( + starwars_droids, + pin_name, + type = "rds" +) +# Writing to pin 'matt.dray/starwars_demo' + +board |> pin_versions(pin_name) # see version history +board |> pin_versions_prune(pin_name, n = 1) # remove history +board |> pin_versions(pin_name) + +# What if you try to overwrite the data but it hasn't changed? +board |> pin_write( + starwars_droids, + pin_name, + type = "rds" +) +# ! The hash of pin "matt.dray/starwars_demo" has not changed. +# • Your pin will not be stored. + + +# Use the pin ------------------------------------------------------------- + + +# You can read a pin to your local machine, or access it from a Quarto file +# or Shiny app hosted on Connect, for example. If the output and the pin are +# both on Connect, no authentication is required; the board is defaulted to +# the Posit Connect instance where they're both hosted. + +board |> + pin_read(pin_name) |> # like you would use e.g. read_csv + with(data = _, plot(mass, height)) # wow! + + +# Delete pin -------------------------------------------------------------- + + +board |> pin_exists(pin_name) # logical, good function for error handling +board |> pin_delete(pin_name) +board |> pin_exists(pin_name) + +``` + +## Azure Storage in R + +You will need an .Renviron file with the four environment variables listed below for the code to work. This .Renviron file should be ignored by git. You can share the contents of .Renviron files with other team members via Teams, email, or Sharepoint. + +Below is a sample .Renviron file + + +``` +AZ_STORAGE_EP=https://STORAGEACCOUNT.blob.core.windows.net/ +AZ_STORAGE_CONTAINER=container-name +AZ_TENANT_ID=long-sequence-of-numbers-and-letters +AZ_APP_ID=another-long-sequence-of-numbers-and-letters +``` + +```{r} +#| eval: false + +install.packages(c("AzureAuth", "AzureStor", "arrow")) # if not yet installed + +# Load all environment variables +ep_uri <- Sys.getenv("AZ_STORAGE_EP") +app_id <- Sys.getenv("AZ_APP_ID") +container_name <- Sys.getenv("AZ_STORAGE_CONTAINER") +tenant <- Sys.getenv("AZ_TENANT_ID") + +# Authenticate +token <- AzureAuth::get_azure_token( + "https://storage.azure.com", + tenant = tenant, + app = app_id, + auth_type = "device_code", +) + +# If you have not authenticated before, you will be taken to an external page to +# authenticate!Use your mlcsu.nhs.uk account. + +# Connect to container +endpoint <- AzureStor::blob_endpoint(ep_uri, token = token) +container <- AzureStor::storage_container(endpoint, container_name) + +# List files in container +blob_list <- AzureStor::list_blobs(container) + +# If you get a 403 error when trying to interact with the container, you may +# have to clear your Azure token and re-authenticate using a different browser. +# Use AzureAuth::clean_token_directory() to clear your token, then repeat the +# AzureAuth::get_azure_token() step above. + +# Upload specific file to container +AzureStor::storage_upload(container, "data/ronald.jpeg", "newdir/ronald.jpeg") + +# Upload contents of a local directory to container +AzureStor::storage_multiupload(container, "data/*", "newdir") + +# Check files have uploaded +blob_list <- AzureStor::list_blobs(container) + +# Load file directly from Azure container +df_from_azure <- AzureStor::storage_read_csv( + container, + "newdir/cats.csv", + show_col_types = FALSE +) + +# Load file directly from Azure container (by temporarily downloading file +# and storing it in memory) +parquet_in_memory <- AzureStor::storage_download( + container, + src = "newdir/cats.parquet", dest = NULL +) +parq_df <- arrow::read_parquet(parquet_in_memory) + +# Delete from Azure container (!!!) +for (blobfile in blob_list$name) { + AzureStor::delete_storage_file(container, blobfile) +} +``` + +## Azure Storage in Python + +This will use the same environment variables as the R version, just stored in a .env file instead. + +We didn't cover this in the presentation, so it's not in the slides, but the code should be self-explanatory. + +{{< embed azure_python.ipynb echo=true >}} + + diff --git a/blogs/posts/2024-05-22_storing-data-safely/renv.lock b/blogs/posts/2024-05-22_storing-data-safely/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/blogs/posts/2024-05-22_storing-data-safely/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-08-08-map-and-nest/example_bar_chart.png b/blogs/posts/2024-08-08_map-and-nest/example_bar_chart.png similarity index 100% rename from blogs/posts/2024-08-08-map-and-nest/example_bar_chart.png rename to blogs/posts/2024-08-08_map-and-nest/example_bar_chart.png diff --git a/blogs/posts/2024-08-08-map-and-nest/index.qmd b/blogs/posts/2024-08-08_map-and-nest/index.qmd similarity index 100% rename from blogs/posts/2024-08-08-map-and-nest/index.qmd rename to blogs/posts/2024-08-08_map-and-nest/index.qmd diff --git a/blogs/posts/2024-08-08_map-and-nest/renv.lock b/blogs/posts/2024-08-08_map-and-nest/renv.lock new file mode 100644 index 0000000..0c305a8 --- /dev/null +++ b/blogs/posts/2024-08-08_map-and-nest/renv.lock @@ -0,0 +1,1026 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "DT": { + "Package": "DT", + "Version": "0.33", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "crosstalk", + "htmltools", + "htmlwidgets", + "httpuv", + "jquerylib", + "jsonlite", + "magrittr", + "promises" + ], + "Hash": "64ff3427f559ce3f2597a4fe13255cb6" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "6b868847b365672d6c1677b1608da9ed" + }, + "Rttf2pt1": { + "Package": "Rttf2pt1", + "Version": "1.3.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "a60168d094ca7e4de5106d60001c3964" + }, + "StrategyUnitTheme": { + "Package": "StrategyUnitTheme", + "Version": "0.1.7", + "Source": "GitHub", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteUsername": "the-strategy-unit", + "RemoteRepo": "StrategyUnitTheme", + "RemoteRef": "main", + "RemoteSha": "09397568ef66d2cf757b677f34dbd9f7fd4b02de", + "Requirements": [ + "ggplot2", + "rmarkdown", + "xaringan" + ], + "Hash": "8066991ff77491786e0c651de8ea90bd" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "ab12c7b080a57475248a30f4db6298c0" + }, + "curl": { + "Package": "curl", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ff51697d9205fe715f29e7171e874c6e" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "extrafont": { + "Package": "extrafont", + "Version": "0.19", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rttf2pt1", + "extrafontdb", + "grDevices", + "utils" + ], + "Hash": "03d9939b37164f34e0522fef13e63158" + }, + "extrafontdb": { + "Package": "extrafontdb", + "Version": "1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "a861555ddec7451c653b40e713166c6f" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "gridExtra": { + "Package": "gridExtra", + "Version": "2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "graphics", + "grid", + "gtable", + "utils" + ], + "Hash": "7d7f283939f563670a697165b2cf5560" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "httpuv": { + "Package": "httpuv", + "Version": "1.6.15", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "Rcpp", + "later", + "promises", + "utils" + ], + "Hash": "d55aa087c47a63ead0f6fc10f8fa1ee0" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "later": { + "Package": "later", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "rlang" + ], + "Hash": "a3e051d405326b8b0012377434c62b37" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "plyr": { + "Package": "plyr", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp" + ], + "Hash": "6b8177fd19982f0020743fadbfdbd933" + }, + "promises": { + "Package": "promises", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "Rcpp", + "fastmap", + "later", + "magrittr", + "rlang", + "stats" + ], + "Hash": "434cd5388a3979e74be5c219bcd6e77d" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "servr": { + "Package": "servr", + "Version": "0.32", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "httpuv", + "jsonlite", + "mime", + "xfun" + ], + "Hash": "63e4ea2379e79cf18813dd0d8146d27c" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "waffle": { + "Package": "waffle", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DT", + "R", + "RColorBrewer", + "curl", + "extrafont", + "ggplot2", + "grid", + "gridExtra", + "gtable", + "htmlwidgets", + "plyr", + "rlang", + "stats", + "stringr", + "utils" + ], + "Hash": "b4a5d1a3036a7e6a63439b16b3ad39fc" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xaringan": { + "Package": "xaringan", + "Version": "0.30", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "htmltools", + "knitr", + "rmarkdown", + "servr", + "xfun" + ], + "Hash": "40a1e30d3fb323f249a5e2fbec50c3b1" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/blogs/posts/2024-08-08-map-and-nest/workforce_staff_group.rds b/blogs/posts/2024-08-08_map-and-nest/workforce_staff_group.rds similarity index 100% rename from blogs/posts/2024-08-08-map-and-nest/workforce_staff_group.rds rename to blogs/posts/2024-08-08_map-and-nest/workforce_staff_group.rds diff --git a/index.qmd b/index.qmd index f5bbf09..48ea558 100644 --- a/index.qmd +++ b/index.qmd @@ -10,3 +10,11 @@ Here, we host information about how we work, links to presentations, and blogpos All members of the Strategy Unit are welcome to contribute. [su_web]: https://strategyunitwm.nhs.uk/ + +```{r} +#| include: false + +# list some packages that are needed in general for rendering +library(rmarkdown) # I'm a bit confused as to why rmarkdown is a dependency for quarto? +library(yaml) +``` \ No newline at end of file diff --git a/presentations/2023-02-01_what-is-data-science/index.qmd b/presentations/2023-02-01_what-is-data-science/index.qmd index 101dc6c..b7ee9bf 100644 --- a/presentations/2023-02-01_what-is-data-science/index.qmd +++ b/presentations/2023-02-01_what-is-data-science/index.qmd @@ -1,224 +1,230 @@ ---- -title: Everything you ever wanted to know about data science -subtitle: but were too afraid to ask -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-08-02 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## What is data science? - -* "A data scientist knows more about computer science than the average statistician, and more about statistics than the average computer scientist" - -## Drew Conway's famous Venn diagram - -Data science Venn diagram - -[Source](http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram) - -## Around the web... - -:::: {.columns} - -::: {.column width="50%"} - -* [The difference between a statitician and a data scientist? About $30,000](https://www.reddit.com/r/datascience/comments/ndpft6/comment/gyccuo7/?utm_source=share&utm_medium=web2x&context=3) -* [... an actual definition of data science. Taking a database and making it do something else.](https://chrisbeeley.net/?p=1495) (warning: this quote is me! :wink:) -* Statistics done on a Mac - -::: - -::: {.column width="50%"} - -Data science Venn diagram - -::: - -:::: - -## What are the skills of data science? - -* Analysis - * ML - * Stats - * Data viz -* Software engineering - * Programming - * SQL/ data - * DevOps - * RAP - -## What are the skills of data science? - -* Domain knowledge - * Communication - * Problem formulation - * Dashboards and reports - -## ML - -Classical programming versus machine learning diagram - -[Source](http://www.nlcssteam.com/blog/machine-learning) - -## Inevitable XKCD - -:::: {.columns} - -::: {.column width="50%"} - -XKCD about machine learning - -[Source](https://xkcd.com/1838/) - -::: - -::: {.column width="50%"} - -* [Google flu trends](https://www.wired.com/2015/10/can-learn-epic-failure-google-flu-trends/) - -::: - -:::: - - -## Stats and data viz - -* ML leans a bit more towards atheoretical prediction -* Stats leans a bit more towards inference (but they both do both) -* Data scientists may use different visualisations - * Interactive web based tools - * Dashboard based visualisers e.g. [{stminsights}](https://github.com/cschwem2er/stminsights) - -## Software engineering - -* Programming - * No/ low code data science? -* SQL/ data - * Tend to use reproducible automated processes -* DevOps - * Plan, code, build, test, release, deploy, operate, monitor -* RAP - * I will come back to this - -## Domain knowledge - -* Do stuff that matters - * The best minds of my generation are thinking about how to make people click ads. That sucks. [Jeffrey Hammerbacher](https://www.fastcompany.com/3008436/why-data-god-jeffrey-hammerbacher-left-facebook-found-cloudera) -* Convince other people that it matters -* This is the hardest part of data science -* Communicate, communicate, communicate! -* Many of you are expert at this - -## Reproducibility - -* Reproducibility in science -* The [$6B spreadsheet error](https://baselinescenario.com/2013/02/09/the-importance-of-excel/) -* [George Osbourne's austerity was based on a spreadsheet error](https://www.theguardian.com/politics/2013/apr/18/uncovered-error-george-osborne-austerity) -* For us, reproducibility also means we can do the same analysis 50 times in one minute - * Which is why I started down the road of data science - -## What is RAP - -* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset -* RAP should be: - -> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training - -:::{.footer} -[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) -::: - -## Levels of RAP- Baseline - -* Data produced by code in an open-source language (e.g., Python, R, SQL). -* Code is version controlled (see Git basics and using Git collaboratively guides). -* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code -* Code has been peer reviewed. -* Code is published in the open and linked to & from accompanying publication (if relevant). - -:::{.footer} -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) -::: - -## Levels of RAP- Silver - -* Code is well-documented... -* Code is well-organised following standard directory format -* Reusable functions and/or classes are used where appropriate -* Pipeline includes a testing framework -* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml -* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format - -:::{.footer} -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) -::: -## Levels of RAP- Gold - -* Code is fully packaged -* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions -* Process runs based on event-based triggers (e.g., new data in database) or on a schedule -* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - -## The data science "Unicorn" - -* The maybe-mythical data science "Unicorn" has mastered: - * Domain knowledge - * Stats and ML - * Software engineering - -## Data science is a team sport - -* In my extended DS team I have: -* Stats and DevOps (and rabble rousing) [this one is me :wink:] -* SQL, data, and training -* DevOps and programming -* Text mining, Python, and APIs -* Bilingual R/ Python, Shiny dashboards - -## Data science is an MMO - -* Data scientists need help with: - * Stakeholder communication and engagement - * Qualitative analysis - * Translating models and prediction into the real world - * Evidence review and problem definition - -## Data science is an MMO - -* Data scientists are an excellent help when you: - * Need a lot of pretty graphs - * Need the same analysis done 50+ times with different data - * Have too much text and not enough time to analyse it - * Want to carefully document your analysis and make it reproducible - * Have a hideously messy, large dataset that you can't hack together yourself - -## The team - -* We will be organising code review and pair coding sessions -* We will be running coffee and coding sessions -* We can be relied on to get very excited about thorny data problems, especially if they involve: - * Drawing pretty graphs - * NHS-R and other communities and events - * Spending long hours in a bunker writing open source code - * Processing text - * Documenting and version controlling analyses - -## Note - -All copyrighted material is reused under [Fair Dealing](https://www.gov.uk/guidance/exceptions-to-copyright#fair-dealing) +--- +title: Everything you ever wanted to know about data science +subtitle: but were too afraid to ask +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-08-02 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## What is data science? + +* "A data scientist knows more about computer science than the average statistician, and more about statistics than the average computer scientist" + +## Drew Conway's famous Venn diagram + +Data science Venn diagram + +[Source](http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram) + +## Around the web... + +:::: {.columns} + +::: {.column width="50%"} + +* [The difference between a statitician and a data scientist? About $30,000](https://www.reddit.com/r/datascience/comments/ndpft6/comment/gyccuo7/?utm_source=share&utm_medium=web2x&context=3) +* [... an actual definition of data science. Taking a database and making it do something else.](https://chrisbeeley.net/?p=1495) (warning: this quote is me! :wink:) +* Statistics done on a Mac + +::: + +::: {.column width="50%"} + +Data science Venn diagram + +::: + +:::: + +## What are the skills of data science? + +* Analysis + * ML + * Stats + * Data viz +* Software engineering + * Programming + * SQL/ data + * DevOps + * RAP + +## What are the skills of data science? + +* Domain knowledge + * Communication + * Problem formulation + * Dashboards and reports + +## ML + +Classical programming versus machine learning diagram + +[Source](http://www.nlcssteam.com/blog/machine-learning) + +## Inevitable XKCD + +:::: {.columns} + +::: {.column width="50%"} + +XKCD about machine learning + +[Source](https://xkcd.com/1838/) + +::: + +::: {.column width="50%"} + +* [Google flu trends](https://www.wired.com/2015/10/can-learn-epic-failure-google-flu-trends/) + +::: + +:::: + + +## Stats and data viz + +* ML leans a bit more towards atheoretical prediction +* Stats leans a bit more towards inference (but they both do both) +* Data scientists may use different visualisations + * Interactive web based tools + * Dashboard based visualisers e.g. [{stminsights}](https://github.com/cschwem2er/stminsights) + +## Software engineering + +* Programming + * No/ low code data science? +* SQL/ data + * Tend to use reproducible automated processes +* DevOps + * Plan, code, build, test, release, deploy, operate, monitor +* RAP + * I will come back to this + +## Domain knowledge + +* Do stuff that matters + * The best minds of my generation are thinking about how to make people click ads. That sucks. [Jeffrey Hammerbacher](https://www.fastcompany.com/3008436/why-data-god-jeffrey-hammerbacher-left-facebook-found-cloudera) +* Convince other people that it matters +* This is the hardest part of data science +* Communicate, communicate, communicate! +* Many of you are expert at this + +## Reproducibility + +* Reproducibility in science +* The [$6B spreadsheet error](https://baselinescenario.com/2013/02/09/the-importance-of-excel/) +* [George Osbourne's austerity was based on a spreadsheet error](https://www.theguardian.com/politics/2013/apr/18/uncovered-error-george-osborne-austerity) +* For us, reproducibility also means we can do the same analysis 50 times in one minute + * Which is why I started down the road of data science + +## What is RAP + +* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset +* RAP should be: + +> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training + +:::{.footer} +[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) +::: + +## Levels of RAP- Baseline + +* Data produced by code in an open-source language (e.g., Python, R, SQL). +* Code is version controlled (see Git basics and using Git collaboratively guides). +* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code +* Code has been peer reviewed. +* Code is published in the open and linked to & from accompanying publication (if relevant). + +:::{.footer} +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) +::: + +## Levels of RAP- Silver + +* Code is well-documented... +* Code is well-organised following standard directory format +* Reusable functions and/or classes are used where appropriate +* Pipeline includes a testing framework +* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml +* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format + +:::{.footer} +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) +::: +## Levels of RAP- Gold + +* Code is fully packaged +* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions +* Process runs based on event-based triggers (e.g., new data in database) or on a schedule +* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + +## The data science "Unicorn" + +* The maybe-mythical data science "Unicorn" has mastered: + * Domain knowledge + * Stats and ML + * Software engineering + +## Data science is a team sport + +* In my extended DS team I have: +* Stats and DevOps (and rabble rousing) [this one is me :wink:] +* SQL, data, and training +* DevOps and programming +* Text mining, Python, and APIs +* Bilingual R/ Python, Shiny dashboards + +## Data science is an MMO + +* Data scientists need help with: + * Stakeholder communication and engagement + * Qualitative analysis + * Translating models and prediction into the real world + * Evidence review and problem definition + +## Data science is an MMO + +* Data scientists are an excellent help when you: + * Need a lot of pretty graphs + * Need the same analysis done 50+ times with different data + * Have too much text and not enough time to analyse it + * Want to carefully document your analysis and make it reproducible + * Have a hideously messy, large dataset that you can't hack together yourself + +## The team + +* We will be organising code review and pair coding sessions +* We will be running coffee and coding sessions +* We can be relied on to get very excited about thorny data problems, especially if they involve: + * Drawing pretty graphs + * NHS-R and other communities and events + * Spending long hours in a bunker writing open source code + * Processing text + * Documenting and version controlling analyses + +## Note + +All copyrighted material is reused under [Fair Dealing](https://www.gov.uk/guidance/exceptions-to-copyright#fair-dealing) diff --git a/presentations/2023-02-01_what-is-data-science/renv.lock b/presentations/2023-02-01_what-is-data-science/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-02-01_what-is-data-science/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-02-23_coffee-and-coding/index.qmd b/presentations/2023-02-23_coffee-and-coding/index.qmd index 6bb7e5e..34ae877 100644 --- a/presentations/2023-02-23_coffee-and-coding/index.qmd +++ b/presentations/2023-02-23_coffee-and-coding/index.qmd @@ -1,109 +1,115 @@ ---- -title: Coffee and coding -subtitle: Intro session -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-02-23 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## Welcome to coffee and coding - -* Project demos, showcasing work from a particular project -* Method demos, showcasing how to use a particular method/tool/package -* Surgery and problem solving sessions -* Defining code standards and SOP - -## What are we trying to achieve? - -* Legibility -* Reproducibility -* Accuracy -* Laziness - -## What are some of the fundamental principles? - -* Predictability, reducing mental load, and reducing truck factor -* Making it easy to collaborate with yourself and others on different computers, in the cloud, in six months' time... -* DRY - -## What is RAP - -* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset -* RAP should be: - -> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training - -[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) - -## The road to RAP - -* We're roughly using NHS Digital's RAP stages -* There is an incredibly large amount to learn! -* Confession time! (everything I do not know...) -* You don't need to do it all at once -* You don't need to do it all at all ever -* Each thing you learn will incrementally help you -* Remember- that's why we learnt this stuff. Because it helped us. And it can help you too - -## Levels of RAP- Baseline - -* Data produced by code in an open-source language (e.g., Python, R, SQL). -* Code is version controlled (see Git basics and using Git collaboratively guides). -* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code -* Code has been peer reviewed. -* Code is published in the open and linked to & from accompanying publication (if relevant). - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - -## Levels of RAP- Silver - -* Code is well-documented... -* Code is well-organised following standard directory format -* Reusable functions and/or classes are used where appropriate -* Pipeline includes a testing framework -* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml -* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - -## Levels of RAP- Gold - -* Code is fully packaged -* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions -* Process runs based on event-based triggers (e.g., new data in database) or on a schedule -* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - - -## A learning journey to get us there - -* Code style, organising your files -* Functions and iteration -* Git and GitHub -* Packaging your code -* Testing -* Package management and versioning - -## How we can help each other get there - -* Work as a team! -* Coffee and coding! -* Ask for help! -* Do pair coding! -* Get your code reviewed! -* Join the NHS-R/ NHSPycom communities - +--- +title: Coffee and coding +subtitle: Intro session +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-02-23 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## Welcome to coffee and coding + +* Project demos, showcasing work from a particular project +* Method demos, showcasing how to use a particular method/tool/package +* Surgery and problem solving sessions +* Defining code standards and SOP + +## What are we trying to achieve? + +* Legibility +* Reproducibility +* Accuracy +* Laziness + +## What are some of the fundamental principles? + +* Predictability, reducing mental load, and reducing truck factor +* Making it easy to collaborate with yourself and others on different computers, in the cloud, in six months' time... +* DRY + +## What is RAP + +* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset +* RAP should be: + +> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training + +[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) + +## The road to RAP + +* We're roughly using NHS Digital's RAP stages +* There is an incredibly large amount to learn! +* Confession time! (everything I do not know...) +* You don't need to do it all at once +* You don't need to do it all at all ever +* Each thing you learn will incrementally help you +* Remember- that's why we learnt this stuff. Because it helped us. And it can help you too + +## Levels of RAP- Baseline + +* Data produced by code in an open-source language (e.g., Python, R, SQL). +* Code is version controlled (see Git basics and using Git collaboratively guides). +* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code +* Code has been peer reviewed. +* Code is published in the open and linked to & from accompanying publication (if relevant). + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + +## Levels of RAP- Silver + +* Code is well-documented... +* Code is well-organised following standard directory format +* Reusable functions and/or classes are used where appropriate +* Pipeline includes a testing framework +* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml +* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + +## Levels of RAP- Gold + +* Code is fully packaged +* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions +* Process runs based on event-based triggers (e.g., new data in database) or on a schedule +* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + + +## A learning journey to get us there + +* Code style, organising your files +* Functions and iteration +* Git and GitHub +* Packaging your code +* Testing +* Package management and versioning + +## How we can help each other get there + +* Work as a team! +* Coffee and coding! +* Ask for help! +* Do pair coding! +* Get your code reviewed! +* Join the NHS-R/ NHSPycom communities + diff --git a/presentations/2023-02-23_coffee-and-coding/renv.lock b/presentations/2023-02-23_coffee-and-coding/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-02-23_coffee-and-coding/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-03-09_coffee-and-coding/index.qmd b/presentations/2023-03-09_coffee-and-coding/index.qmd index f3f026c..dde0a68 100644 --- a/presentations/2023-03-09_coffee-and-coding/index.qmd +++ b/presentations/2023-03-09_coffee-and-coding/index.qmd @@ -1,236 +1,242 @@ ---- -title: "Coffee and Coding" -subtitle: "Good Coding Practices" -author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" -date: 2023-03-09 -date-format: "MMM D, YYYY" -knitr: - opts_chunk: - eval: false - echo: true -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - width: 1920 - height: 1080 ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/ - - -# You should care about your work - -> One of Guido’s key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code. As PEP 20 says, “Readability counts”. - -> A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important. - -[PEP 8 - Style Guide for Python Code][pep8] - -[pep8]: https://peps.python.org/pep-0008/ - ---- - -## Which is easier to read? - -```{r} -#| echo: false -library(tidyverse) -library(NHSRdatasets) -``` - - -```{r} -ae_attendances |> - filter(org_code %in% c("RNA", "RL4")) |> - mutate(performance = 1 + breaches / attendances) |> - filter(type == 1) |> - mutate(met_target = performance >= 0.95) -``` - -or - -```{r} -ae_attendances |> - filter( - org_code %in% c("RNA", "RL4"), - type == 1 - ) |> - mutate( - performance = 1 + breaches / attendances, - met_target = performance >= 0.95 - ) -``` - -. . . - -
-
-spending a few seconds to neatly format your code can greatly improve the legibility to future readers, making the -intent of the code far clearer, and will make finding bugs easier to spot. - -. . . - -
-
-(have you spotted the mistake in the snippets above?) - ---- - -## Tidyverse Style Guide - -> Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread - -> All style guides are fundamentally opinionated. Some decisions genuinely do make code easier to use (especially matching indenting to programming structure), but many decisions are arbitrary. The most important thing about a style guide is that it provides consistency, making code easier to write because you need to make fewer decisions. - -[tidyverse style guide][tidyverse_style] - -[tidyverse_style]: https://style.tidyverse.org/ - ---- - -### syntax key points - -::::{.columns} - -:::{.column width="50%"} - -* object (+column) names should be `snake_case`. Generally variables should be nouns, functions should be verbs. -* avoid re-using names of common functions and variables -* break lines at 80 characters. If your line is too long, that's a good sign you should be encapsulating some of the -logic into a separate function -* Use `"` instead of `'` for quoting text -* pipes should be on separate lines - -::: - -:::{.column width="50%"} -```{r} -# Good -day_one -day_1 - -# Bad -DayOne -dayone -``` - -```{r} -# Good -day_one - -# Bad -first_day_of_the_month -djm1 - -# Bad -T <- FALSE -c <- 10 -mean <- function(x) sum(x) -``` - -```{r} -# Good -iris %>% - group_by(Species) %>% - summarize_if(is.numeric, mean) %>% - ungroup() %>% - gather(measure, value, -Species) %>% - arrange(value) - -# Bad -iris %>% - group_by(Species) %>% - summarize_all(mean) %>% - ungroup() %>% - gather(measure, value, -Species) %>% - arrange(value) -``` - -::: - -:::: - -## `{lintr}` + `{styler}` are your new best friends - -::::{.columns} - -:::{.column width="50%"} - -#### `{lintr}` - -* `{lintr}` is a static code analysis tool that inspects your code (without running it) -* it checks for certain classes of errors (e.g. mismatched `{` and `(`'s) -* it warns about potential issues (e.g. using variables that aren't defined) -* it warns about places where you are not adhering to the code style - -::: - -:::{.column width="50%"} - -#### `{styler}` - -* `{styler}` is an RStudio add in that automatically reformats your code, tidying it up to match the style guide -* 99.9% of the time it will give you equivalent code, but there is the potential that it may change the behaviour of - your code -* it will overwrite the files that you ask it to run on however, so it is vital to be using version control -* a good workflow here is to save your file, "stage" the changes to your file, then run `{styler}`. You can then revert - back to the staged changed if needed. - -::: - -:::: - -## What does `{lintr}` look like? - -::::{.columns} - -:::{.column width="70%"} - -![](lintr_output.png) - -source: [Good practice for writing R code and R packages][gcp] - - -::: - -:::{.column width="30%"} - -running lintr can be done in the console, e.g. - -```{r} -lintr::lintr_dir(".") -``` - -or via the Addins menu - -![](lintr_addin.png) - -::: - -:::: - -## Using `{styler}` - -![](styler_0.1.gif) - -source: [Good practice for writing R code and R packages][gcp] - -[gcp]: https://magrichard.github.io/good_coding_practices/rpresentation.html#12 - -## Further thoughts on improving code legibility - -- do not let files grow too big -- break up logic into separate files, then you can use `source("filename.R)` to run the code in that file -- idealy, break up your logic into separate functions, each function having it's own file, and then call those - functions within your analysis -- do not repeat yourself - if you are copying and pasting your code then you should be thinking about how to write a - single function to handle this repeated logic - +--- +title: "Coffee and Coding" +subtitle: "Good Coding Practices" +author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" +date: 2023-03-09 +date-format: "MMM D, YYYY" +knitr: + opts_chunk: + eval: false + echo: true +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + width: 1920 + height: 1080 +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/ + + +# You should care about your work + +> One of Guido’s key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code. As PEP 20 says, “Readability counts”. + +> A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important. + +[PEP 8 - Style Guide for Python Code][pep8] + +[pep8]: https://peps.python.org/pep-0008/ + +--- + +## Which is easier to read? + +```{r} +#| echo: false +library(tidyverse) +library(NHSRdatasets) +``` + + +```{r} +ae_attendances |> + filter(org_code %in% c("RNA", "RL4")) |> + mutate(performance = 1 + breaches / attendances) |> + filter(type == 1) |> + mutate(met_target = performance >= 0.95) +``` + +or + +```{r} +ae_attendances |> + filter( + org_code %in% c("RNA", "RL4"), + type == 1 + ) |> + mutate( + performance = 1 + breaches / attendances, + met_target = performance >= 0.95 + ) +``` + +. . . + +
+
+spending a few seconds to neatly format your code can greatly improve the legibility to future readers, making the +intent of the code far clearer, and will make finding bugs easier to spot. + +. . . + +
+
+(have you spotted the mistake in the snippets above?) + +--- + +## Tidyverse Style Guide + +> Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread + +> All style guides are fundamentally opinionated. Some decisions genuinely do make code easier to use (especially matching indenting to programming structure), but many decisions are arbitrary. The most important thing about a style guide is that it provides consistency, making code easier to write because you need to make fewer decisions. + +[tidyverse style guide][tidyverse_style] + +[tidyverse_style]: https://style.tidyverse.org/ + +--- + +### syntax key points + +::::{.columns} + +:::{.column width="50%"} + +* object (+column) names should be `snake_case`. Generally variables should be nouns, functions should be verbs. +* avoid re-using names of common functions and variables +* break lines at 80 characters. If your line is too long, that's a good sign you should be encapsulating some of the +logic into a separate function +* Use `"` instead of `'` for quoting text +* pipes should be on separate lines + +::: + +:::{.column width="50%"} +```{r} +# Good +day_one +day_1 + +# Bad +DayOne +dayone +``` + +```{r} +# Good +day_one + +# Bad +first_day_of_the_month +djm1 + +# Bad +T <- FALSE +c <- 10 +mean <- function(x) sum(x) +``` + +```{r} +# Good +iris %>% + group_by(Species) %>% + summarize_if(is.numeric, mean) %>% + ungroup() %>% + gather(measure, value, -Species) %>% + arrange(value) + +# Bad +iris %>% + group_by(Species) %>% + summarize_all(mean) %>% + ungroup() %>% + gather(measure, value, -Species) %>% + arrange(value) +``` + +::: + +:::: + +## `{lintr}` + `{styler}` are your new best friends + +::::{.columns} + +:::{.column width="50%"} + +#### `{lintr}` + +* `{lintr}` is a static code analysis tool that inspects your code (without running it) +* it checks for certain classes of errors (e.g. mismatched `{` and `(`'s) +* it warns about potential issues (e.g. using variables that aren't defined) +* it warns about places where you are not adhering to the code style + +::: + +:::{.column width="50%"} + +#### `{styler}` + +* `{styler}` is an RStudio add in that automatically reformats your code, tidying it up to match the style guide +* 99.9% of the time it will give you equivalent code, but there is the potential that it may change the behaviour of + your code +* it will overwrite the files that you ask it to run on however, so it is vital to be using version control +* a good workflow here is to save your file, "stage" the changes to your file, then run `{styler}`. You can then revert + back to the staged changed if needed. + +::: + +:::: + +## What does `{lintr}` look like? + +::::{.columns} + +:::{.column width="70%"} + +![](lintr_output.png) + +source: [Good practice for writing R code and R packages][gcp] + + +::: + +:::{.column width="30%"} + +running lintr can be done in the console, e.g. + +```{r} +lintr::lintr_dir(".") +``` + +or via the Addins menu + +![](lintr_addin.png) + +::: + +:::: + +## Using `{styler}` + +![](styler_0.1.gif) + +source: [Good practice for writing R code and R packages][gcp] + +[gcp]: https://magrichard.github.io/good_coding_practices/rpresentation.html#12 + +## Further thoughts on improving code legibility + +- do not let files grow too big +- break up logic into separate files, then you can use `source("filename.R)` to run the code in that file +- idealy, break up your logic into separate functions, each function having it's own file, and then call those + functions within your analysis +- do not repeat yourself - if you are copying and pasting your code then you should be thinking about how to write a + single function to handle this repeated logic + diff --git a/presentations/2023-03-09_coffee-and-coding/renv.lock b/presentations/2023-03-09_coffee-and-coding/renv.lock new file mode 100644 index 0000000..b54d999 --- /dev/null +++ b/presentations/2023-03-09_coffee-and-coding/renv.lock @@ -0,0 +1,1618 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "DBI": { + "Package": "DBI", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "065ae649b05f1ff66bb0c793107508f5" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "NHSRdatasets": { + "Package": "NHSRdatasets", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "tibble" + ], + "Hash": "02c9417179975a83e37329f40c451591" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" + }, + "bit64": { + "Package": "bit64", + "Version": "4.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "e84984bf5f12a18628d9a02322128dfd" + }, + "blob": { + "Package": "blob", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "rlang", + "vctrs" + ], + "Hash": "40415719b5a479b87949f3aa0aee737c" + }, + "broom": { + "Package": "broom", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "8fcc818f3b9887aebaf206f141437cc9" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "codetools": { + "Package": "codetools", + "Version": "0.2-20", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "61e097f35917d342622f21cdc79c256e" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "conflicted": { + "Package": "conflicted", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "memoise", + "rlang" + ], + "Hash": "bb097fccb22d156624fd07cd2894ddb6" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "curl": { + "Package": "curl", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ff51697d9205fe715f29e7171e874c6e" + }, + "cyclocomp": { + "Package": "cyclocomp", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "callr", + "crayon", + "desc", + "remotes", + "withr" + ], + "Hash": "cdc4a473222b0112d4df0bcfbed12d44" + }, + "data.table": { + "Package": "data.table", + "Version": "1.16.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "2e00b378fc3be69c865120d9f313039a" + }, + "dbplyr": { + "Package": "dbplyr", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "DBI", + "R", + "R6", + "blob", + "cli", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "utils", + "vctrs", + "withr" + ], + "Hash": "39b2e002522bfd258039ee4e889e0fd1" + }, + "desc": { + "Package": "desc", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "utils" + ], + "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "dtplyr": { + "Package": "dtplyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "data.table", + "dplyr", + "glue", + "lifecycle", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "54ed3ea01b11e81a86544faaecfef8e2" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "forcats": { + "Package": "forcats", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "tibble" + ], + "Hash": "1a0a9a3d5083d0d573c4214576f1e690" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "gargle": { + "Package": "gargle", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "fs", + "glue", + "httr", + "jsonlite", + "lifecycle", + "openssl", + "rappdirs", + "rlang", + "stats", + "utils", + "withr" + ], + "Hash": "fc0b272e5847c58cd5da9b20eedbd026" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "googledrive": { + "Package": "googledrive", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gargle", + "glue", + "httr", + "jsonlite", + "lifecycle", + "magrittr", + "pillar", + "purrr", + "rlang", + "tibble", + "utils", + "uuid", + "vctrs", + "withr" + ], + "Hash": "e99641edef03e2a5e87f0a0b1fcc97f4" + }, + "googlesheets4": { + "Package": "googlesheets4", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cli", + "curl", + "gargle", + "glue", + "googledrive", + "httr", + "ids", + "lifecycle", + "magrittr", + "methods", + "purrr", + "rematch2", + "rlang", + "tibble", + "utils", + "vctrs", + "withr" + ], + "Hash": "d6db1667059d027da730decdc214b959" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "haven": { + "Package": "haven", + "Version": "2.5.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "forcats", + "hms", + "lifecycle", + "methods", + "readr", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "9171f898db9d9c4c1b2c745adc2c1ef1" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "ids": { + "Package": "ids", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "openssl", + "uuid" + ], + "Hash": "99df65cfef20e525ed38c3d2577f7190" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lintr": { + "Package": "lintr", + "Version": "3.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "codetools", + "cyclocomp", + "digest", + "glue", + "knitr", + "rex", + "stats", + "utils", + "xml2", + "xmlparsedata" + ], + "Hash": "08cff46381a242d44c0d8dd0aabd9f71" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "modelr": { + "Package": "modelr", + "Version": "0.1.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "magrittr", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "vctrs" + ], + "Hash": "4f50122dc256b1b6996a4703fecea821" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "openssl": { + "Package": "openssl", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "d413e0fef796c9401a4419485f709ca1" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "ragg": { + "Package": "ragg", + "Version": "1.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "0595fe5e47357111f29ad19101c7d271" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "remotes": { + "Package": "remotes", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "3ee025083e66f18db6cf27b56e23e141" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "reprex": { + "Package": "reprex", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "callr", + "cli", + "clipr", + "fs", + "glue", + "knitr", + "lifecycle", + "rlang", + "rmarkdown", + "rstudioapi", + "utils", + "withr" + ], + "Hash": "97b1d5361a24d9fb588db7afe3e5bcbf" + }, + "rex": { + "Package": "rex", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lazyeval" + ], + "Hash": "ae34cd56890607370665bee5bd17812f" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.17.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5f90cd73946d706cfe26024294236113" + }, + "rvest": { + "Package": "rvest", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "httr", + "lifecycle", + "magrittr", + "rlang", + "selectr", + "tibble", + "xml2" + ], + "Hash": "0bcf0c6f274e90ea314b812a6d19a519" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "selectr": { + "Package": "selectr", + "Version": "0.4-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "methods", + "stringr" + ], + "Hash": "3838071b66e0c566d55cc26bd6e27bf4" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "sys": { + "Package": "sys", + "Version": "3.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "de342ebfebdbf40477d0758d05426646" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle" + ], + "Hash": "213b6b8ed5afbf934843e6c3b090d418" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle", + "systemfonts" + ], + "Hash": "5142f8bc78ed3d819d26461b641627ce" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tidyverse": { + "Package": "tidyverse", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "cli", + "conflicted", + "dbplyr", + "dplyr", + "dtplyr", + "forcats", + "ggplot2", + "googledrive", + "googlesheets4", + "haven", + "hms", + "httr", + "jsonlite", + "lubridate", + "magrittr", + "modelr", + "pillar", + "purrr", + "ragg", + "readr", + "readxl", + "reprex", + "rlang", + "rstudioapi", + "rvest", + "stringr", + "tibble", + "tidyr", + "xml2" + ], + "Hash": "c328568cd14ea89a83bd4ca7f54ae07e" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "uuid": { + "Package": "uuid", + "Version": "1.2-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "34e965e62a41fcafb1ca60e9b142085b" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "xmlparsedata": { + "Package": "xmlparsedata", + "Version": "1.0.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45e4bf3c46476896e821fc0a408fb4fc" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-03-09_midlands-analyst-rap/index.qmd b/presentations/2023-03-09_midlands-analyst-rap/index.qmd index e460b4c..e126338 100644 --- a/presentations/2023-03-09_midlands-analyst-rap/index.qmd +++ b/presentations/2023-03-09_midlands-analyst-rap/index.qmd @@ -1,110 +1,116 @@ ---- -title: RAP -subtitle: what is it and how can my team start using it effectively? -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-03-09 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## What is RAP - -* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset -* RAP should be: - -> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training - -[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) - -## What are we trying to achieve? - -* Legibility -* Reproducibility -* Accuracy -* Laziness - -## What are some of the fundamental principles? - -* Predictability, reducing mental load, and reducing truck factor -* Making it easy to collaborate with yourself and others on different computers, in the cloud, in six months' time... -* DRY - -## The road to RAP - -* We're roughly using NHS Digital's RAP stages -* There is an incredibly large amount to learn! -* Confession time! (everything I do not know...) -* You don't need to do it all at once -* You don't need to do it all at all ever -* Each thing you learn will incrementally help you -* Remember- that's why we learnt this stuff. Because it helped us. And it can help you too - -## Levels of RAP- Baseline - -* Data produced by code in an open-source language (e.g., Python, R, SQL). -* Code is version controlled (see Git basics and using Git collaboratively guides). -* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code -* Code has been peer reviewed. -* Code is published in the open and linked to & from accompanying publication (if relevant). - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - -## Levels of RAP- Silver - -* Code is well-documented... -* Code is well-organised following standard directory format -* Reusable functions and/or classes are used where appropriate -* Pipeline includes a testing framework -* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml -* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - -## Levels of RAP- Gold - -* Code is fully packaged -* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions -* Process runs based on event-based triggers (e.g., new data in database) or on a schedule -* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) - -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) - - -## A learning journey to get you there - -* Code style, organising your files -* Functions and iteration -* Git and GitHub -* Packaging your code -* Testing -* Package management and versioning - -## How we can help each other get there - -* Work as a team! -* Coffee and coding! -* Ask for help! -* Do pair coding! -* Get your code reviewed! -* Join the NHS-R/ NHSPycom communities - -## HACA - -* The first national analytics conference for health and care -* Insight to action! -* July 11th and 12th, University of Birmingham -* Accepting abstracts for short and long talks and posters -* Abstract deadline 27th March -* Help is available (with abstract, poster, preparing presentation...)! +--- +title: RAP +subtitle: what is it and how can my team start using it effectively? +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-03-09 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## What is RAP + +* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset +* RAP should be: + +> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training + +[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) + +## What are we trying to achieve? + +* Legibility +* Reproducibility +* Accuracy +* Laziness + +## What are some of the fundamental principles? + +* Predictability, reducing mental load, and reducing truck factor +* Making it easy to collaborate with yourself and others on different computers, in the cloud, in six months' time... +* DRY + +## The road to RAP + +* We're roughly using NHS Digital's RAP stages +* There is an incredibly large amount to learn! +* Confession time! (everything I do not know...) +* You don't need to do it all at once +* You don't need to do it all at all ever +* Each thing you learn will incrementally help you +* Remember- that's why we learnt this stuff. Because it helped us. And it can help you too + +## Levels of RAP- Baseline + +* Data produced by code in an open-source language (e.g., Python, R, SQL). +* Code is version controlled (see Git basics and using Git collaboratively guides). +* Repository includes a README.md file (or equivalent) that clearly details steps a user must follow to reproduce the code +* Code has been peer reviewed. +* Code is published in the open and linked to & from accompanying publication (if relevant). + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + +## Levels of RAP- Silver + +* Code is well-documented... +* Code is well-organised following standard directory format +* Reusable functions and/or classes are used where appropriate +* Pipeline includes a testing framework +* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml +* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + +## Levels of RAP- Gold + +* Code is fully packaged +* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions +* Process runs based on event-based triggers (e.g., new data in database) or on a schedule +* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) + +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) + + +## A learning journey to get you there + +* Code style, organising your files +* Functions and iteration +* Git and GitHub +* Packaging your code +* Testing +* Package management and versioning + +## How we can help each other get there + +* Work as a team! +* Coffee and coding! +* Ask for help! +* Do pair coding! +* Get your code reviewed! +* Join the NHS-R/ NHSPycom communities + +## HACA + +* The first national analytics conference for health and care +* Insight to action! +* July 11th and 12th, University of Birmingham +* Accepting abstracts for short and long talks and posters +* Abstract deadline 27th March +* Help is available (with abstract, poster, preparing presentation...)! diff --git a/presentations/2023-03-09_midlands-analyst-rap/renv.lock b/presentations/2023-03-09_midlands-analyst-rap/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-03-09_midlands-analyst-rap/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-03-23_coffee-and-coding/index.qmd b/presentations/2023-03-23_coffee-and-coding/index.qmd index f3e915c..5d23924 100644 --- a/presentations/2023-03-23_coffee-and-coding/index.qmd +++ b/presentations/2023-03-23_coffee-and-coding/index.qmd @@ -1,222 +1,228 @@ ---- -title: "Coffee and Coding" -subtitle: "{targets}" -author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" -date: 2023-03-23 -date-format: "MMM D, YYYY" -knitr: - opts_chunk: - eval: false - echo: true -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - width: 1920 - height: 1080 -editor: - markdown: - wrap: 120 ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## What is `{targets}`? - -> The targets package is a Make-like pipeline tool for Statistics and data science in R. With targets, you can maintain -> a reproducible workflow without repeating yourself. targets learns how your pipeline fits together, skips costly -> runtime for tasks that are already up to date, runs only the necessary computation, supports implicit parallel -> computing, abstracts files as R objects, and shows tangible evidence that the results match the underlying code and -> data. - -. . . - -> Data analysis can be slow. A round of scientific computation can take several minutes, hours, or even days to -> complete. After it finishes, if you update your code or data, your hard-earned results may no longer be valid. -> Unchecked, this invalidation creates chronic Sisyphean loop: - -> 1. Launch the code. -> 2. Wait while it runs. -> 3. Discover an issue. -> 4. Restart from scratch. - -::: footer -source: [The `{targets}` R package user manual](https://books.ropensci.org/targets/) -::: - -## What is it actually trying to do? - -::: incremental -- Your analysis is built up of a number of steps that build one on top of another -- but these steps need to run in a particular order -- some of these steps may take a long time to run -- so you only want to run the steps that have changed -::: - -## Typical solution - -::: columns -::: {.column width="50%"} -### Steps - -You have a folder with numbered scripts, such as: - -- `1. get data.R` -- `2. process data.R` -- `3. produce charts.R` -- `4. build model.R` -- `5. report.qmd` -::: - -::: {.column width="50%"} -### Downsides - -::: incremental -- it's easy to accidentally skip a step: what happens if you went from 1 to 3? -- performing one of the steps may take a long time, so you may want to skip it if it's already been run... but how do - you know that it's already been run? -- perhaps step 4 doesn't depend on step 3, but is this obvious that you could skip step 4 if step 3 is updated? -- what if someone labels the files terribly, or doesn't number them at all? -- what if the numbers become out of date and are in the wrong order? -- do you need to create a procedure document that describes what to do, step-by-step? -::: -::: -::: - -## {targets} to the rescue? - -::: columns -::: {.column width="50%"} -Using the previous example, if we were to create functions for each of the steps (all saved in the folder `R/`), we can -start using targets using the function `use_targets()` which will create a file called `_targets.R`. - -We can then modify the file to match our pipeline, for example: - -Note that: - -- `processed_data` depends upon `raw_data`, -- `chart` and `model` depend upon `processed_data`, -- `report` depends upon `chart` and `model`. - -We can visualise our pipeline using `tar_visnetwork()`. -::: - -::: {.column width="50%"} -```{r} -library(targets) - -tar_option_set( - packages = c("tibble", "dplyr", "ggplot2"), -) - -tar_source() - -list( - tar_target(raw_data, get_data()), - tar_target(processed_data, process_data(raw_data)), - tar_target(chart, produce_chart(processed_data)), - tar_target(model, build_model(processed_data)), - tar_target(report, generate_report(chart, model)) -) - -``` - -![](visnetwork.png) -::: -::: - -## Running the pipeline - -Running this pipeline is as simple as: `tar_make()`. - -This will output the following: - -``` -• start target raw_data -• built target raw_data [1.05 seconds] -• start target processed_data -• built target processed_data [0.03 seconds] -• start target chart -• built target chart [0.02 seconds] -• start target model -• built target model [0.01 seconds] -• start target report -• built target report [0 seconds] -• end pipeline [1.75 seconds] -``` - -. . . - -Running `tar_make()` again will show these step's being skipped: - -``` -✔ skip target raw_data -✔ skip target processed_data -✔ skip target chart -✔ skip target model -✔ skip target report -✔ skip pipeline [0.12 seconds] -``` - -## Changing one of the files - -If we change `produce_chart.R` slightly, this will cause `chart` and `report` to be invalidated, but it will skip over -the other steps. - -:::: columns - -::: {.column width="50%"} - -``` -> targets::tar_make() - -✔ skip target raw_data -✔ skip target processed_data -• start target chart -• built target chart [0.03 seconds] -✔ skip target model -• start target report -• built target report [0 seconds] -• end pipeline [1.71 seconds] -``` - -::: - -::: {.column width="50%"} -![](modified_visnetwork.png) -::: - -:::: - -## Using the results of our pipeline - -We can view the results of any step using `tar_read()` and `tar_load()`. These will either directly give you the results -of a step, or load that step into your environment (as a variable with the same name as the step). - -This allows us to view intermediate steps as well as the final outputs of our pipelines. - -One thing you may want to consider doing is as a final step in a pipeline is to generate a quarto document, or save -call a function like `saveRDS` to generate more useful outputs. - -## Current examples of {targets} in action - -- [code used in this presentation](https://github.com/tomjemmett/targets_tutorial) -- [NHP Inputs](https://github.com/The-Strategy-Unit/nhp_inputs) (all of the data processing steps are a targets -pipeline) -- [NHP Strategies](https://github.com/The-Strategy-Unit/nhp_outputs) (runs Sql scripts to update tables in the data -warehouse) -- [NHP Model](https://github.com/The-Strategy-Unit/nhp_outputs) (all of the data extraction, processing, and uploading -for the model is a targets pipeline) -- [Macmillan on NCDR](https://github.com/The-Strategy-Unit/MacmillanOnNCDR) - Jacqueline has been using {targets} for -her current project - -The [{targets} documentation](https://books.ropensci.org/targets/walkthrough.html) is exceptionally detailed and easy to -follow, and goes into more complex examples (such as dynamic branching of steps in a pipeline and high performance -computing setups) +--- +title: "Coffee and Coding" +subtitle: "{targets}" +author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" +date: 2023-03-23 +date-format: "MMM D, YYYY" +knitr: + opts_chunk: + eval: false + echo: true +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + width: 1920 + height: 1080 +editor: + markdown: + wrap: 120 +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## What is `{targets}`? + +> The targets package is a Make-like pipeline tool for Statistics and data science in R. With targets, you can maintain +> a reproducible workflow without repeating yourself. targets learns how your pipeline fits together, skips costly +> runtime for tasks that are already up to date, runs only the necessary computation, supports implicit parallel +> computing, abstracts files as R objects, and shows tangible evidence that the results match the underlying code and +> data. + +. . . + +> Data analysis can be slow. A round of scientific computation can take several minutes, hours, or even days to +> complete. After it finishes, if you update your code or data, your hard-earned results may no longer be valid. +> Unchecked, this invalidation creates chronic Sisyphean loop: + +> 1. Launch the code. +> 2. Wait while it runs. +> 3. Discover an issue. +> 4. Restart from scratch. + +::: footer +source: [The `{targets}` R package user manual](https://books.ropensci.org/targets/) +::: + +## What is it actually trying to do? + +::: incremental +- Your analysis is built up of a number of steps that build one on top of another +- but these steps need to run in a particular order +- some of these steps may take a long time to run +- so you only want to run the steps that have changed +::: + +## Typical solution + +::: columns +::: {.column width="50%"} +### Steps + +You have a folder with numbered scripts, such as: + +- `1. get data.R` +- `2. process data.R` +- `3. produce charts.R` +- `4. build model.R` +- `5. report.qmd` +::: + +::: {.column width="50%"} +### Downsides + +::: incremental +- it's easy to accidentally skip a step: what happens if you went from 1 to 3? +- performing one of the steps may take a long time, so you may want to skip it if it's already been run... but how do + you know that it's already been run? +- perhaps step 4 doesn't depend on step 3, but is this obvious that you could skip step 4 if step 3 is updated? +- what if someone labels the files terribly, or doesn't number them at all? +- what if the numbers become out of date and are in the wrong order? +- do you need to create a procedure document that describes what to do, step-by-step? +::: +::: +::: + +## {targets} to the rescue? + +::: columns +::: {.column width="50%"} +Using the previous example, if we were to create functions for each of the steps (all saved in the folder `R/`), we can +start using targets using the function `use_targets()` which will create a file called `_targets.R`. + +We can then modify the file to match our pipeline, for example: + +Note that: + +- `processed_data` depends upon `raw_data`, +- `chart` and `model` depend upon `processed_data`, +- `report` depends upon `chart` and `model`. + +We can visualise our pipeline using `tar_visnetwork()`. +::: + +::: {.column width="50%"} +```{r} +library(targets) + +tar_option_set( + packages = c("tibble", "dplyr", "ggplot2"), +) + +tar_source() + +list( + tar_target(raw_data, get_data()), + tar_target(processed_data, process_data(raw_data)), + tar_target(chart, produce_chart(processed_data)), + tar_target(model, build_model(processed_data)), + tar_target(report, generate_report(chart, model)) +) + +``` + +![](visnetwork.png) +::: +::: + +## Running the pipeline + +Running this pipeline is as simple as: `tar_make()`. + +This will output the following: + +``` +• start target raw_data +• built target raw_data [1.05 seconds] +• start target processed_data +• built target processed_data [0.03 seconds] +• start target chart +• built target chart [0.02 seconds] +• start target model +• built target model [0.01 seconds] +• start target report +• built target report [0 seconds] +• end pipeline [1.75 seconds] +``` + +. . . + +Running `tar_make()` again will show these step's being skipped: + +``` +✔ skip target raw_data +✔ skip target processed_data +✔ skip target chart +✔ skip target model +✔ skip target report +✔ skip pipeline [0.12 seconds] +``` + +## Changing one of the files + +If we change `produce_chart.R` slightly, this will cause `chart` and `report` to be invalidated, but it will skip over +the other steps. + +:::: columns + +::: {.column width="50%"} + +``` +> targets::tar_make() + +✔ skip target raw_data +✔ skip target processed_data +• start target chart +• built target chart [0.03 seconds] +✔ skip target model +• start target report +• built target report [0 seconds] +• end pipeline [1.71 seconds] +``` + +::: + +::: {.column width="50%"} +![](modified_visnetwork.png) +::: + +:::: + +## Using the results of our pipeline + +We can view the results of any step using `tar_read()` and `tar_load()`. These will either directly give you the results +of a step, or load that step into your environment (as a variable with the same name as the step). + +This allows us to view intermediate steps as well as the final outputs of our pipelines. + +One thing you may want to consider doing is as a final step in a pipeline is to generate a quarto document, or save +call a function like `saveRDS` to generate more useful outputs. + +## Current examples of {targets} in action + +- [code used in this presentation](https://github.com/tomjemmett/targets_tutorial) +- [NHP Inputs](https://github.com/The-Strategy-Unit/nhp_inputs) (all of the data processing steps are a targets +pipeline) +- [NHP Strategies](https://github.com/The-Strategy-Unit/nhp_outputs) (runs Sql scripts to update tables in the data +warehouse) +- [NHP Model](https://github.com/The-Strategy-Unit/nhp_outputs) (all of the data extraction, processing, and uploading +for the model is a targets pipeline) +- [Macmillan on NCDR](https://github.com/The-Strategy-Unit/MacmillanOnNCDR) - Jacqueline has been using {targets} for +her current project + +The [{targets} documentation](https://books.ropensci.org/targets/walkthrough.html) is exceptionally detailed and easy to +follow, and goes into more complex examples (such as dynamic branching of steps in a pipeline and high performance +computing setups) diff --git a/presentations/2023-03-23_coffee-and-coding/renv.lock b/presentations/2023-03-23_coffee-and-coding/renv.lock new file mode 100644 index 0000000..22decef --- /dev/null +++ b/presentations/2023-03-23_coffee-and-coding/renv.lock @@ -0,0 +1,850 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "base64url": { + "Package": "base64url", + "Version": "1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "backports" + ], + "Hash": "0c54cf3a08cc0e550fbd64ad33166143" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "codetools": { + "Package": "codetools", + "Version": "0.2-20", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "61e097f35917d342622f21cdc79c256e" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "data.table": { + "Package": "data.table", + "Version": "1.16.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "2e00b378fc3be69c865120d9f313039a" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "igraph": { + "Package": "igraph", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "cli", + "cpp11", + "grDevices", + "graphics", + "lifecycle", + "magrittr", + "methods", + "pkgconfig", + "rlang", + "stats", + "utils", + "vctrs" + ], + "Hash": "c03878b48737a0e2da3b772d7b2e22da" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "secretbase": { + "Package": "secretbase", + "Version": "1.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "eaf84737a6da68c1e843979963c09a6b" + }, + "targets": { + "Package": "targets", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "base64url", + "callr", + "cli", + "codetools", + "data.table", + "igraph", + "knitr", + "ps", + "rlang", + "secretbase", + "stats", + "tibble", + "tidyselect", + "tools", + "utils", + "vctrs", + "yaml" + ], + "Hash": "021ef38b55dac09df3c2c75befe54e8f" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-03-23_collaborative-working/index.qmd b/presentations/2023-03-23_collaborative-working/index.qmd index 7d42482..8306cb8 100644 --- a/presentations/2023-03-23_collaborative-working/index.qmd +++ b/presentations/2023-03-23_collaborative-working/index.qmd @@ -1,87 +1,93 @@ ---- -title: "Collaborative working" -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-03-23 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## Introduction - -* This is definitely an art and not a science -* I do not claim to have all, or even most of, the answers -* How you use these tools is way more important than the tools themselves -* This is a culture and not a technique - -## Costs - -* Delay and time -* Stress and disagreement -* Committee thinking -* Learning and effort - -## Benefits - -* "From each according to their ability" -* Learning -* Reproducibility and reduced truck factor -* Fun! - -## GitHub as an organising principle behind work - -* A project is just a set of milestones -* A milestone is just a set of issues -* An issue is just a set of commits -* A commit is just text added and removed - -## The repo owner - -* Review milestones -* Review issues - * Discuss the issue on the issue- NOT on email! -* Review pull requests and get your pull requests reviewed! - -## Asynchronous communication - -* Involve others *before* you pull request -* Involve others *when* you pull request -* Read issues! -* Comment on issues! -* File issues- suggestions/ bug reports/ questions - * NOT in emails - -## Asynchronous work - -* Every piece of work has an issues associated with it -* Every piece of work associated with an issue lives on its own branch -* Every branch is incorporated to the main repo by a pull request -* Every pull request is reviewed - -## Iteration and documentation - -* Analyse early, analyse often (using RAPs!) -* Write down what you did -* Write down what you did but then changed your mind about -* Favour Quarto/ RMarkdown - * Clean sessions - * Documentation and graphics - -## Data and .gitignore - -* Your repo needs to be reproducible but also needs to be safe -* The main branch should be reproducible by anyone at any time - * Document package dependencies (using renv) - * Document data loads if the data isn't in the repo - +--- +title: "Collaborative working" +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-03-23 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## Introduction + +* This is definitely an art and not a science +* I do not claim to have all, or even most of, the answers +* How you use these tools is way more important than the tools themselves +* This is a culture and not a technique + +## Costs + +* Delay and time +* Stress and disagreement +* Committee thinking +* Learning and effort + +## Benefits + +* "From each according to their ability" +* Learning +* Reproducibility and reduced truck factor +* Fun! + +## GitHub as an organising principle behind work + +* A project is just a set of milestones +* A milestone is just a set of issues +* An issue is just a set of commits +* A commit is just text added and removed + +## The repo owner + +* Review milestones +* Review issues + * Discuss the issue on the issue- NOT on email! +* Review pull requests and get your pull requests reviewed! + +## Asynchronous communication + +* Involve others *before* you pull request +* Involve others *when* you pull request +* Read issues! +* Comment on issues! +* File issues- suggestions/ bug reports/ questions + * NOT in emails + +## Asynchronous work + +* Every piece of work has an issues associated with it +* Every piece of work associated with an issue lives on its own branch +* Every branch is incorporated to the main repo by a pull request +* Every pull request is reviewed + +## Iteration and documentation + +* Analyse early, analyse often (using RAPs!) +* Write down what you did +* Write down what you did but then changed your mind about +* Favour Quarto/ RMarkdown + * Clean sessions + * Documentation and graphics + +## Data and .gitignore + +* Your repo needs to be reproducible but also needs to be safe +* The main branch should be reproducible by anyone at any time + * Document package dependencies (using renv) + * Document data loads if the data isn't in the repo + diff --git a/presentations/2023-03-23_collaborative-working/renv.lock b/presentations/2023-03-23_collaborative-working/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-03-23_collaborative-working/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-05-15_text-mining/index.qmd b/presentations/2023-05-15_text-mining/index.qmd index d10da8f..466a4f9 100644 --- a/presentations/2023-05-15_text-mining/index.qmd +++ b/presentations/2023-05-15_text-mining/index.qmd @@ -1,116 +1,122 @@ ---- -title: "Text mining of patient experience data" -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-05-15 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## Patient experience - -* The NHS collects a lot of patient experience data -* Rate the service 1-5 (Very poor... Excellent) but also give written feedback - * "Parking was difficult" - * "Doctor was rude" - * "You saved my life" -* Many organisations lack the staffing to read all of the feedback in a systematic way - -## Text mining - -* We have built an algorithm to read it - * Theme - * "Criticality" -* Fits alongside other work happening within NHSE - * A framework for understanding patient experience - -## Patient experience 101 - -* Tick box scoring is not useful (or accurate) -* Text based data is _complex_ and built on _human experience_ -* We're not making word clouds! -* We're not classifying movie reviews or Reddit posts -* The tool should enhance, not replace, human understanding -* "A recommendation engine for feedback data" - -## Everything open, all the time - -* This project was [coded in the open](https://transform.england.nhs.uk/key-tools-and-info/digital-playbooks/open-source-digital-playbook/cdu-data-science-team/) and is MIT licensed -* Engage with the organisations as we find them - * Do they want code or a docker image? - * Do they want to fetch their own themes from an API? - * Do they want to use our dashboard? - -## Phase 1 - -* 10 categories and moderate performance on criticality analysis -* scikit-learn -* Shiny -* Reticulate -* R package of Python code - -## Golem all the things! - -* Opinionated way of building Shiny -* Allows flexibility in deployed versions using YAML -* Agnostic to deployment -* Emphasises dependency management and testing -* Separate "reactive" and "business" logic (see the [accompanying book](https://engineering-shiny.org/)) - -## Phase 2 - -* 30-50 categories and excellent criticality performance -* scikit-learn/ BERT -* More Shiny -* Separate the code bases -* FastAPI -* Inspired by the [Royal College of Paediatrics and Child Health API](https://www.rcpch.ac.uk/resources/growth-charts/digital) -* [Documentation, documentation, documentation](https://github.com/CDU-data-science-team/PatientExperience-QDC) - -## Making it useful - -* Accurately rating low frequency categories -* Per category precision and recall -* Speed versus accuracy -* Representing the thematic structure - -## The future - -* Off the shelf, proprietary data collection systems dominate -* They often offer bundled analytic products of low quality -* The DS time can't and doesn't want to offer a complete data system -* How can we best contribute to improving patient experience for patients in the NHS? - * If the patient experience data won't come to the mountain... - -## Open source FTW! - -* Often individuals in the NHS don't want private companies to "benefit" from open code -* But if they make their products better with open code the patients win -* [Best practice as code](https://www.rcpch.ac.uk/news-events/news/royal-colleges-30-best-practice-code) - -## The projects - -* https://github.com/CDU-data-science-team/pxtextmining -* https://github.com/CDU-data-science-team/experiencesdashboard -* https://github.com/CDU-data-science-team/PatientExperience-QDC - -## The team - -* YiWen Hon (Python & Machine learning) -* Oluwasegun Apejoye (Shiny) - -Contact: - -* chris.beeley1@nhs.net -* https://fosstodon.org/@chrisbeeley +--- +title: "Text mining of patient experience data" +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-05-15 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## Patient experience + +* The NHS collects a lot of patient experience data +* Rate the service 1-5 (Very poor... Excellent) but also give written feedback + * "Parking was difficult" + * "Doctor was rude" + * "You saved my life" +* Many organisations lack the staffing to read all of the feedback in a systematic way + +## Text mining + +* We have built an algorithm to read it + * Theme + * "Criticality" +* Fits alongside other work happening within NHSE + * A framework for understanding patient experience + +## Patient experience 101 + +* Tick box scoring is not useful (or accurate) +* Text based data is _complex_ and built on _human experience_ +* We're not making word clouds! +* We're not classifying movie reviews or Reddit posts +* The tool should enhance, not replace, human understanding +* "A recommendation engine for feedback data" + +## Everything open, all the time + +* This project was [coded in the open](https://transform.england.nhs.uk/key-tools-and-info/digital-playbooks/open-source-digital-playbook/cdu-data-science-team/) and is MIT licensed +* Engage with the organisations as we find them + * Do they want code or a docker image? + * Do they want to fetch their own themes from an API? + * Do they want to use our dashboard? + +## Phase 1 + +* 10 categories and moderate performance on criticality analysis +* scikit-learn +* Shiny +* Reticulate +* R package of Python code + +## Golem all the things! + +* Opinionated way of building Shiny +* Allows flexibility in deployed versions using YAML +* Agnostic to deployment +* Emphasises dependency management and testing +* Separate "reactive" and "business" logic (see the [accompanying book](https://engineering-shiny.org/)) + +## Phase 2 + +* 30-50 categories and excellent criticality performance +* scikit-learn/ BERT +* More Shiny +* Separate the code bases +* FastAPI +* Inspired by the [Royal College of Paediatrics and Child Health API](https://www.rcpch.ac.uk/resources/growth-charts/digital) +* [Documentation, documentation, documentation](https://github.com/CDU-data-science-team/PatientExperience-QDC) + +## Making it useful + +* Accurately rating low frequency categories +* Per category precision and recall +* Speed versus accuracy +* Representing the thematic structure + +## The future + +* Off the shelf, proprietary data collection systems dominate +* They often offer bundled analytic products of low quality +* The DS time can't and doesn't want to offer a complete data system +* How can we best contribute to improving patient experience for patients in the NHS? + * If the patient experience data won't come to the mountain... + +## Open source FTW! + +* Often individuals in the NHS don't want private companies to "benefit" from open code +* But if they make their products better with open code the patients win +* [Best practice as code](https://www.rcpch.ac.uk/news-events/news/royal-colleges-30-best-practice-code) + +## The projects + +* https://github.com/CDU-data-science-team/pxtextmining +* https://github.com/CDU-data-science-team/experiencesdashboard +* https://github.com/CDU-data-science-team/PatientExperience-QDC + +## The team + +* YiWen Hon (Python & Machine learning) +* Oluwasegun Apejoye (Shiny) + +Contact: + +* chris.beeley1@nhs.net +* https://fosstodon.org/@chrisbeeley diff --git a/presentations/2023-05-15_text-mining/renv.lock b/presentations/2023-05-15_text-mining/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-05-15_text-mining/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-05-23_data-science-for-good/index.qmd b/presentations/2023-05-23_data-science-for-good/index.qmd index 4d77f62..b241f96 100644 --- a/presentations/2023-05-23_data-science-for-good/index.qmd +++ b/presentations/2023-05-23_data-science-for-good/index.qmd @@ -1,152 +1,158 @@ ---- -title: "What good data science looks like" -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-05-23 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - width: 1920 - height: 1080 - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/ - -## Patient experience - -* The NHS collects a lot of patient experience data -* Rate the service 1-5 (Very poor... Excellent) but also give written feedback - * "Parking was difficult" - * "Doctor was rude" - * "You saved my life" -* Many organisations lack the staffing to read all of the feedback in a systematic way -* Produce an algorithm to rate theme and "criticality" - -## Help people to do their jobs - -* Text based data is _complex_ and built on _human experience_ -* The tool should enhance, not replace, human understanding -* Enhancing search and filtering - * If they read 100 comments today, which should they read? -* "A recommendation engine for feedback data" - -## Reflect what users want - -* I have worked with this data since before it existed -* I came to realise that people were struggling to read all of their data -* Fits alongside other work happening within NHSE - * A framework for understanding patient experience - -## Useful - -* A fundamental principle is that everyone can use -* If you can run the code, run it -* If you can use the API, use it -* If you just want the dashboard, use it -* Credit to the [growth charts API](https://www.rcpch.ac.uk/resources/growth-charts/digital/about) - -## Understandable - -* Tuned to the users needs -* Not simply tuning accuracy scores -* Look at the type of mistake the model is making -* Look at the category it's predicting - * We can lose a few of common unimportant categories - * We need to get every rare and important category - -## Iterative - -* Year one - * 10 categories - * Moderate criticality performance - * No deep learning - * Weak dashboard - * Positive evaluation - -## Iterative - -* Year two - * 30-50 categories - * Strong criticality performance - * Deep learning - * Improved dashboard - * WIP -* Overall five minor versions of algorithm and seven of dashboard - -## Documented - -* We've documented in the way you usually would -* We were asked in year 1 to provide plain English documentation -* We made [a website](https://cdu-data-science-team.github.io/PatientExperience-QDC/) with all the product details - -## Develop skills of the staff, technical and otherwise - -* Year one created a Python programmer -* Year two created an R/ Shiny programmer -* The team has learned: - * Static website generation - * Text cleaning/ searching/ mining - * Collaborative coding practices - * Working with and communicating with users - * Linux, databases, APIs... - -## Benefits from, and benefits, the community - -![[NHSBSA R Shiny template](https://github.com/nhsbsa-data-analytics/nhsbsaShinyR)](golem.png) - -## Benefits from, and benefits, the community - -* We benefit and benefit from - * NHS-R - * NHS-Pycom - * Government Digital Service - * Colleagues and friends - -## Open and reproducible - -* Off the shelf, proprietary data collection systems dominate -* They often offer bundled analytic products of low quality -* The DS time can't and doesn't want to offer a complete data system -* How can we best contribute to improving patient experience for patients in the NHS? - * If the patient experience data won't come to the mountain... - -## Open source FTW! - -* Often individuals in the NHS don't want private companies to "benefit" from open code -* But if they make their products better with open code the patients win -* [Best practice as code](https://www.rcpch.ac.uk/news-events/news/royal-colleges-30-best-practice-code) - -## Fun! - -* Combing through spreadsheets looking for one comment is not fun -* Doing things the same way you did them last year is not fun -* Trying to implement a project that is too complicated is not fun - -  - -* Working with a diverse team with different skills is fun -* Accessing high quality documentation to understand a project better is fun* - -## Team and code - -* Andreas Soteriades (Y1) -* YiWen Hon, Oluwasegun Apejoye (Y2) - -  - -* [pxtextmining](https://github.com/CDU-data-science-team/pxtextmining) -* [experiencesdashboard](https://github.com/CDU-data-science-team/experiencesdashboard) -* [Documentation](https://github.com/CDU-data-science-team/PatientExperience-QDC) - -
- -* chris.beeley1@nhs.net -* https://fosstodon.org/@chrisbeeley +--- +title: "What good data science looks like" +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-05-23 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + width: 1920 + height: 1080 + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/ + +## Patient experience + +* The NHS collects a lot of patient experience data +* Rate the service 1-5 (Very poor... Excellent) but also give written feedback + * "Parking was difficult" + * "Doctor was rude" + * "You saved my life" +* Many organisations lack the staffing to read all of the feedback in a systematic way +* Produce an algorithm to rate theme and "criticality" + +## Help people to do their jobs + +* Text based data is _complex_ and built on _human experience_ +* The tool should enhance, not replace, human understanding +* Enhancing search and filtering + * If they read 100 comments today, which should they read? +* "A recommendation engine for feedback data" + +## Reflect what users want + +* I have worked with this data since before it existed +* I came to realise that people were struggling to read all of their data +* Fits alongside other work happening within NHSE + * A framework for understanding patient experience + +## Useful + +* A fundamental principle is that everyone can use +* If you can run the code, run it +* If you can use the API, use it +* If you just want the dashboard, use it +* Credit to the [growth charts API](https://www.rcpch.ac.uk/resources/growth-charts/digital/about) + +## Understandable + +* Tuned to the users needs +* Not simply tuning accuracy scores +* Look at the type of mistake the model is making +* Look at the category it's predicting + * We can lose a few of common unimportant categories + * We need to get every rare and important category + +## Iterative + +* Year one + * 10 categories + * Moderate criticality performance + * No deep learning + * Weak dashboard + * Positive evaluation + +## Iterative + +* Year two + * 30-50 categories + * Strong criticality performance + * Deep learning + * Improved dashboard + * WIP +* Overall five minor versions of algorithm and seven of dashboard + +## Documented + +* We've documented in the way you usually would +* We were asked in year 1 to provide plain English documentation +* We made [a website](https://cdu-data-science-team.github.io/PatientExperience-QDC/) with all the product details + +## Develop skills of the staff, technical and otherwise + +* Year one created a Python programmer +* Year two created an R/ Shiny programmer +* The team has learned: + * Static website generation + * Text cleaning/ searching/ mining + * Collaborative coding practices + * Working with and communicating with users + * Linux, databases, APIs... + +## Benefits from, and benefits, the community + +![[NHSBSA R Shiny template](https://github.com/nhsbsa-data-analytics/nhsbsaShinyR)](golem.png) + +## Benefits from, and benefits, the community + +* We benefit and benefit from + * NHS-R + * NHS-Pycom + * Government Digital Service + * Colleagues and friends + +## Open and reproducible + +* Off the shelf, proprietary data collection systems dominate +* They often offer bundled analytic products of low quality +* The DS time can't and doesn't want to offer a complete data system +* How can we best contribute to improving patient experience for patients in the NHS? + * If the patient experience data won't come to the mountain... + +## Open source FTW! + +* Often individuals in the NHS don't want private companies to "benefit" from open code +* But if they make their products better with open code the patients win +* [Best practice as code](https://www.rcpch.ac.uk/news-events/news/royal-colleges-30-best-practice-code) + +## Fun! + +* Combing through spreadsheets looking for one comment is not fun +* Doing things the same way you did them last year is not fun +* Trying to implement a project that is too complicated is not fun + +  + +* Working with a diverse team with different skills is fun +* Accessing high quality documentation to understand a project better is fun* + +## Team and code + +* Andreas Soteriades (Y1) +* YiWen Hon, Oluwasegun Apejoye (Y2) + +  + +* [pxtextmining](https://github.com/CDU-data-science-team/pxtextmining) +* [experiencesdashboard](https://github.com/CDU-data-science-team/experiencesdashboard) +* [Documentation](https://github.com/CDU-data-science-team/PatientExperience-QDC) + +
+ +* chris.beeley1@nhs.net +* https://fosstodon.org/@chrisbeeley diff --git a/presentations/2023-05-23_data-science-for-good/renv.lock b/presentations/2023-05-23_data-science-for-good/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-05-23_data-science-for-good/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-07-11_haca-nhp-demand-model/index.qmd b/presentations/2023-07-11_haca-nhp-demand-model/index.qmd index 8a64056..b495752 100644 --- a/presentations/2023-07-11_haca-nhp-demand-model/index.qmd +++ b/presentations/2023-07-11_haca-nhp-demand-model/index.qmd @@ -1,846 +1,851 @@ ---- -title: "An Introduction to the New Hospital Programme Demand Model" -subtitle: "HACA 2023" -author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" -date: 2023-07-11 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [https://tinyurl.com/haca23nhp][ds_presentations] ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-07-11_haca-nhp-demand-model/ - -## The team - -::: {layout-ncol=4} -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/large/public/Screenshot%202023-04-12%20at%2013.09.41.png?itok=BE3fvKxo) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Chris%20Beeley011.jpg?itok=em9LBwxS) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.20.01.png?itok=RLv4lbeG) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.30.13.png?itok=YCi_1JHq) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.34.07.png?itok=uzTRFP8D) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.40.01.png?itok=FZZFb4H4) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Paul_Seamer.jpg?itok=nkbJVKZL) - -![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.28.53.png?itok=HiOTdX_O) - -::: -## A hospital is a place where you can find people... - -::: {.incremental} -- having the best day of their life, -- the worst day of their life, -- the first day of their life, -- and the last day of their life. -::: - -## Planning is hard - -::: {.columns} - -:::: {.column} -[![](small_school.png)][school] -:::: - -:::: {.column} -- built with enough capacity to replace the existing school -- failed to take into account a new housing estate -- likely needs double the number of spaces within the next decade - -[BBC article][school] -:::: - -::: - -[school]: https://www.bbc.co.uk/news/uk-scotland-glasgow-west-65644082 - -## Review of existing models - -{{< video https://www.youtube.com/embed/RfiuBGD5IeU start="7175" width="640" height="360" >}} - -[Steven Wyatt - NHS-R 2022][sw_nhsr_22] - -[sw_nhsr_22]: https://www.youtube.com/watch?v=RfiuBGD5IeU&list=PLXCrMzQaI6c3kFGGOuR432J8tUJq_VhWp&index=17 - - -## Review of existing models - -- lots of models -- lots of external consultancies -- lots of similarities - -. . . - -- lots of repetition/duplication -- sufficiently different that comparing results is difficult -- methodological progress slow -- no base to build from - -::: {.notes} -- consultancies don't tend to offer products, but services -- difficult to compare different models to understand if differences are methodological or due to assumptions -- same issues seen 20/30 years ago -- learning and expertise gathered tends to be trapped within trusts, or kept secret by consultancies -::: - -## Common issues - -- handling uncertainty -- unnecessary/early aggregation -- poor coverage of some changes -- lack of ownership & auditability of assumptions -- conflating demand forecasting with affordability - -::: {.notes} -- most models handle changes like demographic changes and the impact of changes in occupancy rates -- but few try to handle addressing inequities, health status adjustment -::: - -# "How much, and what types of activity might a hospital need to accommodate in the future?" {.inverse} - -::: {.notes} - -- difficult problem -- a question that is routinely asked outside of new hospital planning -- a planning problem, not a forecasting problem, health systems want to influence the types and quantities of activity they deliver in future -- difficult to get a handle in confidence, the results should recognise the inherent uncertainty - -::: - -## Our model - -- ~~open source~~ (not quite yet...) -- uses standard, well-known datasets (e.g. HES, ONS population projections) -- currently handles Inpatient admissions, Outpatient attendances, and A&E arrivals -- extensible and adaptable -- covering all of the change factors -- stochastic Monte-Carlo model to handle uncertainty - - -## Project Structure - -::: {.columns} - -:::: {.column width=70%} -- Data Extraction (R + `{targets}` & Sql) -- Inputs App (R + `{shiny}`) -- Outputs App (R + `{shiny}`) -- Model Engine (Python & Docker) -- Azure Infrastructure (VM/ACR/ACI/Storage Accounts) -- All of the code is stored on GitHub (currently, private repos 😔) - -:::: - -:::: {.column width=30%} -```{mermaid} -%%| fig-responsive: false -%%| fig-height: 5 -flowchart TB - classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; - classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; - - A[Data Extraction] - B[Inputs App] - C[Model] - D[Outputs App] - - - SB[(input app data)] - SC[(model data)] - SD[(results data)] - - A ---> SB - A ---> SC - - SB ---> B - SC ---> C - - B ---> C - - C ---> SD - SD ---> D - - B -.-> D - - class A,B,C,D orange - class SB,SC,SD lightslate -``` - -:::: - -::: - -## Model Overview - -:::{.incremental} - -- the baseline data is a year worth of a provider's HES data -- each row in the baseline data is run through a series of steps -- each step creates a factor that says how many times (on average) to sample that row -- the factors are multiplied together and used to create a random Poisson value -- we resample the rows using this random values -- efficiencies are then applied, e.g. LoS reductions, type conversions - -::: - -::: {.notes} - -- IP/OP/A&E data -- complex, but not complicated - -::: - - -## Model Diagram - -```{mermaid} -flowchart TB - classDef blue fill:#5881c1,stroke:#2c2825,color:#2c2825; - classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; - classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; - classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; - classDef slate fill:#e0e2e3,stroke:#2c2825,color:#2c2825; - - S[Baseline Activity] - T[Future Activity] - - class S,T red - - subgraph rr[Row Resampling] - direction LR - - subgraph pop[Population Changes] - direction TB - pop_p[Population Growth] - pop_a[Age/Sex Structure] - pop_h[Population Specific Health Status] - - class pop_p,pop_a,pop_h orange - - pop_p --- pop_a --- pop_h - end - - subgraph dsi[Demand Supply Imbalances] - direction TB - dsi_w[Waiting List Adjustment] - dsi_r[Repatriation/Expatriation] - dsi_p[Private Healthcare Dynamics] - - class dsi_w,dsi_r,dsi_p orange - - dsi_w --- dsi_r --- dsi_p - end - - subgraph nsi[Need Supply Imbalances] - direction TB - nsi_g[Gaps in Care] - nsi_i[Inequalities] - nsi_t[Threshold Imbalances] - - class nsi_g,nsi_i,nsi_t orange - - nsi_g --- nsi_i --- nsi_t - end - - subgraph nda [Non-Demographic Adjustment] - direction TB - nda_m[Medical Interventions] - nda_c[Changes to National Standards] - nda_p[Patient Expectations] - - class nda_m,nda_c,nda_p orange - - nda_m --- nda_c --- nda_p - end - - subgraph mit[Activity Mitigators] - direction TB - mit_a[Activity Avoidance] - mit_t[Type Conversion] - mit_e[Efficiencies] - - class mit_a,mit_t,mit_e orange - - mit_a --- mit_t --- mit_e - end - - pop --- dsi --- nsi --- nda --- mit - - class dsi,nsi,pop,nda,mit lightslate - end - - class rr slate - - S --> rr --> T -``` - -::: {.notes} - -- uses either patient-level data, or minimal aggregation -- row resampling grouped into 5 broad groups - - population changes address the changes to the structure of the population and health status over the medium term - - demand supply imbalances: hospitals are currently struggling to keep pace with demand, so we correct for this to not carry forwards these into the future - - need supply imbalance: addressing gaps in care that currently exist - - non-demographic: such as the development of new medical technologies - - activity mitigators: strategies trusts adopt for reducing activity, or delivering activity more efficiently -- some assumptions set nationally, such as population growth via ONS population projections -- other assumptions set locally, with support from a Shiny app - -::: - -## Model Diagram - -```{mermaid} -flowchart TB - classDef blue fill:#5881c1,stroke:#2c2825,color:#2c2825; - classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; - classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; - classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; - classDef slate fill:#e0e2e3,stroke:#2c2825,color:#2c2825; - - S[Baseline Activity] - T[Future Activity] - - ORANGE[Implemented] - BLUE[Not yet implemented] - - class ORANGE orange - class BLUE blue - - class S,T red - - subgraph rr[Row Resampling] - direction LR - - subgraph pop[Population Changes] - direction TB - pop_p[Population Growth] - pop_a[Age/Sex Structure] - pop_h[Population Specific Health Status] - - class pop_p,pop_a,pop_h orange - - pop_p --- pop_a --- pop_h - end - - subgraph dsi[Demand Supply Imbalances] - direction TB - dsi_w[Waiting List Adjustment] - dsi_r[Repatriation/Expatriation] - dsi_p[Private Healthcare Dynamics] - - class dsi_w,dsi_r orange - class dsi_p blue - - dsi_w --- dsi_r --- dsi_p - end - - subgraph nsi[Need Supply Imbalances] - direction TB - nsi_g[Gaps in Care] - nsi_i[Inequalities] - nsi_t[Threshold Imbalances] - - class nsi_g,nsi_i,nsi_t blue - - nsi_g --- nsi_i --- nsi_t - end - - subgraph nda [Non-Demographic Adjustment] - direction TB - nda_m[Medical Interventions] - nda_c[Changes to National Standards] - nda_p[Patient Expectations] - - class nda_m,nda_c,nda_p blue - - nda_m --- nda_c --- nda_p - end - - subgraph mit[Activity Mitigators] - direction TB - mit_a[Activity Avoidance] - mit_t[Type Conversion] - mit_e[Efficiencies] - - class mit_a,mit_t,mit_e orange - - mit_a --- mit_t --- mit_e - end - - pop --- dsi --- nsi --- nda --- mit - - class dsi,nsi,pop,nda,mit lightslate - end - - class rr slate - - S --> rr --> T -``` - -## Monte Carlo Simulation - -::: {.columns} - -:::: {.column} -- We run the model N times, varying the input parameters each time slightly to handle the uncertainty. -- The results of the model are aggregated at the end of each model run -- The aggregated results are combined at the end into a single file -:::: - -:::: {.column} -```{mermaid} -flowchart LR - classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; - classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; - - A[Baseline Activity] - Ba[Model Run 0] - Bb[Model Run 1] - Bc[Model Run 2] - Bd[Model Run 3] - Bn[Model Run n] - C[Results] - - A ---> Ba ---> C - A ---> Bb ---> C - A ---> Bc ---> C - A ---> Bd ---> C - A ---> Bn ---> C - - class A,C red - class Ba,Bb,Bc,Bd,Bn orange - -``` - -::: {.small} -Inspired by - -- [MapReduce (Google, 2004)][map_reduce] -- [Split, Apply, Combine (H. Wickham, 2011)][sac] - -[map_reduce]: https://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf -[sac]: https://www.jstatsoft.org/article/download/v040i01/468 - -::: - -:::: - -::: - -## Model Parameters - -- We ask users to provide parameters in the form of 90% confidence intervals -- We can then convert these confidence intervals into distributions -- During the model we sample values from these distributions for each model parameter -- All of the parameters represent the average rate to sample a row of data from the baseline - -## Model Parameters - -> "We expect in the future to see between a 25% reduction and a 25% increase in this activity" - -::: {.columns} - -:::: {.column} - -- grey highlighted section: 90% confidence intervals -- black line: confidence intervals into distributions -- yellow points: sampled parameter for a model run - -:::: - -:::: {.column} - -```{r} -library(dplyr) -library(ggplot2) -library(patchwork) - -param_plot <- function(m, s, points, xlim = c(0, 2)) { - d <- \(.x) dnorm(.x, m, s) - - tibble(x = c(0, 2)) |> - ggplot(aes(x)) + - stat_function( - xlim = qnorm(c(0.05, 0.95), m, s), - fun = d, - geom = "area", - fill = "#b2b7b9" - ) + - stat_function( - fun = d, - xlim = xlim, - geom = "line", - linewidth = 3 - ) + - annotate( - "point", - x = points, - y = d(points), - colour = "#f9bf07", - size = 12 - ) + - expand_limits(x = c(0, 2)) + - theme_void() + - theme(axis.text.x = element_text()) -} -``` - -```{r} -#| fig-height: 6 -param_plot(1, .15, c(0.85, 0.9, 1.05)) -``` - -:::: - -::: - -## Model Parameters - -> "We expect in the future to see between a 20% reduction and a 90% reduction in this activity" - -::: {.columns} - -:::: {.column} - -- grey highlighted section: 90% confidence intervals -- black line: confidence intervals into distributions -- yellow points: sampled parameter for a model run - -:::: - -:::: {.column} - -```{r} -#| fig-height: 6 -param_plot(0.4, .2, c(0.2, 0.4, 0.7)) -``` - -:::: - -::: - -## Model Parameters - -> "We expect in the future to see between a 2% reduction and an 18% reduction in this activity" - -::: {.columns} - -:::: {.column} - -- grey highlighted section: 90% confidence intervals -- black line: confidence intervals into distributions -- yellow points: sampled parameter for a model run - -:::: - -:::: {.column} - -```{r} -#| fig-height: 6 -param_plot(0.9, .05, c(0.8, 0.89, 1), xlim = c(0, 1)) -``` - -:::: - -::: - -## Model Run Example (1) - -::: {.columns .small-table} - -:::: {.column width=50%} - -| id | age | sex | specialty | los | f | -| -: | --: | :-: | :-------: | --: | ---: | -| 1 | 50 | m | 100 | 4 | 1.00 | -| 2 | 50 | m | 110 | 3 | 1.00 | -| 3 | 51 | m | 120 | 5 | 1.00 | -| 4 | 50 | f | 100 | 1 | 1.00 | -| 5 | 50 | f | 110 | 2 | 1.00 | -| 6 | 52 | f | 120 | 0 | 1.00 | - -:::: - -:::: {.column width=25%} - -:::: - -:::: {.column width=25%} - -:::: - -::: - -::: {.absolute bottom=10} - -Start with baseline data - we are going to sample each row exactly once (column `f`). - -::: - -## Model Run Example (2) - -::: {.columns .small-table} - -:::: {.column width=50%} - -| id | age | sex | specialty | los | f | -| -: | --: | :-: | :-------: | --: | ---: | -| 1 | 50 | m | 100 | 4 | 1.00 | -| 2 | 50 | m | 110 | 3 | 1.00 | -| 3 | 51 | m | 120 | 5 | 1.00 | -| 4 | 50 | f | 100 | 1 | 1.00 | -| 5 | 50 | f | 110 | 2 | 1.00 | -| 6 | 52 | f | 120 | 0 | 1.00 | - -:::: - -:::: {.column width=25%} - -| age | sex | f | -| --: | :-: | ---: | -| 50 | m | 0.90 | -| 51 | m | 1.10 | -| 52 | m | 1.20 | -| 50 | f | 0.80 | -| 51 | f | 0.70 | -| 52 | f | 1.30 | - -:::: - -:::: {.column width=25%} - -| f | -| -----------------: | -| 1.00 × 0.90 = 0.90 | -| 1.00 × 0.90 = 0.90 | -| 1.00 × 1.10 = 1.10 | -| 1.00 × 0.80 = 0.80 | -| 1.00 × 0.80 = 0.80 | -| 1.00 × 1.30 = 1.30 | - -:::: - -::: - -::: {.absolute bottom=10} - -We perform a step where we join based on age and sex, then update the `f` column. - -::: - -## Model Run Example (3) - -::: {.columns .small-table} - -:::: {.column width=50%} - -| id | age | sex | specialty | los | f | -| -: | --: | :-: | :-------: | --: | ---: | -| 1 | 50 | m | 100 | 4 | 0.90 | -| 2 | 50 | m | 110 | 3 | 0.90 | -| 3 | 51 | m | 120 | 5 | 1.10 | -| 4 | 50 | f | 100 | 1 | 0.80 | -| 5 | 50 | f | 110 | 2 | 0.80 | -| 6 | 52 | f | 120 | 0 | 1.30 | - -:::: - -:::: {.column width=25%} - -| specialty | f | -| :-------: | ---: | -| 100 | 0.90 | -| 110 | 1.10 | - -:::: - -:::: {.column width=25%} - -| f | -| -----------------: | -| 0.90 × 0.90 = 0.81 | -| 0.90 × 1.10 = 0.99 | -| 1.10 × 1.00 = 1.10 | -| 0.80 × 0.90 = 0.72 | -| 0.80 × 1.10 = 0.88 | -| 1.30 × 1.00 = 1.30 | - -:::: - -::: - -::: {.absolute bottom=10} - -The next step joins on the specialty column, again updating `f`. Note, if there is no value to join on, then we multiply -by 1. - -::: - -## Model Run Example (4) - -::: {.columns .small-table} - -:::: {.column width=50%} - -| id | age | sex | specialty | los | f | n | -| -: | --: | :-: | :-------: | --: | ---: | -: | -| 1 | 50 | m | 100 | 4 | 0.90 | 1 | -| 2 | 50 | m | 110 | 3 | 0.90 | 0 | -| 3 | 51 | m | 120 | 5 | 1.10 | 2 | -| 4 | 50 | f | 100 | 1 | 0.80 | 1 | -| 5 | 50 | f | 110 | 2 | 0.80 | 0 | -| 6 | 52 | f | 120 | 0 | 1.30 | 3 | - -:::: - -:::: {.column width=50%} - -| id | age | sex | specialty | los | -| -: | --: | :-: | :-------: | --: | -| 1 | 50 | m | 100 | 4 | -| 3 | 51 | m | 120 | 5 | -| 3 | 51 | m | 120 | 5 | -| 4 | 50 | f | 100 | 1 | -| 6 | 52 | f | 120 | 0 | -| 6 | 52 | f | 120 | 0 | -| 6 | 52 | f | 120 | 0 | - -:::: - -::: - -::: {.absolute bottom=10} - -Once all of the steps are performed, sample a random value `n` from a Poisson distribution with `λ=f`, then we select -each row `n` times. - -::: - -## Model Run Example (5) - -::: {.columns .small-table} - -:::: {.column width=50%} - -| id | age | sex | specialty | los | g | -| -: | --: | :-: | :-------: | --: | ---: | -| 1 | 50 | m | 100 | 4 | 0.75 | -| 3 | 51 | m | 120 | 5 | 0.50 | -| 3 | 51 | m | 120 | 5 | 1.00 | -| 4 | 50 | f | 100 | 1 | 0.90 | -| 6 | 52 | f | 120 | 0 | 0.80 | -| 6 | 52 | f | 120 | 0 | 0.80 | -| 6 | 52 | f | 120 | 0 | 0.80 | -:::: - -:::: {.column width=50%} - - -| id | age | sex | specialty | los | -| -: | --: | :-: | :-------: | --: | -| 1 | 50 | m | 100 | 2 | -| 3 | 51 | m | 120 | 1 | -| 3 | 51 | m | 120 | 5 | -| 4 | 50 | f | 100 | 0 | -| 6 | 52 | f | 120 | 0 | -| 6 | 52 | f | 120 | 0 | -| 6 | 52 | f | 120 | 0 | - -:::: - -::: - -::: {.absolute bottom=10} - -After resampling, we apply efficiency steps. E.g., similar joins are used to -create column `g`, which is then used to sample a new LOS from a binomial distribution. - -::: - -## How the model is built - -- The model is built in Python and can be run on any machine you can install Python on -- Uses various packages, such as `numpy` and `pandas` -- Reads data in `.parquet` format for efficiency -- Returns aggregated results as a `.json` file -- Could also output full row level results if needed - -## How the model is built - -- Code is built in a modular approach -- Each activity type (Inpatients/Outpatients/A&E) has its own model code -- Code is reused where possible (e.g. all three models share the code for demographic adjustment) - -## How the model is deployed - -- Deployed as a Docker Container -- Runs in Azure Container Instances -- Each model run creates a new container, and the container is destroyed when the model run completes - -## Data Extraction - -- Uses principles of [RAP][RAP], using R + `{targets}` and Sql -- All of the data required to run the model -- Data is extracted from various sources - * Sql Datawarehouse (HES data) - * ONS population projections + life expectancy tables - * Central returns, e.g. KH03 - * ODS data (organisation names, successors) -- Extracted data is uploaded to Azure storage containers - -[RAP]: https://analysisfunction.civilservice.gov.uk/support/reproducible-analytical-pipelines/ - -## Inputs App - -A `{shiny}` app that allows the user to set parameters, and submit as a job to run the model with those values. - -![](inputs_app-1.png){fig-align="center" width=75%} - - -## Inputs App - -![](inputs_app-2.png){fig-align="center" width=85%} - -## Outputs App - -A `{shiny}` app that allows the user to view the results of model runs. - -![](outputs_app-1.png){fig-align="center" width=75%} - - -## Outputs App - -![](outputs_app-2.png){fig-align="center" width=85%} - -## Questions? - -

- -#### Contact The Strategy Unit - -::: {.no-bullets} -- {{< fa envelope >}} [strategy.unit@nhs.net](mailto:strategy.unit@nhs.net) -- {{< fa brands github size=1x >}} [The-Strategy-Unit](https://github.com/The-Strategy-Unit) -::: - -#### Contact Me - -::: {.no-bullets} -- {{< fa envelope >}} [thomas.jemmett@nhs.net](mailto:thomas.jemmett@nhs.net) -- {{< fa brands github size=1x >}} [tomjemmett](https://github.com/tomjemmett) +--- +title: "An Introduction to the New Hospital Programme Demand Model" +subtitle: "HACA 2023" +author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" +date: 2023-07-11 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [https://tinyurl.com/haca23nhp][ds_presentations] +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-07-11_haca-nhp-demand-model/ + +## The team + +::: {layout-ncol=4} +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/large/public/Screenshot%202023-04-12%20at%2013.09.41.png?itok=BE3fvKxo) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Chris%20Beeley011.jpg?itok=em9LBwxS) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.20.01.png?itok=RLv4lbeG) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.30.13.png?itok=YCi_1JHq) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.34.07.png?itok=uzTRFP8D) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.40.01.png?itok=FZZFb4H4) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Paul_Seamer.jpg?itok=nkbJVKZL) + +![](https://www.strategyunitwm.nhs.uk/sites/default/files/styles/crop_square_image/public/Screenshot%202023-04-12%20at%2013.28.53.png?itok=HiOTdX_O) + +::: +## A hospital is a place where you can find people... + +::: {.incremental} +- having the best day of their life, +- the worst day of their life, +- the first day of their life, +- and the last day of their life. +::: + +## Planning is hard + +::: {.columns} + +:::: {.column} +[![](small_school.png)][school] +:::: + +:::: {.column} +- built with enough capacity to replace the existing school +- failed to take into account a new housing estate +- likely needs double the number of spaces within the next decade + +[BBC article][school] +:::: + +::: + +[school]: https://www.bbc.co.uk/news/uk-scotland-glasgow-west-65644082 + +## Review of existing models + +{{< video https://www.youtube.com/embed/RfiuBGD5IeU start="7175" width="640" height="360" >}} + +[Steven Wyatt - NHS-R 2022][sw_nhsr_22] + +[sw_nhsr_22]: https://www.youtube.com/watch?v=RfiuBGD5IeU&list=PLXCrMzQaI6c3kFGGOuR432J8tUJq_VhWp&index=17 + + +## Review of existing models + +- lots of models +- lots of external consultancies +- lots of similarities + +. . . + +- lots of repetition/duplication +- sufficiently different that comparing results is difficult +- methodological progress slow +- no base to build from + +::: {.notes} +- consultancies don't tend to offer products, but services +- difficult to compare different models to understand if differences are methodological or due to assumptions +- same issues seen 20/30 years ago +- learning and expertise gathered tends to be trapped within trusts, or kept secret by consultancies +::: + +## Common issues + +- handling uncertainty +- unnecessary/early aggregation +- poor coverage of some changes +- lack of ownership & auditability of assumptions +- conflating demand forecasting with affordability + +::: {.notes} +- most models handle changes like demographic changes and the impact of changes in occupancy rates +- but few try to handle addressing inequities, health status adjustment +::: + +# "How much, and what types of activity might a hospital need to accommodate in the future?" {.inverse} + +::: {.notes} + +- difficult problem +- a question that is routinely asked outside of new hospital planning +- a planning problem, not a forecasting problem, health systems want to influence the types and quantities of activity they deliver in future +- difficult to get a handle in confidence, the results should recognise the inherent uncertainty + +::: + +## Our model + +- ~~open source~~ (not quite yet...) +- uses standard, well-known datasets (e.g. HES, ONS population projections) +- currently handles Inpatient admissions, Outpatient attendances, and A&E arrivals +- extensible and adaptable +- covering all of the change factors +- stochastic Monte-Carlo model to handle uncertainty + + +## Project Structure + +::: {.columns} + +:::: {.column width=70%} +- Data Extraction (R + `{targets}` & Sql) +- Inputs App (R + `{shiny}`) +- Outputs App (R + `{shiny}`) +- Model Engine (Python & Docker) +- Azure Infrastructure (VM/ACR/ACI/Storage Accounts) +- All of the code is stored on GitHub (currently, private repos 😔) + +:::: + +:::: {.column width=30%} +```{mermaid} +%%| fig-responsive: false +%%| fig-height: 5 +flowchart TB + classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; + classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; + + A[Data Extraction] + B[Inputs App] + C[Model] + D[Outputs App] + + + SB[(input app data)] + SC[(model data)] + SD[(results data)] + + A ---> SB + A ---> SC + + SB ---> B + SC ---> C + + B ---> C + + C ---> SD + SD ---> D + + B -.-> D + + class A,B,C,D orange + class SB,SC,SD lightslate +``` + +:::: + +::: + +## Model Overview + +:::{.incremental} + +- the baseline data is a year worth of a provider's HES data +- each row in the baseline data is run through a series of steps +- each step creates a factor that says how many times (on average) to sample that row +- the factors are multiplied together and used to create a random Poisson value +- we resample the rows using this random values +- efficiencies are then applied, e.g. LoS reductions, type conversions + +::: + +::: {.notes} + +- IP/OP/A&E data +- complex, but not complicated + +::: + + +## Model Diagram + +```{mermaid} +flowchart TB + classDef blue fill:#5881c1,stroke:#2c2825,color:#2c2825; + classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; + classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; + classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; + classDef slate fill:#e0e2e3,stroke:#2c2825,color:#2c2825; + + S[Baseline Activity] + T[Future Activity] + + class S,T red + + subgraph rr[Row Resampling] + direction LR + + subgraph pop[Population Changes] + direction TB + pop_p[Population Growth] + pop_a[Age/Sex Structure] + pop_h[Population Specific Health Status] + + class pop_p,pop_a,pop_h orange + + pop_p --- pop_a --- pop_h + end + + subgraph dsi[Demand Supply Imbalances] + direction TB + dsi_w[Waiting List Adjustment] + dsi_r[Repatriation/Expatriation] + dsi_p[Private Healthcare Dynamics] + + class dsi_w,dsi_r,dsi_p orange + + dsi_w --- dsi_r --- dsi_p + end + + subgraph nsi[Need Supply Imbalances] + direction TB + nsi_g[Gaps in Care] + nsi_i[Inequalities] + nsi_t[Threshold Imbalances] + + class nsi_g,nsi_i,nsi_t orange + + nsi_g --- nsi_i --- nsi_t + end + + subgraph nda [Non-Demographic Adjustment] + direction TB + nda_m[Medical Interventions] + nda_c[Changes to National Standards] + nda_p[Patient Expectations] + + class nda_m,nda_c,nda_p orange + + nda_m --- nda_c --- nda_p + end + + subgraph mit[Activity Mitigators] + direction TB + mit_a[Activity Avoidance] + mit_t[Type Conversion] + mit_e[Efficiencies] + + class mit_a,mit_t,mit_e orange + + mit_a --- mit_t --- mit_e + end + + pop --- dsi --- nsi --- nda --- mit + + class dsi,nsi,pop,nda,mit lightslate + end + + class rr slate + + S --> rr --> T +``` + +::: {.notes} + +- uses either patient-level data, or minimal aggregation +- row resampling grouped into 5 broad groups + - population changes address the changes to the structure of the population and health status over the medium term + - demand supply imbalances: hospitals are currently struggling to keep pace with demand, so we correct for this to not carry forwards these into the future + - need supply imbalance: addressing gaps in care that currently exist + - non-demographic: such as the development of new medical technologies + - activity mitigators: strategies trusts adopt for reducing activity, or delivering activity more efficiently +- some assumptions set nationally, such as population growth via ONS population projections +- other assumptions set locally, with support from a Shiny app + +::: + +## Model Diagram + +```{mermaid} +flowchart TB + classDef blue fill:#5881c1,stroke:#2c2825,color:#2c2825; + classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; + classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; + classDef lightslate fill:#b2b7b9,stroke:#2c2825,color:#2c2825; + classDef slate fill:#e0e2e3,stroke:#2c2825,color:#2c2825; + + S[Baseline Activity] + T[Future Activity] + + ORANGE[Implemented] + BLUE[Not yet implemented] + + class ORANGE orange + class BLUE blue + + class S,T red + + subgraph rr[Row Resampling] + direction LR + + subgraph pop[Population Changes] + direction TB + pop_p[Population Growth] + pop_a[Age/Sex Structure] + pop_h[Population Specific Health Status] + + class pop_p,pop_a,pop_h orange + + pop_p --- pop_a --- pop_h + end + + subgraph dsi[Demand Supply Imbalances] + direction TB + dsi_w[Waiting List Adjustment] + dsi_r[Repatriation/Expatriation] + dsi_p[Private Healthcare Dynamics] + + class dsi_w,dsi_r orange + class dsi_p blue + + dsi_w --- dsi_r --- dsi_p + end + + subgraph nsi[Need Supply Imbalances] + direction TB + nsi_g[Gaps in Care] + nsi_i[Inequalities] + nsi_t[Threshold Imbalances] + + class nsi_g,nsi_i,nsi_t blue + + nsi_g --- nsi_i --- nsi_t + end + + subgraph nda [Non-Demographic Adjustment] + direction TB + nda_m[Medical Interventions] + nda_c[Changes to National Standards] + nda_p[Patient Expectations] + + class nda_m,nda_c,nda_p blue + + nda_m --- nda_c --- nda_p + end + + subgraph mit[Activity Mitigators] + direction TB + mit_a[Activity Avoidance] + mit_t[Type Conversion] + mit_e[Efficiencies] + + class mit_a,mit_t,mit_e orange + + mit_a --- mit_t --- mit_e + end + + pop --- dsi --- nsi --- nda --- mit + + class dsi,nsi,pop,nda,mit lightslate + end + + class rr slate + + S --> rr --> T +``` + +## Monte Carlo Simulation + +::: {.columns} + +:::: {.column} +- We run the model N times, varying the input parameters each time slightly to handle the uncertainty. +- The results of the model are aggregated at the end of each model run +- The aggregated results are combined at the end into a single file +:::: + +:::: {.column} +```{mermaid} +flowchart LR + classDef orange fill:#f9bf07,stroke:#2c2825,color:#2c2825; + classDef red fill:#ec6555,stroke:#2c2825,color:#2c2825; + + A[Baseline Activity] + Ba[Model Run 0] + Bb[Model Run 1] + Bc[Model Run 2] + Bd[Model Run 3] + Bn[Model Run n] + C[Results] + + A ---> Ba ---> C + A ---> Bb ---> C + A ---> Bc ---> C + A ---> Bd ---> C + A ---> Bn ---> C + + class A,C red + class Ba,Bb,Bc,Bd,Bn orange + +``` + +::: {.small} +Inspired by + +- [MapReduce (Google, 2004)][map_reduce] +- [Split, Apply, Combine (H. Wickham, 2011)][sac] + +[map_reduce]: https://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf +[sac]: https://www.jstatsoft.org/article/download/v040i01/468 + +::: + +:::: + +::: + +## Model Parameters + +- We ask users to provide parameters in the form of 90% confidence intervals +- We can then convert these confidence intervals into distributions +- During the model we sample values from these distributions for each model parameter +- All of the parameters represent the average rate to sample a row of data from the baseline + +## Model Parameters + +> "We expect in the future to see between a 25% reduction and a 25% increase in this activity" + +::: {.columns} + +:::: {.column} + +- grey highlighted section: 90% confidence intervals +- black line: confidence intervals into distributions +- yellow points: sampled parameter for a model run + +:::: + +:::: {.column} + +```{r} +library(dplyr) +library(ggplot2) +library(patchwork) + +param_plot <- function(m, s, points, xlim = c(0, 2)) { + d <- \(.x) dnorm(.x, m, s) + + tibble(x = c(0, 2)) |> + ggplot(aes(x)) + + stat_function( + xlim = qnorm(c(0.05, 0.95), m, s), + fun = d, + geom = "area", + fill = "#b2b7b9" + ) + + stat_function( + fun = d, + xlim = xlim, + geom = "line", + linewidth = 3 + ) + + annotate( + "point", + x = points, + y = d(points), + colour = "#f9bf07", + size = 12 + ) + + expand_limits(x = c(0, 2)) + + theme_void() + + theme(axis.text.x = element_text()) +} +``` + +```{r} +#| fig-height: 6 +param_plot(1, .15, c(0.85, 0.9, 1.05)) +``` + +:::: + +::: + +## Model Parameters + +> "We expect in the future to see between a 20% reduction and a 90% reduction in this activity" + +::: {.columns} + +:::: {.column} + +- grey highlighted section: 90% confidence intervals +- black line: confidence intervals into distributions +- yellow points: sampled parameter for a model run + +:::: + +:::: {.column} + +```{r} +#| fig-height: 6 +param_plot(0.4, .2, c(0.2, 0.4, 0.7)) +``` + +:::: + +::: + +## Model Parameters + +> "We expect in the future to see between a 2% reduction and an 18% reduction in this activity" + +::: {.columns} + +:::: {.column} + +- grey highlighted section: 90% confidence intervals +- black line: confidence intervals into distributions +- yellow points: sampled parameter for a model run + +:::: + +:::: {.column} + +```{r} +#| fig-height: 6 +param_plot(0.9, .05, c(0.8, 0.89, 1), xlim = c(0, 1)) +``` + +:::: + +::: + +## Model Run Example (1) + +::: {.columns .small-table} + +:::: {.column width=50%} + +| id | age | sex | specialty | los | f | +| -: | --: | :-: | :-------: | --: | ---: | +| 1 | 50 | m | 100 | 4 | 1.00 | +| 2 | 50 | m | 110 | 3 | 1.00 | +| 3 | 51 | m | 120 | 5 | 1.00 | +| 4 | 50 | f | 100 | 1 | 1.00 | +| 5 | 50 | f | 110 | 2 | 1.00 | +| 6 | 52 | f | 120 | 0 | 1.00 | + +:::: + +:::: {.column width=25%} + +:::: + +:::: {.column width=25%} + +:::: + +::: + +::: {.absolute bottom=10} + +Start with baseline data - we are going to sample each row exactly once (column `f`). + +::: + +## Model Run Example (2) + +::: {.columns .small-table} + +:::: {.column width=50%} + +| id | age | sex | specialty | los | f | +| -: | --: | :-: | :-------: | --: | ---: | +| 1 | 50 | m | 100 | 4 | 1.00 | +| 2 | 50 | m | 110 | 3 | 1.00 | +| 3 | 51 | m | 120 | 5 | 1.00 | +| 4 | 50 | f | 100 | 1 | 1.00 | +| 5 | 50 | f | 110 | 2 | 1.00 | +| 6 | 52 | f | 120 | 0 | 1.00 | + +:::: + +:::: {.column width=25%} + +| age | sex | f | +| --: | :-: | ---: | +| 50 | m | 0.90 | +| 51 | m | 1.10 | +| 52 | m | 1.20 | +| 50 | f | 0.80 | +| 51 | f | 0.70 | +| 52 | f | 1.30 | + +:::: + +:::: {.column width=25%} + +| f | +| -----------------: | +| 1.00 × 0.90 = 0.90 | +| 1.00 × 0.90 = 0.90 | +| 1.00 × 1.10 = 1.10 | +| 1.00 × 0.80 = 0.80 | +| 1.00 × 0.80 = 0.80 | +| 1.00 × 1.30 = 1.30 | + +:::: + +::: + +::: {.absolute bottom=10} + +We perform a step where we join based on age and sex, then update the `f` column. + +::: + +## Model Run Example (3) + +::: {.columns .small-table} + +:::: {.column width=50%} + +| id | age | sex | specialty | los | f | +| -: | --: | :-: | :-------: | --: | ---: | +| 1 | 50 | m | 100 | 4 | 0.90 | +| 2 | 50 | m | 110 | 3 | 0.90 | +| 3 | 51 | m | 120 | 5 | 1.10 | +| 4 | 50 | f | 100 | 1 | 0.80 | +| 5 | 50 | f | 110 | 2 | 0.80 | +| 6 | 52 | f | 120 | 0 | 1.30 | + +:::: + +:::: {.column width=25%} + +| specialty | f | +| :-------: | ---: | +| 100 | 0.90 | +| 110 | 1.10 | + +:::: + +:::: {.column width=25%} + +| f | +| -----------------: | +| 0.90 × 0.90 = 0.81 | +| 0.90 × 1.10 = 0.99 | +| 1.10 × 1.00 = 1.10 | +| 0.80 × 0.90 = 0.72 | +| 0.80 × 1.10 = 0.88 | +| 1.30 × 1.00 = 1.30 | + +:::: + +::: + +::: {.absolute bottom=10} + +The next step joins on the specialty column, again updating `f`. Note, if there is no value to join on, then we multiply +by 1. + +::: + +## Model Run Example (4) + +::: {.columns .small-table} + +:::: {.column width=50%} + +| id | age | sex | specialty | los | f | n | +| -: | --: | :-: | :-------: | --: | ---: | -: | +| 1 | 50 | m | 100 | 4 | 0.90 | 1 | +| 2 | 50 | m | 110 | 3 | 0.90 | 0 | +| 3 | 51 | m | 120 | 5 | 1.10 | 2 | +| 4 | 50 | f | 100 | 1 | 0.80 | 1 | +| 5 | 50 | f | 110 | 2 | 0.80 | 0 | +| 6 | 52 | f | 120 | 0 | 1.30 | 3 | + +:::: + +:::: {.column width=50%} + +| id | age | sex | specialty | los | +| -: | --: | :-: | :-------: | --: | +| 1 | 50 | m | 100 | 4 | +| 3 | 51 | m | 120 | 5 | +| 3 | 51 | m | 120 | 5 | +| 4 | 50 | f | 100 | 1 | +| 6 | 52 | f | 120 | 0 | +| 6 | 52 | f | 120 | 0 | +| 6 | 52 | f | 120 | 0 | + +:::: + +::: + +::: {.absolute bottom=10} + +Once all of the steps are performed, sample a random value `n` from a Poisson distribution with `λ=f`, then we select +each row `n` times. + +::: + +## Model Run Example (5) + +::: {.columns .small-table} + +:::: {.column width=50%} + +| id | age | sex | specialty | los | g | +| -: | --: | :-: | :-------: | --: | ---: | +| 1 | 50 | m | 100 | 4 | 0.75 | +| 3 | 51 | m | 120 | 5 | 0.50 | +| 3 | 51 | m | 120 | 5 | 1.00 | +| 4 | 50 | f | 100 | 1 | 0.90 | +| 6 | 52 | f | 120 | 0 | 0.80 | +| 6 | 52 | f | 120 | 0 | 0.80 | +| 6 | 52 | f | 120 | 0 | 0.80 | +:::: + +:::: {.column width=50%} + + +| id | age | sex | specialty | los | +| -: | --: | :-: | :-------: | --: | +| 1 | 50 | m | 100 | 2 | +| 3 | 51 | m | 120 | 1 | +| 3 | 51 | m | 120 | 5 | +| 4 | 50 | f | 100 | 0 | +| 6 | 52 | f | 120 | 0 | +| 6 | 52 | f | 120 | 0 | +| 6 | 52 | f | 120 | 0 | + +:::: + +::: + +::: {.absolute bottom=10} + +After resampling, we apply efficiency steps. E.g., similar joins are used to +create column `g`, which is then used to sample a new LOS from a binomial distribution. + +::: + +## How the model is built + +- The model is built in Python and can be run on any machine you can install Python on +- Uses various packages, such as `numpy` and `pandas` +- Reads data in `.parquet` format for efficiency +- Returns aggregated results as a `.json` file +- Could also output full row level results if needed + +## How the model is built + +- Code is built in a modular approach +- Each activity type (Inpatients/Outpatients/A&E) has its own model code +- Code is reused where possible (e.g. all three models share the code for demographic adjustment) + +## How the model is deployed + +- Deployed as a Docker Container +- Runs in Azure Container Instances +- Each model run creates a new container, and the container is destroyed when the model run completes + +## Data Extraction + +- Uses principles of [RAP][RAP], using R + `{targets}` and Sql +- All of the data required to run the model +- Data is extracted from various sources + * Sql Datawarehouse (HES data) + * ONS population projections + life expectancy tables + * Central returns, e.g. KH03 + * ODS data (organisation names, successors) +- Extracted data is uploaded to Azure storage containers + +[RAP]: https://analysisfunction.civilservice.gov.uk/support/reproducible-analytical-pipelines/ + +## Inputs App + +A `{shiny}` app that allows the user to set parameters, and submit as a job to run the model with those values. + +![](inputs_app-1.png){fig-align="center" width=75%} + + +## Inputs App + +![](inputs_app-2.png){fig-align="center" width=85%} + +## Outputs App + +A `{shiny}` app that allows the user to view the results of model runs. + +![](outputs_app-1.png){fig-align="center" width=75%} + + +## Outputs App + +![](outputs_app-2.png){fig-align="center" width=85%} + +## Questions? + +

+ +#### Contact The Strategy Unit + +::: {.no-bullets} +- {{< fa envelope >}} [strategy.unit@nhs.net](mailto:strategy.unit@nhs.net) +- {{< fa brands github size=1x >}} [The-Strategy-Unit](https://github.com/The-Strategy-Unit) +::: + +#### Contact Me + +::: {.no-bullets} +- {{< fa envelope >}} [thomas.jemmett@nhs.net](mailto:thomas.jemmett@nhs.net) +- {{< fa brands github size=1x >}} [tomjemmett](https://github.com/tomjemmett) ::: \ No newline at end of file diff --git a/presentations/2023-07-11_haca-nhp-demand-model/renv.lock b/presentations/2023-07-11_haca-nhp-demand-model/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-07-11_haca-nhp-demand-model/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-08-02_mlcsu-ksn-meeting/index.qmd b/presentations/2023-08-02_mlcsu-ksn-meeting/index.qmd index 150879d..b252e92 100644 --- a/presentations/2023-08-02_mlcsu-ksn-meeting/index.qmd +++ b/presentations/2023-08-02_mlcsu-ksn-meeting/index.qmd @@ -1,179 +1,185 @@ ---- -title: Travels with R and Python -subtitle: the power of data science in healthcare -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2023-08-02 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-08-02_mlcsu-ksn-meeting - -## What is data science? - -* "A data scientist knows more about computer science than the average statistician, and more about statistics than the average computer scientist" - -[(Josh Wills, a former head of data engineering at Slack)](https://medium.com/odscjournal/data-scientists-versus-statisticians-8ea146b7a47f) - -## Drew Conway's famous Venn diagram - -![](Data_Science_VD.png){fig-alt="Data science Venn diagram" fig-align="center"} - -[Source](http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram) - -## What are the skills of data science? - -* Analysis - * ML - * Stats - * Data viz -* Software engineering - * Programming - * SQL/ data - * DevOps - * RAP - -## What are the skills of data science? - -* Domain knowledge - * Communication - * Problem formulation - * Dashboards and reports - -## Stats and data viz - -* ML leans a bit more towards atheoretical prediction -* Stats leans a bit more towards inference (but they both do both) -* Data scientists may use different visualisations - * Interactive web based tools - * Dashboard based visualisers e.g. [{stminsights}](https://github.com/cschwem2er/stminsights) - -## Software engineering - -* Programming - * No/ low code data science? -* SQL/ data - * Tend to use reproducible automated processes -* DevOps - * Plan, code, build, test, release, deploy, operate, monitor -* RAP - * I will come back to this - -## Domain knowledge - -* Do stuff that matters - * The best minds of my generation are thinking about how to make people click ads. That sucks. [Jeffrey Hammerbacher](https://www.fastcompany.com/3008436/why-data-god-jeffrey-hammerbacher-left-facebook-found-cloudera) -* Convince other people that it matters -* This is the hardest part of data science - -## RAP - -* Data science isn't RAP -* RAP isn't data science -* They are firm friends - -## Reproducibility - -* Reproducibility in science -* The [$6B spreadsheet error](https://baselinescenario.com/2013/02/09/the-importance-of-excel/) -* [George Osbourne's austerity was based on a spreadsheet error](https://www.theguardian.com/politics/2013/apr/18/uncovered-error-george-osborne-austerity) -* For us, reproducibility also means we can do the same analysis 50 times in one minute - * Which is why I started down the road of data science - -## What is RAP - -* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset -* RAP should be: - -> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training - -:::{.footer} -[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) -::: - -## Levels of RAP- Baseline - -* Data produced by code in an open-source language (e.g., Python, R, SQL) -* Code is version controlled -* Repository includes a README.md file that clearly details steps a user must follow to reproduce the code -* Code has been peer reviewed -* Code is published in the open and linked to & from accompanying publication (if relevant) - -:::{.footer} -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) -::: - -## Levels of RAP- Silver - -* Code is well-documented... -* Code is well-organised following standard directory format -* Reusable functions and/or classes are used where appropriate -* Pipeline includes a testing framework -* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml) -* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format - -:::{.footer} -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) -::: - -## Levels of RAP- Gold - -* Code is fully packaged -* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions -* Process runs based on event-based triggers (e.g., new data in database) or on a schedule -* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) - -:::{.footer} -[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) -::: - -## Data science in healthcare - -* Forecasting - * Stats versus ML -* Text mining - * R versus Python -* Demand modelling - * DevOps as a way of life - -## Get involved! - -* [NHS-R community](https://nhsrcommunity.com/) - * Webinars, training, conference, Slack -* [NHS Pycom](https://nhs-pycom.net/) - * ditto... -* MLCSU GitHub? -* Build links with the other CSUs - -## Contact - -::: {.columns} - -:::: {.column} -::: {.no-bullets} -- {{< fa envelope >}} [strategy.unit@nhs.net](mailto:strategy.unit@nhs.net) -- {{< fa brands github size=1x >}} [The-Strategy-Unit](https://github.com/The-Strategy-Unit) -::: -:::: - -:::: {.column} -::: {.no-bullets} -- {{< fa envelope >}} [chris.beeley1@nhs.net](mailto:chris.beeley1@nhs.net) -- {{< fa brands github size=1x >}} [chrisbeeley](https://github.com/ChrisBeeley) -::: -:::: - -::: - - - +--- +title: Travels with R and Python +subtitle: the power of data science in healthcare +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2023-08-02 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-08-02_mlcsu-ksn-meeting + +## What is data science? + +* "A data scientist knows more about computer science than the average statistician, and more about statistics than the average computer scientist" + +[(Josh Wills, a former head of data engineering at Slack)](https://medium.com/odscjournal/data-scientists-versus-statisticians-8ea146b7a47f) + +## Drew Conway's famous Venn diagram + +![](Data_Science_VD.png){fig-alt="Data science Venn diagram" fig-align="center"} + +[Source](http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram) + +## What are the skills of data science? + +* Analysis + * ML + * Stats + * Data viz +* Software engineering + * Programming + * SQL/ data + * DevOps + * RAP + +## What are the skills of data science? + +* Domain knowledge + * Communication + * Problem formulation + * Dashboards and reports + +## Stats and data viz + +* ML leans a bit more towards atheoretical prediction +* Stats leans a bit more towards inference (but they both do both) +* Data scientists may use different visualisations + * Interactive web based tools + * Dashboard based visualisers e.g. [{stminsights}](https://github.com/cschwem2er/stminsights) + +## Software engineering + +* Programming + * No/ low code data science? +* SQL/ data + * Tend to use reproducible automated processes +* DevOps + * Plan, code, build, test, release, deploy, operate, monitor +* RAP + * I will come back to this + +## Domain knowledge + +* Do stuff that matters + * The best minds of my generation are thinking about how to make people click ads. That sucks. [Jeffrey Hammerbacher](https://www.fastcompany.com/3008436/why-data-god-jeffrey-hammerbacher-left-facebook-found-cloudera) +* Convince other people that it matters +* This is the hardest part of data science + +## RAP + +* Data science isn't RAP +* RAP isn't data science +* They are firm friends + +## Reproducibility + +* Reproducibility in science +* The [$6B spreadsheet error](https://baselinescenario.com/2013/02/09/the-importance-of-excel/) +* [George Osbourne's austerity was based on a spreadsheet error](https://www.theguardian.com/politics/2013/apr/18/uncovered-error-george-osborne-austerity) +* For us, reproducibility also means we can do the same analysis 50 times in one minute + * Which is why I started down the road of data science + +## What is RAP + +* a process in which code is used to minimise manual, undocumented steps, and a clear, properly documented process is produced in code which can reliably give the same result from the same dataset +* RAP should be: + +> the core working practice that must be supported by all platforms and teams; make this a core focus of NHS analyst training + +:::{.footer} +[Goldacre review](https://www.gov.uk/government/publications/better-broader-safer-using-health-data-for-research-and-analysis) +::: + +## Levels of RAP- Baseline + +* Data produced by code in an open-source language (e.g., Python, R, SQL) +* Code is version controlled +* Repository includes a README.md file that clearly details steps a user must follow to reproduce the code +* Code has been peer reviewed +* Code is published in the open and linked to & from accompanying publication (if relevant) + +:::{.footer} +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) +::: + +## Levels of RAP- Silver + +* Code is well-documented... +* Code is well-organised following standard directory format +* Reusable functions and/or classes are used where appropriate +* Pipeline includes a testing framework +* Repository includes dependency information (e.g. requirements.txt, PipFile, environment.yml) +* Data is handled and output in a [Tidy data](https://vita.had.co.nz/papers/tidy-data.pdf) format + +:::{.footer} +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) +::: + +## Levels of RAP- Gold + +* Code is fully packaged +* Repository automatically runs tests etc. via CI/CD or a different integration/deployment tool e.g. GitHub Actions +* Process runs based on event-based triggers (e.g., new data in database) or on a schedule +* Changes to the RAP are clearly signposted. E.g. a changelog in the package, releases etc. (See gov.uk info on Semantic Versioning) + +:::{.footer} +[Source: NHS Digital RAP community of practice](https://nhsdigital.github.io/rap-community-of-practice/introduction_to_RAP/levels_of_RAP/) +::: + +## Data science in healthcare + +* Forecasting + * Stats versus ML +* Text mining + * R versus Python +* Demand modelling + * DevOps as a way of life + +## Get involved! + +* [NHS-R community](https://nhsrcommunity.com/) + * Webinars, training, conference, Slack +* [NHS Pycom](https://nhs-pycom.net/) + * ditto... +* MLCSU GitHub? +* Build links with the other CSUs + +## Contact + +::: {.columns} + +:::: {.column} +::: {.no-bullets} +- {{< fa envelope >}} [strategy.unit@nhs.net](mailto:strategy.unit@nhs.net) +- {{< fa brands github size=1x >}} [The-Strategy-Unit](https://github.com/The-Strategy-Unit) +::: +:::: + +:::: {.column} +::: {.no-bullets} +- {{< fa envelope >}} [chris.beeley1@nhs.net](mailto:chris.beeley1@nhs.net) +- {{< fa brands github size=1x >}} [chrisbeeley](https://github.com/ChrisBeeley) +::: +:::: + +::: + + + diff --git a/presentations/2023-08-02_mlcsu-ksn-meeting/renv.lock b/presentations/2023-08-02_mlcsu-ksn-meeting/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-08-02_mlcsu-ksn-meeting/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-08-23_nhs-r_unit-testing/index.qmd b/presentations/2023-08-23_nhs-r_unit-testing/index.qmd index 558473e..ebc3f60 100644 --- a/presentations/2023-08-23_nhs-r_unit-testing/index.qmd +++ b/presentations/2023-08-23_nhs-r_unit-testing/index.qmd @@ -1,1036 +1,1042 @@ ---- -title: Unit testing in R -subtitle: NHS-R Community Webinar -author: "[Tom Jemmett](tom.jemmett@gmail.com)" -date: 2023-08-23 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true -execute: - echo: true - error: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-08-23_nhs-r_unit-testing - - -## What is testing? - -> Software testing is the act of examining the artifacts and the behavior of the software under test by validation and verification. Software testing can also provide an objective, independent view of the software to allow the business to appreciate and understand the risks of software implementation -> -> [wikipedia](https://en.wikipedia.org/wiki/Software_testing) - -## How can we test our code? - -:::{.columns} - -::::{.column width=45%} -#### Statically - -::: {.incremental} -* (without executing the code) -* happens constantly, as we are writing code -* via code reviews -* compilers/interpreters/linters statically analyse the code for syntax errors -::: - -:::: - -::::{.column width=10%} - -:::: - -::::{.column width=45%} -#### Dynamically - -:::: - -::: - -## How can we test our code? - -:::{.columns} - -::::{.column width=45% .very-light-charcoal} -#### Statically - -* (without executing the code) -* happens constantly, as we are writing code -* via code reviews -* compilers/interpreters/linters statically analyse the code for syntax errors - -:::: - -::::{.column width=10%} - -:::: - -::::{.column width=45%} -#### Dynamically - -::: {.incremental} -* (by executing the code) -* split into functional and non-functional testing -* testing can be manual, or automated -::: - -:::: - -::: - -:::{.notes} -* non-functional testing covers things like performance, security, and usability testing -::: - -## Different types of functional tests - -[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. - -. . . - -[**Integration Testing**]{.yellow} integrates units to ensure that the code works together. - -. . . - -[**End-to-End Testing**]{.yellow} (e2e) makes sure that the entire system functions correctly. - -. . . - -[**User Acceptance Testing**]{.yellow} (UAT) ensures that the product meets the real user's requirements. - -## Different types of functional tests - -[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. - -[**Integration Testing**]{.yellow} integrates units to ensure that the code works together. - -[**End-to-End Testing**]{.yellow} (e2e) makes sure that the entire system functions correctly. - -:::{.very-light-charcoal} -**User Acceptance Testing** (UAT) ensures that the product meets the real user's requirements. -::: - -:::{.notes} -Unit, Integration, and E2E testing are all things we can automate in code, whereas UAT testing is going to be manual -::: - -## Different types of functional tests - -[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. - -:::{.very-light-charcoal} -**Integration Testing** integrates units to ensure that the code works together. - -**End-to-End Testing** (e2e) makes sure that the entire system functions correctly. - -**User Acceptance Testing** (UAT) ensures that the product meets the real user's requirements. -::: - -:::{.notes} -Only focussing on unit testing in this talk, but the techniques/packages could be extended to integration testing. Often other tools (potentially specific tools) are needed for E2E testing. -::: - -## Example - -We have a `{shiny}` app which grabs some data from a database, manipulates the data, and generates a plot. - -:::{.incremental} -- we would write **unit tests** to check the data manipulation and plot functions work correctly (with pre-created sample/simple datasets) -- we would write **integration tests** to check that the data manipulation function works with the plot function (with similar data to what we used for the **unit tests**) -- we would write **e2e** tests to ensure that from start to finish the app grabs the data and produces a plot as required -::: - -:::{.notes} -simple (unit tests) to complex (e2e tests) -::: - -## Testing Pyramid - -![](https://global-uploads.webflow.com/619e15d781b21202de206fb5/6316d9f844c1d214873c6c9b_the-software-testing-pyramid.webp) - -:::{.footer} -[Image source:]{.light-charcoal} The Testing Pyramid: Simplified for One and All [headspin.io][testing_pyramid] - -[testing_pyramid]: https://www.headspin.io/blog/the-testing-pyramid-simplified-for-one-and-all -::: - -# Unit [Testing]{.yellow} {.inverse .center} - -. . . - -``` r -install.packages("testthat") - -library(testthat) -``` - -```{r} -#| include: FALSE -library(testthat) -library(dplyr) -``` - -## Let's create a simple function... - -```{r} -my_function <- function(x, y) { - - stopifnot( - "x must be numeric" = is.numeric(x), - "y must be numeric" = is.numeric(y), - "x must be same length as y" = length(x) == length(y), - "cannot divide by zero!" = y != 0 - ) - - x / y -} -``` - -## Let's create a simple function... - -```{r} -#| eval: FALSE -#| code-line-numbers: "1,10,11" -my_function <- function(x, y) { - - stopifnot( - "x must be numeric" = is.numeric(x), - "y must be numeric" = is.numeric(y), - "x must be same length as y" = length(x) == length(y), - "cannot divide by zero!" = y != 0 - ) - - x / y -} -``` - -## Let's create a simple function... - -```{r} -#| eval: FALSE -#| code-line-numbers: "3-8" -my_function <- function(x, y) { - - stopifnot( - "x must be numeric" = is.numeric(x), - "y must be numeric" = is.numeric(y), - "x must be same length as y" = length(x) == length(y), - "cannot divide by zero!" = y != 0 - ) - - x / y -} -``` - -:::{.footer} -[The Ten Rules of Defensive Programming in R][dpinr] - -[dpinr]: https://www.r-bloggers.com/2018/07/the-ten-rules-of-defensive-programming-in-r/ -::: - -## ... and create our first test - -```{r} -#| eval: FALSE -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - -## ... and create our first test - -```{r} -#| eval: FALSE -#| code-line-numbers: "1,14" -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - -## ... and create our first test - -```{r} -#| eval: FALSE -#| code-line-numbers: "3,7,11" -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - - -## ... and create our first test - -```{r} -#| eval: FALSE -#| code-line-numbers: "4,8,12" -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - -## ... and create our first test - -```{r} -#| eval: FALSE -#| code-line-numbers: "2,5,6,9,10,13" -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - - -## ... and create our first test - -```{r} -test_that("my_function correctly divides values", { - expect_equal( - my_function(4, 2), - 2 - ) - expect_equal( - my_function(1, 4), - 0.25 - ) - expect_equal( - my_function(c(4, 1), c(2, 4)), - c(2, 0.25) - ) -}) -``` - -## other `expect_*()` functions... - -```{r} -test_that("my_function correctly divides values", { - expect_lt( - my_function(4, 2), - 10 - ) - expect_gt( - my_function(1, 4), - 0.2 - ) - expect_length( - my_function(c(4, 1), c(2, 4)), - 2 - ) -}) -``` - -:::{.footer} -[{testthat} documentation][ttd] - -[ttd]: https://testthat.r-lib.org/reference/index.html -::: - -## Arrange, Act, Assert - -:::{.columns} - -::::{.column width=50%} -:::: - -::::{.column width=50%} - -```{r} -#| eval: false -test_that("my_function works", { - # arrange - # - # - # - - # act - # - - # assert - # -}) -``` -:::: - -::: - -## Arrange, Act, Assert - -:::{.columns} - -::::{.column width=50%} -we [**arrange**]{.yellow} the environment, before running the function - -:::{.incremental} -- to create sample values -- create fake/temporary files -- set random seed -- set R options/environment variables -::: - -:::: - -::::{.column width=50%} - -```{r} -#| eval: false -#| code-line-numbers: "2-5" -test_that("my_function works", { - # arrange - x <- 5 - y <- 7 - expected <- 0.714285 - - # act - # - - # assert - # -}) -``` - -:::: -::: - -## Arrange, Act, Assert - -:::{.columns} - -::::{.column width=50%} -[we **arrange** the environment, before running the function]{.very-light-charcoal} - -we [**act**]{.yellow} by calling the function - -:::: - -::::{.column width=50%} - -```{r} -#| eval: false -#| code-line-numbers: "7-8" -test_that("my_function works", { - # arrange - x <- 5 - y <- 7 - expected <- 0.714285 - - # act - actual <- my_function(x, y) - - # assert - # -}) -``` - -:::: -::: - - -## Arrange, Act, Assert - -:::{.columns} - -::::{.column width=50%} -[we **arrange** the environment, before running the function]{.very-light-charcoal} - -[we **act** by calling the function]{.very-light-charcoal} - -we [**assert**]{.yellow} that the **actual** results match our **expected** results -:::: - -::::{.column width=50%} - -```{r} -#| eval: false -#| code-line-numbers: "10-11" -test_that("my_function works", { - # arrange - x <- 5 - y <- 7 - expected <- 0.714285 - - # act - actual <- my_function(x, y) - - # assert - expect_equal(actual, expected) -}) -``` - -:::: -::: - -## Our test failed!?! 😢 - -```{r} -test_that("my_function works", { - # arrange - x <- 5 - y <- 7 - expected <- 0.714285 - - # act - actual <- my_function(x, y) - - # assert - expect_equal(actual, expected) -}) -``` - -## Tolerance to the rescue 🙂 - -```{r} -test_that("my_function works", { - # arrange - x <- 5 - y <- 7 - expected <- 0.714285 - - # act - actual <- my_function(x, y) - - # assert - expect_equal(actual, expected, tolerance = 1e-6) -}) -``` - -. . . - -[(*this is a slightly artificial example, usually the default tolerance is good enough*)]{.small} - -## Testing edge cases - -:::{.columns} - -::::{.column width=40%} -Remember the validation steps we built into our function to handle edge cases? - -
- -Let's write tests for these edge cases: - -[we expect errors]{.yellow} -:::: - -::::{.column width=60%} -```{r} -test_that("my_function works", { - expect_error(my_function(5, 0)) - expect_error(my_function("a", 3)) - expect_error(my_function(3, "a")) - expect_error(my_function(1:2, 4)) -}) -``` -:::: - -::: - -## Another (simple) example - -:::{.columns} - -::::{.column width=60%} -```{r} -my_new_function <- function(x, y) { - if (x > y) { - "x" - } else { - "y" - } -} -``` -:::: - -::::{.column width=40%} - -Consider this function - there is [branched logic]{.yellow}, so we need to carefully design tests to validate the logic works as intended. - -:::: -::: - -## Another (simple) example - -```{r} -my_new_function <- function(x, y) { - if (x > y) { - "x" - } else { - "y" - } -} -``` - -
- -```{r} -test_that("it returns 'x' if x is bigger than y", { - expect_equal(my_new_function(4, 3), "x") -}) -test_that("it returns 'y' if y is bigger than x", { - expect_equal(my_new_function(3, 4), "y") - expect_equal(my_new_function(3, 3), "y") -}) -``` - -## How to design good tests - -[*a non-exhaustive list*]{.small} - -- consider all the functions arguments, -- what are the [expected]{.yellow} values for these arguments? -- what are [unexpected]{.yellow} values, and are they handled? -- are there [edge cases]{.yellow} that need to be handled? -- have you covered all of the [different paths]{.yellow} in your code? -- have you managed to create tests that check the [range of results]{.yellow} you expect? - -## But, why create tests? - -[*another non-exhaustive list*]{.small} - -- good tests will help you uncover existing issues in your code -- will defend you from future changes that break existing functionality -- will alert you to changes in dependencies that may have changed the functionality of your code -- can act as documentation for other developers - -## Testing complex functions - -:::{.columns} - -::::{.column width=70%} -```{r} -#| eval: FALSE -my_big_function <- function(type) { - con <- dbConnect(RSQLite::SQLite(), "data.db") - df <- tbl(con, "data_table") |> - collect() |> - mutate(across(date, lubridate::ymd)) - - conditions <- read_csv( - "conditions.csv", col_types = "cc" - ) |> - filter(condition_type == type) - - df |> - semi_join(conditions, by = "condition") |> - count(date) |> - ggplot(aes(date, n)) + - geom_line() + - geom_point() -} -``` -:::: - -::::{.column width=30%} -Where do you even begin to start writing tests for something so complex? - -
- -:::{.small} -*Note: to get the code on the left to fit on one page, I skipped including a few library calls* - -```{r} -#| eval: FALSE -library(tidyverse) -library(DBI) -``` -::: - -:::: - -::: - -## Split the logic into smaller functions - -Function to get the data from the database - -```{r} -#| eval: FALSE -get_data_from_sql <- function() { - con <- dbConnect(RSQLite::SQLite(), "data.db") - tbl(con, "data_table") |> - collect() |> - mutate(across(date, lubridate::ymd)) -} -``` - -## Split the logic into smaller functions - -Function to get the relevant conditions - -```{r} -#| eval: FALSE -get_conditions <- function(type) { - read_csv( - "conditions.csv", col_types = "cc" - ) |> - filter(condition_type == type) -} -``` - -## Split the logic into smaller functions - -Function to combine the data and create a count by date - -```{r} -summarise_data <- function(df, conditions) { - df |> - semi_join(conditions, by = "condition") |> - count(date) -} -``` - -## Split the logic into smaller functions - -Function to generate a plot from the summarised data - -```{r} -create_plot <- function(df) { - df |> - ggplot(aes(date, n)) + - geom_line() + - geom_point() -} -``` - - -## Split the logic into smaller functions - -The original function refactored to use the new functions - -```{r} -my_big_function <- function(type) { - conditions <- get_conditions(type) - - get_data_from_sql() |> - summarise_data(conditions) |> - create_plot() -} -``` - -. . . - -This is going to be significantly easier to test, because we now can verify that the individual components work correctly, rather than having to consider all of the possibilities at once. - -## Let's test `summarise_data` - -``` r -summarise_data <- function(df, conditions) { - df |> - semi_join(conditions, by = "condition") |> - count(date) -} -``` - -## Let's test `summarise_data` - -``` r -test_that("it summarises the data", { - # arrange - - - - - - - - - - - # act - - # assert - -}) -``` - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} -``` r -test_that("it summarises the data", { - # arrange - - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - - - - - - # act - - # assert - -}) -``` - -:::: - -::::{.column width=25% .small} -Generate some random data to build a reasonably sized data frame. - -You could also create a table manually, but part of the trick of writing good tests for this function is to make it so the dates don't all have the same count. - -The reason for this is it's harder to know for sure that the count worked if every row returns the same value. - -We don't need the values to be exactly like they are in the real data, just close enough. Instead of dates, we can use numbers, and instead of actual conditions, we can use letters. -:::: - -::: - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} - -``` r -test_that("it summarises the data", { - # arrange - set.seed(123) - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - - - - - - # act - - # assert - -}) -``` -:::: - -::::{.column width=25% .small} -Tests need to be reproducible, and generating our table at random will give us unpredictable results. - -So, we need to set the random seed; now every time this test runs we will generate the same data. -:::: - -::: - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} -``` r -test_that("it summarises the data", { - # arrange - set.seed(123) - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - conditions <- tibble(condition = c("a", "b")) - - - - - # act - - # assert - -}) -``` -:::: - -::::{.column width=25% .small} -Create the conditions table. We don't need all of the columns that are present in the real csv, just the ones that will make our code work. - -We also need to test that the filtering join (`semi_join`) is working, so we want to use a subset of the conditions that were used in `df`. -:::: - -::: - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} -``` r -test_that("it summarises the data", { - # arrange - set.seed(123) - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - conditions <- tibble(condition = c("a", "b")) - - - - - # act - actual <- summarise_data(df, conditions) - # assert - -}) -``` -:::: - -::::{.column width=25% .small} -Because we are generating `df` randomly, to figure out what our "expected" results are, I simply ran the code inside of the test to generate the "actual" results. - -Generally, this isn't a good idea. You are creating the results of your test from the code; ideally, you want to be thinking about what the results of your function should be. - -Imagine your function doesn't work as intended, there is some subtle bug that you are not yet aware of. By writing tests "backwards" you may write test cases that confirm the results, but not expose the bug. This is why it's good to think about edge cases. -:::: - -::: - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} -``` r -test_that("it summarises the data", { - # arrange - set.seed(123) - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - conditions <- tibble(condition = c("a", "b")) - expected <- tibble( - date = 1:10, - n = c(19, 18, 12, 14, 17, 18, 24, 18, 31, 21) - ) - # act - actual <- summarise_data(df, conditions) - # assert - -}) -``` -:::: - -::::{.column width=25% .small} -That said, in cases where we can be confident (say by static analysis of our code) that it is correct, building tests in this way will give us the confidence going forwards that future changes do not break existing functionality. - -In this case, I have created the expected data frame using the results from running the function. -:::: - -::: - -## Let's test `summarise_data` - -:::{.columns} - -::::{.column width=75%} -```{r} -test_that("it summarises the data", { - # arrange - set.seed(123) - df <- tibble( - date = sample(1:10, 300, TRUE), - condition = sample(c("a", "b", "c"), 300, TRUE) - ) - conditions <- tibble(condition = c("a", "b")) - expected <- tibble( - date = 1:10, - n = c(19, 18, 12, 14, 17, 18, 24, 18, 31, 21) - ) - # act - actual <- summarise_data(df, conditions) - # assert - expect_equal(actual, expected) -}) -``` -:::: - -::::{.column width=25% .small} -The test works! -:::: - -::: - -## Next steps - -- You can add tests to any R project (to test functions), -- But `{testthat}` works best with Packages -- The R Packages book has 3 chapters on [testing](https://r-pkgs.org/testing-basics.html) -- There are two useful helper functions in `{usethis}` - * `use_testthat()` will set up the folders for test scripts - * `use_test()` will create a test file for the currently open script - -## Next steps - -- If your test needs to temporarily create a file, or change some R-options, the [`{withr}` package](https://withr.r-lib.org/) has a lot of useful functions that will automatically clean things up when the test finishes -- If you are writing tests that involve calling out to a database, or you want to test `my_big_function` (from before) without calling the intermediate functions, then you should look at the [`{mockery}` package](https://github.com/r-lib/mockery) - -# Thanks! {.inverse} - -Questions? - - +--- +title: Unit testing in R +subtitle: NHS-R Community Webinar +author: "[Tom Jemmett](tom.jemmett@gmail.com)" +date: 2023-08-23 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true +execute: + echo: true + error: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-08-23_nhs-r_unit-testing + + +## What is testing? + +> Software testing is the act of examining the artifacts and the behavior of the software under test by validation and verification. Software testing can also provide an objective, independent view of the software to allow the business to appreciate and understand the risks of software implementation +> +> [wikipedia](https://en.wikipedia.org/wiki/Software_testing) + +## How can we test our code? + +:::{.columns} + +::::{.column width=45%} +#### Statically + +::: {.incremental} +* (without executing the code) +* happens constantly, as we are writing code +* via code reviews +* compilers/interpreters/linters statically analyse the code for syntax errors +::: + +:::: + +::::{.column width=10%} + +:::: + +::::{.column width=45%} +#### Dynamically + +:::: + +::: + +## How can we test our code? + +:::{.columns} + +::::{.column width=45% .very-light-charcoal} +#### Statically + +* (without executing the code) +* happens constantly, as we are writing code +* via code reviews +* compilers/interpreters/linters statically analyse the code for syntax errors + +:::: + +::::{.column width=10%} + +:::: + +::::{.column width=45%} +#### Dynamically + +::: {.incremental} +* (by executing the code) +* split into functional and non-functional testing +* testing can be manual, or automated +::: + +:::: + +::: + +:::{.notes} +* non-functional testing covers things like performance, security, and usability testing +::: + +## Different types of functional tests + +[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. + +. . . + +[**Integration Testing**]{.yellow} integrates units to ensure that the code works together. + +. . . + +[**End-to-End Testing**]{.yellow} (e2e) makes sure that the entire system functions correctly. + +. . . + +[**User Acceptance Testing**]{.yellow} (UAT) ensures that the product meets the real user's requirements. + +## Different types of functional tests + +[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. + +[**Integration Testing**]{.yellow} integrates units to ensure that the code works together. + +[**End-to-End Testing**]{.yellow} (e2e) makes sure that the entire system functions correctly. + +:::{.very-light-charcoal} +**User Acceptance Testing** (UAT) ensures that the product meets the real user's requirements. +::: + +:::{.notes} +Unit, Integration, and E2E testing are all things we can automate in code, whereas UAT testing is going to be manual +::: + +## Different types of functional tests + +[**Unit Testing**]{.yellow} checks each component (or unit) for accuracy independently of one another. + +:::{.very-light-charcoal} +**Integration Testing** integrates units to ensure that the code works together. + +**End-to-End Testing** (e2e) makes sure that the entire system functions correctly. + +**User Acceptance Testing** (UAT) ensures that the product meets the real user's requirements. +::: + +:::{.notes} +Only focussing on unit testing in this talk, but the techniques/packages could be extended to integration testing. Often other tools (potentially specific tools) are needed for E2E testing. +::: + +## Example + +We have a `{shiny}` app which grabs some data from a database, manipulates the data, and generates a plot. + +:::{.incremental} +- we would write **unit tests** to check the data manipulation and plot functions work correctly (with pre-created sample/simple datasets) +- we would write **integration tests** to check that the data manipulation function works with the plot function (with similar data to what we used for the **unit tests**) +- we would write **e2e** tests to ensure that from start to finish the app grabs the data and produces a plot as required +::: + +:::{.notes} +simple (unit tests) to complex (e2e tests) +::: + +## Testing Pyramid + +![](https://global-uploads.webflow.com/619e15d781b21202de206fb5/6316d9f844c1d214873c6c9b_the-software-testing-pyramid.webp) + +:::{.footer} +[Image source:]{.light-charcoal} The Testing Pyramid: Simplified for One and All [headspin.io][testing_pyramid] + +[testing_pyramid]: https://www.headspin.io/blog/the-testing-pyramid-simplified-for-one-and-all +::: + +# Unit [Testing]{.yellow} {.inverse .center} + +. . . + +``` r +install.packages("testthat") + +library(testthat) +``` + +```{r} +#| include: FALSE +library(testthat) +library(dplyr) +``` + +## Let's create a simple function... + +```{r} +my_function <- function(x, y) { + + stopifnot( + "x must be numeric" = is.numeric(x), + "y must be numeric" = is.numeric(y), + "x must be same length as y" = length(x) == length(y), + "cannot divide by zero!" = y != 0 + ) + + x / y +} +``` + +## Let's create a simple function... + +```{r} +#| eval: FALSE +#| code-line-numbers: "1,10,11" +my_function <- function(x, y) { + + stopifnot( + "x must be numeric" = is.numeric(x), + "y must be numeric" = is.numeric(y), + "x must be same length as y" = length(x) == length(y), + "cannot divide by zero!" = y != 0 + ) + + x / y +} +``` + +## Let's create a simple function... + +```{r} +#| eval: FALSE +#| code-line-numbers: "3-8" +my_function <- function(x, y) { + + stopifnot( + "x must be numeric" = is.numeric(x), + "y must be numeric" = is.numeric(y), + "x must be same length as y" = length(x) == length(y), + "cannot divide by zero!" = y != 0 + ) + + x / y +} +``` + +:::{.footer} +[The Ten Rules of Defensive Programming in R][dpinr] + +[dpinr]: https://www.r-bloggers.com/2018/07/the-ten-rules-of-defensive-programming-in-r/ +::: + +## ... and create our first test + +```{r} +#| eval: FALSE +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + +## ... and create our first test + +```{r} +#| eval: FALSE +#| code-line-numbers: "1,14" +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + +## ... and create our first test + +```{r} +#| eval: FALSE +#| code-line-numbers: "3,7,11" +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + + +## ... and create our first test + +```{r} +#| eval: FALSE +#| code-line-numbers: "4,8,12" +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + +## ... and create our first test + +```{r} +#| eval: FALSE +#| code-line-numbers: "2,5,6,9,10,13" +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + + +## ... and create our first test + +```{r} +test_that("my_function correctly divides values", { + expect_equal( + my_function(4, 2), + 2 + ) + expect_equal( + my_function(1, 4), + 0.25 + ) + expect_equal( + my_function(c(4, 1), c(2, 4)), + c(2, 0.25) + ) +}) +``` + +## other `expect_*()` functions... + +```{r} +test_that("my_function correctly divides values", { + expect_lt( + my_function(4, 2), + 10 + ) + expect_gt( + my_function(1, 4), + 0.2 + ) + expect_length( + my_function(c(4, 1), c(2, 4)), + 2 + ) +}) +``` + +:::{.footer} +[{testthat} documentation][ttd] + +[ttd]: https://testthat.r-lib.org/reference/index.html +::: + +## Arrange, Act, Assert + +:::{.columns} + +::::{.column width=50%} +:::: + +::::{.column width=50%} + +```{r} +#| eval: false +test_that("my_function works", { + # arrange + # + # + # + + # act + # + + # assert + # +}) +``` +:::: + +::: + +## Arrange, Act, Assert + +:::{.columns} + +::::{.column width=50%} +we [**arrange**]{.yellow} the environment, before running the function + +:::{.incremental} +- to create sample values +- create fake/temporary files +- set random seed +- set R options/environment variables +::: + +:::: + +::::{.column width=50%} + +```{r} +#| eval: false +#| code-line-numbers: "2-5" +test_that("my_function works", { + # arrange + x <- 5 + y <- 7 + expected <- 0.714285 + + # act + # + + # assert + # +}) +``` + +:::: +::: + +## Arrange, Act, Assert + +:::{.columns} + +::::{.column width=50%} +[we **arrange** the environment, before running the function]{.very-light-charcoal} + +we [**act**]{.yellow} by calling the function + +:::: + +::::{.column width=50%} + +```{r} +#| eval: false +#| code-line-numbers: "7-8" +test_that("my_function works", { + # arrange + x <- 5 + y <- 7 + expected <- 0.714285 + + # act + actual <- my_function(x, y) + + # assert + # +}) +``` + +:::: +::: + + +## Arrange, Act, Assert + +:::{.columns} + +::::{.column width=50%} +[we **arrange** the environment, before running the function]{.very-light-charcoal} + +[we **act** by calling the function]{.very-light-charcoal} + +we [**assert**]{.yellow} that the **actual** results match our **expected** results +:::: + +::::{.column width=50%} + +```{r} +#| eval: false +#| code-line-numbers: "10-11" +test_that("my_function works", { + # arrange + x <- 5 + y <- 7 + expected <- 0.714285 + + # act + actual <- my_function(x, y) + + # assert + expect_equal(actual, expected) +}) +``` + +:::: +::: + +## Our test failed!?! 😢 + +```{r} +test_that("my_function works", { + # arrange + x <- 5 + y <- 7 + expected <- 0.714285 + + # act + actual <- my_function(x, y) + + # assert + expect_equal(actual, expected) +}) +``` + +## Tolerance to the rescue 🙂 + +```{r} +test_that("my_function works", { + # arrange + x <- 5 + y <- 7 + expected <- 0.714285 + + # act + actual <- my_function(x, y) + + # assert + expect_equal(actual, expected, tolerance = 1e-6) +}) +``` + +. . . + +[(*this is a slightly artificial example, usually the default tolerance is good enough*)]{.small} + +## Testing edge cases + +:::{.columns} + +::::{.column width=40%} +Remember the validation steps we built into our function to handle edge cases? + +
+ +Let's write tests for these edge cases: + +[we expect errors]{.yellow} +:::: + +::::{.column width=60%} +```{r} +test_that("my_function works", { + expect_error(my_function(5, 0)) + expect_error(my_function("a", 3)) + expect_error(my_function(3, "a")) + expect_error(my_function(1:2, 4)) +}) +``` +:::: + +::: + +## Another (simple) example + +:::{.columns} + +::::{.column width=60%} +```{r} +my_new_function <- function(x, y) { + if (x > y) { + "x" + } else { + "y" + } +} +``` +:::: + +::::{.column width=40%} + +Consider this function - there is [branched logic]{.yellow}, so we need to carefully design tests to validate the logic works as intended. + +:::: +::: + +## Another (simple) example + +```{r} +my_new_function <- function(x, y) { + if (x > y) { + "x" + } else { + "y" + } +} +``` + +
+ +```{r} +test_that("it returns 'x' if x is bigger than y", { + expect_equal(my_new_function(4, 3), "x") +}) +test_that("it returns 'y' if y is bigger than x", { + expect_equal(my_new_function(3, 4), "y") + expect_equal(my_new_function(3, 3), "y") +}) +``` + +## How to design good tests + +[*a non-exhaustive list*]{.small} + +- consider all the functions arguments, +- what are the [expected]{.yellow} values for these arguments? +- what are [unexpected]{.yellow} values, and are they handled? +- are there [edge cases]{.yellow} that need to be handled? +- have you covered all of the [different paths]{.yellow} in your code? +- have you managed to create tests that check the [range of results]{.yellow} you expect? + +## But, why create tests? + +[*another non-exhaustive list*]{.small} + +- good tests will help you uncover existing issues in your code +- will defend you from future changes that break existing functionality +- will alert you to changes in dependencies that may have changed the functionality of your code +- can act as documentation for other developers + +## Testing complex functions + +:::{.columns} + +::::{.column width=70%} +```{r} +#| eval: FALSE +my_big_function <- function(type) { + con <- dbConnect(RSQLite::SQLite(), "data.db") + df <- tbl(con, "data_table") |> + collect() |> + mutate(across(date, lubridate::ymd)) + + conditions <- read_csv( + "conditions.csv", col_types = "cc" + ) |> + filter(condition_type == type) + + df |> + semi_join(conditions, by = "condition") |> + count(date) |> + ggplot(aes(date, n)) + + geom_line() + + geom_point() +} +``` +:::: + +::::{.column width=30%} +Where do you even begin to start writing tests for something so complex? + +
+ +:::{.small} +*Note: to get the code on the left to fit on one page, I skipped including a few library calls* + +```{r} +#| eval: FALSE +library(tidyverse) +library(DBI) +``` +::: + +:::: + +::: + +## Split the logic into smaller functions + +Function to get the data from the database + +```{r} +#| eval: FALSE +get_data_from_sql <- function() { + con <- dbConnect(RSQLite::SQLite(), "data.db") + tbl(con, "data_table") |> + collect() |> + mutate(across(date, lubridate::ymd)) +} +``` + +## Split the logic into smaller functions + +Function to get the relevant conditions + +```{r} +#| eval: FALSE +get_conditions <- function(type) { + read_csv( + "conditions.csv", col_types = "cc" + ) |> + filter(condition_type == type) +} +``` + +## Split the logic into smaller functions + +Function to combine the data and create a count by date + +```{r} +summarise_data <- function(df, conditions) { + df |> + semi_join(conditions, by = "condition") |> + count(date) +} +``` + +## Split the logic into smaller functions + +Function to generate a plot from the summarised data + +```{r} +create_plot <- function(df) { + df |> + ggplot(aes(date, n)) + + geom_line() + + geom_point() +} +``` + + +## Split the logic into smaller functions + +The original function refactored to use the new functions + +```{r} +my_big_function <- function(type) { + conditions <- get_conditions(type) + + get_data_from_sql() |> + summarise_data(conditions) |> + create_plot() +} +``` + +. . . + +This is going to be significantly easier to test, because we now can verify that the individual components work correctly, rather than having to consider all of the possibilities at once. + +## Let's test `summarise_data` + +``` r +summarise_data <- function(df, conditions) { + df |> + semi_join(conditions, by = "condition") |> + count(date) +} +``` + +## Let's test `summarise_data` + +``` r +test_that("it summarises the data", { + # arrange + + + + + + + + + + + # act + + # assert + +}) +``` + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} +``` r +test_that("it summarises the data", { + # arrange + + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + + + + + + # act + + # assert + +}) +``` + +:::: + +::::{.column width=25% .small} +Generate some random data to build a reasonably sized data frame. + +You could also create a table manually, but part of the trick of writing good tests for this function is to make it so the dates don't all have the same count. + +The reason for this is it's harder to know for sure that the count worked if every row returns the same value. + +We don't need the values to be exactly like they are in the real data, just close enough. Instead of dates, we can use numbers, and instead of actual conditions, we can use letters. +:::: + +::: + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} + +``` r +test_that("it summarises the data", { + # arrange + set.seed(123) + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + + + + + + # act + + # assert + +}) +``` +:::: + +::::{.column width=25% .small} +Tests need to be reproducible, and generating our table at random will give us unpredictable results. + +So, we need to set the random seed; now every time this test runs we will generate the same data. +:::: + +::: + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} +``` r +test_that("it summarises the data", { + # arrange + set.seed(123) + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + conditions <- tibble(condition = c("a", "b")) + + + + + # act + + # assert + +}) +``` +:::: + +::::{.column width=25% .small} +Create the conditions table. We don't need all of the columns that are present in the real csv, just the ones that will make our code work. + +We also need to test that the filtering join (`semi_join`) is working, so we want to use a subset of the conditions that were used in `df`. +:::: + +::: + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} +``` r +test_that("it summarises the data", { + # arrange + set.seed(123) + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + conditions <- tibble(condition = c("a", "b")) + + + + + # act + actual <- summarise_data(df, conditions) + # assert + +}) +``` +:::: + +::::{.column width=25% .small} +Because we are generating `df` randomly, to figure out what our "expected" results are, I simply ran the code inside of the test to generate the "actual" results. + +Generally, this isn't a good idea. You are creating the results of your test from the code; ideally, you want to be thinking about what the results of your function should be. + +Imagine your function doesn't work as intended, there is some subtle bug that you are not yet aware of. By writing tests "backwards" you may write test cases that confirm the results, but not expose the bug. This is why it's good to think about edge cases. +:::: + +::: + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} +``` r +test_that("it summarises the data", { + # arrange + set.seed(123) + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + conditions <- tibble(condition = c("a", "b")) + expected <- tibble( + date = 1:10, + n = c(19, 18, 12, 14, 17, 18, 24, 18, 31, 21) + ) + # act + actual <- summarise_data(df, conditions) + # assert + +}) +``` +:::: + +::::{.column width=25% .small} +That said, in cases where we can be confident (say by static analysis of our code) that it is correct, building tests in this way will give us the confidence going forwards that future changes do not break existing functionality. + +In this case, I have created the expected data frame using the results from running the function. +:::: + +::: + +## Let's test `summarise_data` + +:::{.columns} + +::::{.column width=75%} +```{r} +test_that("it summarises the data", { + # arrange + set.seed(123) + df <- tibble( + date = sample(1:10, 300, TRUE), + condition = sample(c("a", "b", "c"), 300, TRUE) + ) + conditions <- tibble(condition = c("a", "b")) + expected <- tibble( + date = 1:10, + n = c(19, 18, 12, 14, 17, 18, 24, 18, 31, 21) + ) + # act + actual <- summarise_data(df, conditions) + # assert + expect_equal(actual, expected) +}) +``` +:::: + +::::{.column width=25% .small} +The test works! +:::: + +::: + +## Next steps + +- You can add tests to any R project (to test functions), +- But `{testthat}` works best with Packages +- The R Packages book has 3 chapters on [testing](https://r-pkgs.org/testing-basics.html) +- There are two useful helper functions in `{usethis}` + * `use_testthat()` will set up the folders for test scripts + * `use_test()` will create a test file for the currently open script + +## Next steps + +- If your test needs to temporarily create a file, or change some R-options, the [`{withr}` package](https://withr.r-lib.org/) has a lot of useful functions that will automatically clean things up when the test finishes +- If you are writing tests that involve calling out to a database, or you want to test `my_big_function` (from before) without calling the intermediate functions, then you should look at the [`{mockery}` package](https://github.com/r-lib/mockery) + +# Thanks! {.inverse} + +Questions? + + [thomas.jemmett@nhs.net](mailto:thomas.jemmett@nhs.net) / DM me on slack \ No newline at end of file diff --git a/presentations/2023-08-23_nhs-r_unit-testing/renv.lock b/presentations/2023-08-23_nhs-r_unit-testing/renv.lock new file mode 100644 index 0000000..2eaae5e --- /dev/null +++ b/presentations/2023-08-23_nhs-r_unit-testing/renv.lock @@ -0,0 +1,673 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "brio": { + "Package": "brio", + "Version": "1.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c1ee497a6d999947c2c224ae46799b1a" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "desc": { + "Package": "desc", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "utils" + ], + "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" + }, + "diffobj": { + "Package": "diffobj", + "Version": "0.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "crayon", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgbuild": { + "Package": "pkgbuild", + "Version": "1.4.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "callr", + "cli", + "desc", + "processx" + ], + "Hash": "30eaaab94db72652e72e3475c1b55278" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "pkgload": { + "Package": "pkgload", + "Version": "1.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "desc", + "fs", + "glue", + "lifecycle", + "methods", + "pkgbuild", + "processx", + "rlang", + "rprojroot", + "utils", + "withr" + ], + "Hash": "2ec30ffbeec83da57655b850cf2d3e0e" + }, + "praise": { + "Package": "praise", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "a555924add98c99d2f411e37e7d25e9f" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "testthat": { + "Package": "testthat", + "Version": "3.2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "brio", + "callr", + "cli", + "desc", + "digest", + "evaluate", + "jsonlite", + "lifecycle", + "magrittr", + "methods", + "pkgload", + "praise", + "processx", + "ps", + "rlang", + "utils", + "waldo", + "withr" + ], + "Hash": "3f6e7e5e2220856ff865e4834766bf2b" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "waldo": { + "Package": "waldo", + "Version": "0.6.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "diffobj", + "glue", + "methods", + "rlang" + ], + "Hash": "52f574062a7b66e56926988c3fbdb3b7" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-08-24_coffee-and-coding_geospatial/index.qmd b/presentations/2023-08-24_coffee-and-coding_geospatial/index.qmd index f682029..14cab2a 100644 --- a/presentations/2023-08-24_coffee-and-coding_geospatial/index.qmd +++ b/presentations/2023-08-24_coffee-and-coding_geospatial/index.qmd @@ -1,414 +1,420 @@ ---- -title: "Coffee and Coding" -subtitle: "Working with Geospatial Data in R" -author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" -date: 2023-08-24 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] - preview-links: auto - slide-number: false - auto-animate: true - width: 1920 - height: 1080 -execute: - echo: true ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/ - -## Packages we are using today - -```{r setup} -library(tidyverse) - -library(sf) - -library(tidygeocoder) -library(PostcodesioR) - -library(osrm) - -library(leaflet) -``` - -## Getting boundary data - -We can use the [ONS's Geoportal](https://geoportal.statistics.gov.uk/) we can grab boundary data to generate maps - -:::{.columns} - -::::{.column width=50%} -```{r, icb-plot} -#| eval: false -icb_url <- paste0( - "https://services1.arcgis.com", - "/ESMARspQHYMw9BZ9/arcgis", - "/rest/services", - "/Integrated_Care_Boards_April_2023_EN_BGC", - "/FeatureServer/0/query", - "?outFields=*&where=1%3D1&f=geojson" -) -icb_boundaries <- read_sf(icb_url) - -icb_boundaries |> - ggplot() + - geom_sf() + - theme_void() -``` -:::: - -::::{.column width=50%} -```{r, icb-plot} -#| eval: true -#| echo: false -#| fig-height: 8 -``` -:::: - -::: - -## What is the `icb_boundaries` data? - -```{r, show-icb-boundaries} -icb_boundaries |> - select(ICB23CD, ICB23NM) -``` - -## Working with geospatial dataframes - -We can simply join `sf` data frames and "regular" data frames together - -:::{.columns} - -::::{.column width=50%} -```{r, icb-metrics} -#| eval: false -icb_metrics <- icb_boundaries |> - st_drop_geometry() |> - select(ICB23CD) |> - mutate(admissions = rpois(n(), 1000000)) - -icb_boundaries |> - inner_join(icb_metrics, by = "ICB23CD") |> - ggplot() + - geom_sf(aes(fill = admissions)) + - scale_fill_viridis_c() + - theme_void() -``` - -:::: - -::::{.column width=50%} -```{r, icb-metrics} -#| eval: true -#| echo: false -#| fig-height: 8 -``` -:::: - -::: - -## Working with geospatial data frames - -We can manipulate `sf` objects like other data frames - -:::{.columns} - -::::{.column width=50%} - -```{r, london-icbs-1} -#| eval: false -london_icbs <- icb_boundaries |> - filter(ICB23NM |> stringr::str_detect("London")) - -ggplot() + - geom_sf(data = london_icbs) + - geom_sf(data = st_centroid(london_icbs)) + - theme_void() -``` - -:::: - -::::{.column width=50%} -```{r, london-icbs-1} -#| eval: true -#| echo: false -#| fig-height: 8 -``` -:::: - -::: - - -## Working with geospatial data frames - -Summarising the data will combine the geometries. - -```{r, london-icbs-2} -london_icbs |> - summarise(area = sum(Shape__Area)) |> - # and use geospatial functions to create calculations using the geometry - mutate(new_area = st_area(geometry), .before = "geometry") -``` - -
-Why the difference in area? - -. . . - -
-We are using a simplified geometry, so calculating the area will be slightly inaccurate. -The original area was calculated on the non-simplified geometries. - -## Creating our own geospatial data - -```{r, get-location} -location_raw <- postcode_lookup("B2 4BJ") -glimpse(location_raw) -``` - -. . . - -
- -```{r, location-to-sf} -location <- location_raw |> - st_as_sf(coords = c("eastings", "northings"), crs = 27700) |> - select(postcode, ccg) |> - st_transform(crs = 4326) - -location -``` - -## Creating a geospatial data frame for all NHS Trusts - -:::{.columns} - -::::{.column width=50%} - -```{r, get-trusts} -#| eval: false -# using the NHSRtools package -# remotes::install_github("NHS-R-Community/NHSRtools") -trusts <- ods_get_trusts() |> - filter(status == "Active") |> - select(name, org_id, post_code) |> - geocode(postalcode = "post_code") |> - st_as_sf(coords = c("long", "lat"), crs = 4326) -``` - -```{r, get-trusts-geojson} -#| echo: false -# the previous chunk takes some time to run, so this is a pre-cached version -trusts <- read_sf("trusts.geojson") -``` - -```{r, show-trusts} -#| eval: false -trusts |> - leaflet() |> - addProviderTiles("Stamen.TonerLite") |> - addMarkers(popup = ~name) -``` -:::: - -::::{.column width=50%} -```{r, show-trusts} -#| eval: true -#| echo: false -#| fig-height: 8 -``` -:::: - -::: - -## What are the nearest trusts to our location? - -```{r, nearest-trusts} -nearest_trusts <- trusts |> - mutate( - distance = st_distance(geometry, location)[, 1] - ) |> - arrange(distance) |> - head(5) - -nearest_trusts -``` - -## Let's find driving routes to these trusts - -```{r, routes} -routes <- nearest_trusts |> - mutate( - route = map(geometry, ~ osrmRoute(location, st_coordinates(.x))) - ) |> - st_drop_geometry() |> - rename(straight_line_distance = distance) |> - unnest(route) |> - st_as_sf() - -routes -``` - -## Let's show the routes - -```{r, route-map} -#| fig-width: 20 -#| fig-height: 8 -leaflet(routes) |> - addTiles() |> - addMarkers(data = location) |> - addPolylines(color = "black", weight = 3, opacity = 1) |> - addCircleMarkers(data = nearest_trusts, radius = 4, opacity = 1, fillOpacity = 1) -``` - -## We can use `{osrm}` to calculate isochrones - -:::{.columns} - -::::{.column width=50%} -```{r, isochrone} -#| eval: false -iso <- osrmIsochrone(location, breaks = seq(0, 60, 15), res = 10) - -isochrone_ids <- unique(iso$id) - -pal <- colorFactor( - viridis::viridis(length(isochrone_ids)), - isochrone_ids -) - -leaflet(location) |> - addProviderTiles("Stamen.TonerLite") |> - addMarkers() |> - addPolygons( - data = iso, - fillColor = ~ pal(id), - color = "#000000", - weight = 1 - ) -``` -:::: - -::::{.column width=50%} -```{r, isochrone} -#| echo: false -#| eval: true -#| fig-width: 9 -#| fig-height: 8 -``` -:::: - -::: - -## What trusts are in the isochrones? - -The `summarise()` function will "union" the geometry - -```{r, iso-summary} -summarise(iso) -``` - -## What trusts are in the isochrones? - -We can use this with a geo-filter to find the trusts in the isochrone - -```{r, trusts-in-iso} -# also works -trusts_in_iso <- trusts |> - st_filter( - summarise(iso), - .predicate = st_within - ) - -trusts_in_iso -``` - -## What trusts are in the isochrones? - -:::{.columns} - -::::{.column width=50%} -```{r, trusts-in-iso-plot} -#| eval: false -leaflet(trusts_in_iso) |> - addProviderTiles("Stamen.TonerLite") |> - addMarkers() |> - addPolygons( - data = iso, - fillColor = ~pal(id), - color = "#000000", - weight = 1 - ) -``` -:::: - -::::{.column width=50%} -```{r, trusts-in-iso-plot} -#| echo: false -#| eval: true -#| fig-width: 9 -#| fig-height: 8 -``` -:::: - -::: - -## Doing the same but within a radius - -:::{.columns} - -::::{.column width=50%} -```{r, trusts-in-radius} -#| eval: false -r <- 25000 - -trusts_in_radius <- trusts |> - st_filter( - location, - .predicate = st_is_within_distance, - dist = r - ) - -# transforming gives us a pretty smooth circle -radius <- location |> - st_transform(crs = 27700) |> - st_buffer(dist = r) |> - st_transform(crs = 4326) - -leaflet(trusts_in_radius) |> - addProviderTiles("Stamen.TonerLite") |> - addMarkers() |> - addPolygons( - data = radius, - color = "#000000", - weight = 1 - ) -``` -:::: - -::::{.column width=50%} -```{r, trusts-in-radius} -#| echo: false -#| eval: true -#| fig-width: 9 -#| fig-height: 8 -``` -:::: - -::: - -## Further reading - -* [Geocomputation with R](https://r.geocompx.org/) -* [r-spatial](https://r-spatial.org/) -* [{sf} documentation](https://r-spatial.github.io/sf/) -* [Leaflet documentation](https://rstudio.github.io/leaflet/) -* [Tidy Geospatial Networks in R](https://luukvdmeer.github.io/sfnetworks/) \ No newline at end of file +--- +title: "Coffee and Coding" +subtitle: "Working with Geospatial Data in R" +author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" +date: 2023-08-24 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations][ds_presentations] + preview-links: auto + slide-number: false + auto-animate: true + width: 1920 + height: 1080 +execute: + echo: true +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/ + +## Packages we are using today + +```{r setup} +library(tidyverse) + +library(sf) + +library(tidygeocoder) +library(PostcodesioR) + +library(osrm) + +library(leaflet) +``` + +## Getting boundary data + +We can use the [ONS's Geoportal](https://geoportal.statistics.gov.uk/) we can grab boundary data to generate maps + +:::{.columns} + +::::{.column width=50%} +```{r, icb-plot} +#| eval: false +icb_url <- paste0( + "https://services1.arcgis.com", + "/ESMARspQHYMw9BZ9/arcgis", + "/rest/services", + "/Integrated_Care_Boards_April_2023_EN_BGC", + "/FeatureServer/0/query", + "?outFields=*&where=1%3D1&f=geojson" +) +icb_boundaries <- read_sf(icb_url) + +icb_boundaries |> + ggplot() + + geom_sf() + + theme_void() +``` +:::: + +::::{.column width=50%} +```{r, icb-plot} +#| eval: true +#| echo: false +#| fig-height: 8 +``` +:::: + +::: + +## What is the `icb_boundaries` data? + +```{r, show-icb-boundaries} +icb_boundaries |> + select(ICB23CD, ICB23NM) +``` + +## Working with geospatial dataframes + +We can simply join `sf` data frames and "regular" data frames together + +:::{.columns} + +::::{.column width=50%} +```{r, icb-metrics} +#| eval: false +icb_metrics <- icb_boundaries |> + st_drop_geometry() |> + select(ICB23CD) |> + mutate(admissions = rpois(n(), 1000000)) + +icb_boundaries |> + inner_join(icb_metrics, by = "ICB23CD") |> + ggplot() + + geom_sf(aes(fill = admissions)) + + scale_fill_viridis_c() + + theme_void() +``` + +:::: + +::::{.column width=50%} +```{r, icb-metrics} +#| eval: true +#| echo: false +#| fig-height: 8 +``` +:::: + +::: + +## Working with geospatial data frames + +We can manipulate `sf` objects like other data frames + +:::{.columns} + +::::{.column width=50%} + +```{r, london-icbs-1} +#| eval: false +london_icbs <- icb_boundaries |> + filter(ICB23NM |> stringr::str_detect("London")) + +ggplot() + + geom_sf(data = london_icbs) + + geom_sf(data = st_centroid(london_icbs)) + + theme_void() +``` + +:::: + +::::{.column width=50%} +```{r, london-icbs-1} +#| eval: true +#| echo: false +#| fig-height: 8 +``` +:::: + +::: + + +## Working with geospatial data frames + +Summarising the data will combine the geometries. + +```{r, london-icbs-2} +london_icbs |> + summarise(area = sum(Shape__Area)) |> + # and use geospatial functions to create calculations using the geometry + mutate(new_area = st_area(geometry), .before = "geometry") +``` + +
+Why the difference in area? + +. . . + +
+We are using a simplified geometry, so calculating the area will be slightly inaccurate. +The original area was calculated on the non-simplified geometries. + +## Creating our own geospatial data + +```{r, get-location} +location_raw <- postcode_lookup("B2 4BJ") +glimpse(location_raw) +``` + +. . . + +
+ +```{r, location-to-sf} +location <- location_raw |> + st_as_sf(coords = c("eastings", "northings"), crs = 27700) |> + select(postcode, ccg) |> + st_transform(crs = 4326) + +location +``` + +## Creating a geospatial data frame for all NHS Trusts + +:::{.columns} + +::::{.column width=50%} + +```{r, get-trusts} +#| eval: false +# using the NHSRtools package +# remotes::install_github("NHS-R-Community/NHSRtools") +trusts <- ods_get_trusts() |> + filter(status == "Active") |> + select(name, org_id, post_code) |> + geocode(postalcode = "post_code") |> + st_as_sf(coords = c("long", "lat"), crs = 4326) +``` + +```{r, get-trusts-geojson} +#| echo: false +# the previous chunk takes some time to run, so this is a pre-cached version +trusts <- read_sf("trusts.geojson") +``` + +```{r, show-trusts} +#| eval: false +trusts |> + leaflet() |> + addProviderTiles("Stamen.TonerLite") |> + addMarkers(popup = ~name) +``` +:::: + +::::{.column width=50%} +```{r, show-trusts} +#| eval: true +#| echo: false +#| fig-height: 8 +``` +:::: + +::: + +## What are the nearest trusts to our location? + +```{r, nearest-trusts} +nearest_trusts <- trusts |> + mutate( + distance = st_distance(geometry, location)[, 1] + ) |> + arrange(distance) |> + head(5) + +nearest_trusts +``` + +## Let's find driving routes to these trusts + +```{r, routes} +routes <- nearest_trusts |> + mutate( + route = map(geometry, ~ osrmRoute(location, st_coordinates(.x))) + ) |> + st_drop_geometry() |> + rename(straight_line_distance = distance) |> + unnest(route) |> + st_as_sf() + +routes +``` + +## Let's show the routes + +```{r, route-map} +#| fig-width: 20 +#| fig-height: 8 +leaflet(routes) |> + addTiles() |> + addMarkers(data = location) |> + addPolylines(color = "black", weight = 3, opacity = 1) |> + addCircleMarkers(data = nearest_trusts, radius = 4, opacity = 1, fillOpacity = 1) +``` + +## We can use `{osrm}` to calculate isochrones + +:::{.columns} + +::::{.column width=50%} +```{r, isochrone} +#| eval: false +iso <- osrmIsochrone(location, breaks = seq(0, 60, 15), res = 10) + +isochrone_ids <- unique(iso$id) + +pal <- colorFactor( + viridisLite::viridis(length(isochrone_ids)), + isochrone_ids +) + +leaflet(location) |> + addProviderTiles("Stamen.TonerLite") |> + addMarkers() |> + addPolygons( + data = iso, + fillColor = ~ pal(id), + color = "#000000", + weight = 1 + ) +``` +:::: + +::::{.column width=50%} +```{r, isochrone} +#| echo: false +#| eval: true +#| fig-width: 9 +#| fig-height: 8 +``` +:::: + +::: + +## What trusts are in the isochrones? + +The `summarise()` function will "union" the geometry + +```{r, iso-summary} +summarise(iso) +``` + +## What trusts are in the isochrones? + +We can use this with a geo-filter to find the trusts in the isochrone + +```{r, trusts-in-iso} +# also works +trusts_in_iso <- trusts |> + st_filter( + summarise(iso), + .predicate = st_within + ) + +trusts_in_iso +``` + +## What trusts are in the isochrones? + +:::{.columns} + +::::{.column width=50%} +```{r, trusts-in-iso-plot} +#| eval: false +leaflet(trusts_in_iso) |> + addProviderTiles("Stamen.TonerLite") |> + addMarkers() |> + addPolygons( + data = iso, + fillColor = ~pal(id), + color = "#000000", + weight = 1 + ) +``` +:::: + +::::{.column width=50%} +```{r, trusts-in-iso-plot} +#| echo: false +#| eval: true +#| fig-width: 9 +#| fig-height: 8 +``` +:::: + +::: + +## Doing the same but within a radius + +:::{.columns} + +::::{.column width=50%} +```{r, trusts-in-radius} +#| eval: false +r <- 25000 + +trusts_in_radius <- trusts |> + st_filter( + location, + .predicate = st_is_within_distance, + dist = r + ) + +# transforming gives us a pretty smooth circle +radius <- location |> + st_transform(crs = 27700) |> + st_buffer(dist = r) |> + st_transform(crs = 4326) + +leaflet(trusts_in_radius) |> + addProviderTiles("Stamen.TonerLite") |> + addMarkers() |> + addPolygons( + data = radius, + color = "#000000", + weight = 1 + ) +``` +:::: + +::::{.column width=50%} +```{r, trusts-in-radius} +#| echo: false +#| eval: true +#| fig-width: 9 +#| fig-height: 8 +``` +:::: + +::: + +## Further reading + +* [Geocomputation with R](https://r.geocompx.org/) +* [r-spatial](https://r-spatial.org/) +* [{sf} documentation](https://r-spatial.github.io/sf/) +* [Leaflet documentation](https://rstudio.github.io/leaflet/) +* [Tidy Geospatial Networks in R](https://luukvdmeer.github.io/sfnetworks/) diff --git a/presentations/2023-08-24_coffee-and-coding_geospatial/renv.lock b/presentations/2023-08-24_coffee-and-coding_geospatial/renv.lock new file mode 100644 index 0000000..96fbaa2 --- /dev/null +++ b/presentations/2023-08-24_coffee-and-coding_geospatial/renv.lock @@ -0,0 +1,1877 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "DBI": { + "Package": "DBI", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "065ae649b05f1ff66bb0c793107508f5" + }, + "KernSmooth": { + "Package": "KernSmooth", + "Version": "2.23-24", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats" + ], + "Hash": "9f33a1ee37bbe8919eb2ec4b9f2473a5" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "PostcodesioR": { + "Package": "PostcodesioR", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "httr" + ], + "Hash": "6fd5043a1023796198ad74afe58a1eee" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "6b868847b365672d6c1677b1608da9ed" + }, + "RcppSimdJson": { + "Package": "RcppSimdJson", + "Version": "0.1.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "utils" + ], + "Hash": "444ca3d3bcf460a5e8f45d96431fb7a5" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" + }, + "bit64": { + "Package": "bit64", + "Version": "4.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "e84984bf5f12a18628d9a02322128dfd" + }, + "blob": { + "Package": "blob", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "rlang", + "vctrs" + ], + "Hash": "40415719b5a479b87949f3aa0aee737c" + }, + "broom": { + "Package": "broom", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "8fcc818f3b9887aebaf206f141437cc9" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "class": { + "Package": "class", + "Version": "7.3-22", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "stats", + "utils" + ], + "Hash": "f91f6b29f38b8c280f2b9477787d4bb2" + }, + "classInt": { + "Package": "classInt", + "Version": "0.4-10", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "KernSmooth", + "R", + "class", + "e1071", + "grDevices", + "graphics", + "stats" + ], + "Hash": "f5a40793b1ae463a7ffb3902a95bf864" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "conflicted": { + "Package": "conflicted", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "memoise", + "rlang" + ], + "Hash": "bb097fccb22d156624fd07cd2894ddb6" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "ab12c7b080a57475248a30f4db6298c0" + }, + "curl": { + "Package": "curl", + "Version": "6.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ff51697d9205fe715f29e7171e874c6e" + }, + "data.table": { + "Package": "data.table", + "Version": "1.16.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "2e00b378fc3be69c865120d9f313039a" + }, + "dbplyr": { + "Package": "dbplyr", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "DBI", + "R", + "R6", + "blob", + "cli", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "utils", + "vctrs", + "withr" + ], + "Hash": "39b2e002522bfd258039ee4e889e0fd1" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "dtplyr": { + "Package": "dtplyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "data.table", + "dplyr", + "glue", + "lifecycle", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "54ed3ea01b11e81a86544faaecfef8e2" + }, + "e1071": { + "Package": "e1071", + "Version": "1.7-16", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "class", + "grDevices", + "graphics", + "methods", + "proxy", + "stats", + "utils" + ], + "Hash": "27a09ca40266a1066d62ef5402dd51d6" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "forcats": { + "Package": "forcats", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "tibble" + ], + "Hash": "1a0a9a3d5083d0d573c4214576f1e690" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "gargle": { + "Package": "gargle", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "fs", + "glue", + "httr", + "jsonlite", + "lifecycle", + "openssl", + "rappdirs", + "rlang", + "stats", + "utils", + "withr" + ], + "Hash": "fc0b272e5847c58cd5da9b20eedbd026" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "googlePolylines": { + "Package": "googlePolylines", + "Version": "0.8.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp" + ], + "Hash": "c4657cf9d38168744b11016b818459ca" + }, + "googledrive": { + "Package": "googledrive", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gargle", + "glue", + "httr", + "jsonlite", + "lifecycle", + "magrittr", + "pillar", + "purrr", + "rlang", + "tibble", + "utils", + "uuid", + "vctrs", + "withr" + ], + "Hash": "e99641edef03e2a5e87f0a0b1fcc97f4" + }, + "googlesheets4": { + "Package": "googlesheets4", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cli", + "curl", + "gargle", + "glue", + "googledrive", + "httr", + "ids", + "lifecycle", + "magrittr", + "methods", + "purrr", + "rematch2", + "rlang", + "tibble", + "utils", + "vctrs", + "withr" + ], + "Hash": "d6db1667059d027da730decdc214b959" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "haven": { + "Package": "haven", + "Version": "2.5.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "forcats", + "hms", + "lifecycle", + "methods", + "readr", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "9171f898db9d9c4c1b2c745adc2c1ef1" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "ids": { + "Package": "ids", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "openssl", + "uuid" + ], + "Hash": "99df65cfef20e525ed38c3d2577f7190" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "janitor": { + "Package": "janitor", + "Version": "2.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "dplyr", + "hms", + "lifecycle", + "lubridate", + "magrittr", + "purrr", + "rlang", + "snakecase", + "stringi", + "stringr", + "tidyr", + "tidyselect" + ], + "Hash": "5baae149f1082f466df9d1442ba7aa65" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "leaflet": { + "Package": "leaflet", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RColorBrewer", + "crosstalk", + "htmltools", + "htmlwidgets", + "jquerylib", + "leaflet.providers", + "magrittr", + "methods", + "png", + "raster", + "scales", + "sp", + "stats", + "viridisLite", + "xfun" + ], + "Hash": "ca012d4a706e21ce217ba15f22d402b2" + }, + "leaflet.providers": { + "Package": "leaflet.providers", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools" + ], + "Hash": "c0b81ad9d5d932772f7a457ac398cf36" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "mapiso": { + "Package": "mapiso", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "isoband", + "sf" + ], + "Hash": "ba0165748eb593d7276567def5b2228e" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "modelr": { + "Package": "modelr", + "Version": "0.1.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "magrittr", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "vctrs" + ], + "Hash": "4f50122dc256b1b6996a4703fecea821" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "openssl": { + "Package": "openssl", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "d413e0fef796c9401a4419485f709ca1" + }, + "osrm": { + "Package": "osrm", + "Version": "4.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RcppSimdJson", + "curl", + "googlePolylines", + "mapiso", + "sf", + "utils" + ], + "Hash": "1507ab660b3f838680e205fd4f4d2de2" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "png": { + "Package": "png", + "Version": "0.1-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "bd54ba8a0a5faded999a7aab6e46b374" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "proxy": { + "Package": "proxy", + "Version": "0.4-27", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "e0ef355c12942cf7a6b91a6cfaea8b3e" + }, + "ps": { + "Package": "ps", + "Version": "1.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b4404b1de13758dea1c0484ad0d48563" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "ragg": { + "Package": "ragg", + "Version": "1.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "0595fe5e47357111f29ad19101c7d271" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "raster": { + "Package": "raster", + "Version": "3.6-30", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "methods", + "sp", + "terra" + ], + "Hash": "0e2829df8cb74a98179c886b023ffea8" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "reprex": { + "Package": "reprex", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "callr", + "cli", + "clipr", + "fs", + "glue", + "knitr", + "lifecycle", + "rlang", + "rmarkdown", + "rstudioapi", + "utils", + "withr" + ], + "Hash": "97b1d5361a24d9fb588db7afe3e5bcbf" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.17.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5f90cd73946d706cfe26024294236113" + }, + "rvest": { + "Package": "rvest", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "httr", + "lifecycle", + "magrittr", + "rlang", + "selectr", + "tibble", + "xml2" + ], + "Hash": "0bcf0c6f274e90ea314b812a6d19a519" + }, + "s2": { + "Package": "s2", + "Version": "1.1.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "wk" + ], + "Hash": "3c8013cdd7f1d20de5762e3f97e5e274" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "selectr": { + "Package": "selectr", + "Version": "0.4-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "methods", + "stringr" + ], + "Hash": "3838071b66e0c566d55cc26bd6e27bf4" + }, + "sf": { + "Package": "sf", + "Version": "1.0-19", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DBI", + "R", + "Rcpp", + "classInt", + "grDevices", + "graphics", + "grid", + "magrittr", + "methods", + "s2", + "stats", + "tools", + "units", + "utils" + ], + "Hash": "fe02eec2f6b3ba0e24afe83d5ccfb528" + }, + "snakecase": { + "Package": "snakecase", + "Version": "0.11.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stringi", + "stringr" + ], + "Hash": "58767e44739b76965332e8a4fe3f91f1" + }, + "sp": { + "Package": "sp", + "Version": "2.1-4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "75940133cca2e339afce15a586f85b11" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "sys": { + "Package": "sys", + "Version": "3.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "de342ebfebdbf40477d0758d05426646" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle" + ], + "Hash": "213b6b8ed5afbf934843e6c3b090d418" + }, + "terra": { + "Package": "terra", + "Version": "1.7-83", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "methods" + ], + "Hash": "fbeffe988419d292225a57cf9c284802" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle", + "systemfonts" + ], + "Hash": "5142f8bc78ed3d819d26461b641627ce" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidygeocoder": { + "Package": "tidygeocoder", + "Version": "1.0.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "dplyr", + "httr", + "jsonlite", + "lifecycle", + "progress", + "tibble" + ], + "Hash": "44fd552dae5b20c4224e895449e3827a" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tidyverse": { + "Package": "tidyverse", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "cli", + "conflicted", + "dbplyr", + "dplyr", + "dtplyr", + "forcats", + "ggplot2", + "googledrive", + "googlesheets4", + "haven", + "hms", + "httr", + "jsonlite", + "lubridate", + "magrittr", + "modelr", + "pillar", + "purrr", + "ragg", + "readr", + "readxl", + "reprex", + "rlang", + "rstudioapi", + "rvest", + "stringr", + "tibble", + "tidyr", + "xml2" + ], + "Hash": "c328568cd14ea89a83bd4ca7f54ae07e" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "units": { + "Package": "units", + "Version": "0.8-5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp" + ], + "Hash": "119d19da480e873f72241ff6962ffd83" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "uuid": { + "Package": "uuid", + "Version": "1.2-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "34e965e62a41fcafb1ca60e9b142085b" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "wk": { + "Package": "wk", + "Version": "0.9.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "37be35d733130f1de1ef51672cf7cdc0" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-09-07_coffee_and_coding_functions/index.qmd b/presentations/2023-09-07_coffee_and_coding_functions/index.qmd index b76945b..6451d9b 100644 --- a/presentations/2023-09-07_coffee_and_coding_functions/index.qmd +++ b/presentations/2023-09-07_coffee_and_coding_functions/index.qmd @@ -23,6 +23,12 @@ execute: echo: true --- +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + ```{r setup} #| echo: false diff --git a/presentations/2023-09-07_coffee_and_coding_functions/renv.lock b/presentations/2023-09-07_coffee_and_coding_functions/renv.lock new file mode 100644 index 0000000..d607eaf --- /dev/null +++ b/presentations/2023-09-07_coffee_and_coding_functions/renv.lock @@ -0,0 +1,1446 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "Deriv": { + "Package": "Deriv", + "Version": "4.1.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "cd52c065c9e687c60c56b51f10f7bcd3" + }, + "Formula": { + "Package": "Formula", + "Version": "1.2-5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats" + ], + "Hash": "7a29697b75e027767a53fde6c903eca7" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "MatrixModels": { + "Package": "MatrixModels", + "Version": "0.5-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "methods", + "stats" + ], + "Hash": "0776bf7526869e0286b0463cb72fb211" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "6b868847b365672d6c1677b1608da9ed" + }, + "RcppEigen": { + "Package": "RcppEigen", + "Version": "0.3.4.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "stats", + "utils" + ], + "Hash": "4ac8e423216b8b70cb9653d1b3f71eb9" + }, + "SparseM": { + "Package": "SparseM", + "Version": "1.84-2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "e78499cbcbbca98200254bd171379165" + }, + "StrategyUnitTheme": { + "Package": "StrategyUnitTheme", + "Version": "0.1.7", + "Source": "GitHub", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteUsername": "the-strategy-unit", + "RemoteRepo": "StrategyUnitTheme", + "RemoteRef": "main", + "RemoteSha": "09397568ef66d2cf757b677f34dbd9f7fd4b02de", + "Requirements": [ + "ggplot2", + "rmarkdown", + "xaringan" + ], + "Hash": "8066991ff77491786e0c651de8ea90bd" + }, + "abind": { + "Package": "abind", + "Version": "1.4-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods", + "utils" + ], + "Hash": "2288423bb0f20a457800d7fc47f6aa54" + }, + "backports": { + "Package": "backports", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "e1e1b9d75c37401117b636b7ae50827a" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "boot": { + "Package": "boot", + "Version": "1.3-31", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats" + ], + "Hash": "de2a4646c18661d6a0a08ec67f40b7ed" + }, + "broom": { + "Package": "broom", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "backports", + "dplyr", + "generics", + "glue", + "lifecycle", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyr" + ], + "Hash": "8fcc818f3b9887aebaf206f141437cc9" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "car": { + "Package": "car", + "Version": "3.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Formula", + "MASS", + "R", + "abind", + "carData", + "grDevices", + "graphics", + "lme4", + "mgcv", + "nlme", + "nnet", + "pbkrtest", + "quantreg", + "scales", + "stats", + "utils" + ], + "Hash": "82067bf302d1440b730437693a86406a" + }, + "carData": { + "Package": "carData", + "Version": "3.0-5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ac6cdb8552c61bd36b0e54d07cf2aab7" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "corrplot": { + "Package": "corrplot", + "Version": "0.95", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "f52d5babd10d348f9c0771386b61ea4a" + }, + "cowplot": { + "Package": "cowplot", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "ggplot2", + "grDevices", + "grid", + "gtable", + "methods", + "rlang", + "scales" + ], + "Hash": "8ef2084dd7d28847b374e55440e4f8cb" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "doBy": { + "Package": "doBy", + "Version": "4.6.24", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Deriv", + "MASS", + "Matrix", + "R", + "boot", + "broom", + "cowplot", + "dplyr", + "ggplot2", + "methods", + "microbenchmark", + "modelr", + "rlang", + "tibble", + "tidyr" + ], + "Hash": "8ddf795104defe53c5392a588888ec68" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "ggpubr": { + "Package": "ggpubr", + "Version": "0.6.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cowplot", + "dplyr", + "ggplot2", + "ggrepel", + "ggsci", + "ggsignif", + "glue", + "grid", + "gridExtra", + "magrittr", + "polynom", + "purrr", + "rlang", + "rstatix", + "scales", + "stats", + "tibble", + "tidyr", + "utils" + ], + "Hash": "c957612b8bb1ee9ab7b2450d26663e7e" + }, + "ggrepel": { + "Package": "ggrepel", + "Version": "0.9.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "ggplot2", + "grid", + "rlang", + "scales", + "withr" + ], + "Hash": "bc7c0d607038ae9cb4f539b47cbdbda7" + }, + "ggsci": { + "Package": "ggsci", + "Version": "3.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "ggplot2", + "grDevices", + "scales" + ], + "Hash": "0c3268cddf4d3a3ce4e7e6330f8e92c8" + }, + "ggsignif": { + "Package": "ggsignif", + "Version": "0.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "ggplot2" + ], + "Hash": "a57f0f5dbcfd0d77ad4ff33032f5dc79" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "gridExtra": { + "Package": "gridExtra", + "Version": "2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "graphics", + "grid", + "gtable", + "utils" + ], + "Hash": "7d7f283939f563670a697165b2cf5560" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "here": { + "Package": "here", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "rprojroot" + ], + "Hash": "24b224366f9c2e7534d2344d10d59211" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "httpuv": { + "Package": "httpuv", + "Version": "1.6.15", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "Rcpp", + "later", + "promises", + "utils" + ], + "Hash": "d55aa087c47a63ead0f6fc10f8fa1ee0" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "janitor": { + "Package": "janitor", + "Version": "2.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "dplyr", + "hms", + "lifecycle", + "lubridate", + "magrittr", + "purrr", + "rlang", + "snakecase", + "stringi", + "stringr", + "tidyr", + "tidyselect" + ], + "Hash": "5baae149f1082f466df9d1442ba7aa65" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "later": { + "Package": "later", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "rlang" + ], + "Hash": "a3e051d405326b8b0012377434c62b37" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lme4": { + "Package": "lme4", + "Version": "1.1-35.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "R", + "Rcpp", + "RcppEigen", + "boot", + "graphics", + "grid", + "lattice", + "methods", + "minqa", + "nlme", + "nloptr", + "parallel", + "splines", + "stats", + "utils" + ], + "Hash": "16a08fc75007da0d08e0c0388c7c33e6" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "microbenchmark": { + "Package": "microbenchmark", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "stats" + ], + "Hash": "f9d226d88d4087d817d4e616626ce8e5" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "minqa": { + "Package": "minqa", + "Version": "1.2.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp" + ], + "Hash": "785ef8e22389d4a7634c6c944f2dc07d" + }, + "modelr": { + "Package": "modelr", + "Version": "0.1.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "magrittr", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "vctrs" + ], + "Hash": "4f50122dc256b1b6996a4703fecea821" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "nloptr": { + "Package": "nloptr", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "27550641889a3abf3aec4d91186311ec" + }, + "nnet": { + "Package": "nnet", + "Version": "7.3-19", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "2c797b46eea7fb58ede195bc0b1f1138" + }, + "numDeriv": { + "Package": "numDeriv", + "Version": "2016.8-1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "df58958f293b166e4ab885ebcad90e02" + }, + "pbkrtest": { + "Package": "pbkrtest", + "Version": "0.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "R", + "broom", + "doBy", + "dplyr", + "lme4", + "methods", + "numDeriv" + ], + "Hash": "938e6bbc4ac57534f8b43224506a8966" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "polynom": { + "Package": "polynom", + "Version": "1.4-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "ceb5c2a59ba33d42d051285a3e8a5118" + }, + "promises": { + "Package": "promises", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "Rcpp", + "fastmap", + "later", + "magrittr", + "rlang", + "stats" + ], + "Hash": "434cd5388a3979e74be5c219bcd6e77d" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "quantreg": { + "Package": "quantreg", + "Version": "5.99", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "Matrix", + "MatrixModels", + "R", + "SparseM", + "graphics", + "methods", + "stats", + "survival" + ], + "Hash": "c6e207b874494a7ac4d52f02dcd7d3af" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" + }, + "rstatix": { + "Package": "rstatix", + "Version": "0.7.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "broom", + "car", + "corrplot", + "dplyr", + "generics", + "magrittr", + "purrr", + "rlang", + "stats", + "tibble", + "tidyr", + "tidyselect", + "utils" + ], + "Hash": "5045fbb71b143878d8c51975d1d7d56d" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "servr": { + "Package": "servr", + "Version": "0.32", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "httpuv", + "jsonlite", + "mime", + "xfun" + ], + "Hash": "63e4ea2379e79cf18813dd0d8146d27c" + }, + "snakecase": { + "Package": "snakecase", + "Version": "0.11.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stringi", + "stringr" + ], + "Hash": "58767e44739b76965332e8a4fe3f91f1" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "survival": { + "Package": "survival", + "Version": "3.7-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "splines", + "stats", + "utils" + ], + "Hash": "5aaa9cbaf4aba20f8e06fdea1850a398" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xaringan": { + "Package": "xaringan", + "Version": "0.30", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "htmltools", + "knitr", + "rmarkdown", + "servr", + "xfun" + ], + "Hash": "40a1e30d3fb323f249a5e2fbec50c3b1" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + }, + "zoo": { + "Package": "zoo", + "Version": "1.8-12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "5c715954112b45499fb1dadc6ee6ee3e" + } + } +} diff --git a/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/index.qmd b/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/index.qmd index 6c213eb..6f80b54 100644 --- a/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/index.qmd +++ b/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/index.qmd @@ -1,416 +1,421 @@ ---- -title: "System Dynamics in health and care" -subtitle: "fitting square data into round models" -author: "[Sally Thompson](mailto:sally.thompson37@nhs.net)" -date: 2023-10-09 -date-format: "DD MMMM YYYY" -format: - - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - self-contained: true - - preview-links: auto - slide-number: false - auto-animate: true - width: 1600 - height: 900 - - footer: | - NHS-R conference 2023 - -execute: - eval: true - echo: true - message: false - warning: false ---- - -```{r setup} -#| include: false -#| label: setup - -library(tidyr) -library(dplyr) -library(readr) -library(lubridate) -library(tibble) -library(stringr) - -file_name <- "https://raw.githubusercontent.com/microsoft/r-server-hospital-length-of-stay/master/Data/LengthOfStay.csv" - -data <- read_csv(file = file_name) - -spell_dates <- data |> - mutate(date_admit = mdy(vdate) + years(10), - date_discharge = mdy(discharged) + years(10)) |> - select(facid, date_admit, date_discharge) |> - sample_n(10000) - -``` - -## Health Data in the Headlines - - -![](images/headline%20waitlist%20record.png){.absolute top=75 left=0 fig-alt="newspaper headline waiting list hits record 7.5 million"} - - - -![](images/headline%20ambulance%20ae.png){.absolute top=200 right=0 fig-alt="news headline linking ambulance response times to to A&E waits"} - - - -![](images/headline%20bed%20block.png){.absolute bottom=40 left=275 fig-alt="news headline links delays in discharging people from hospital to full A&E departments"} - -::: {.notes} -Used to seeing headlines that give a snapshot figure but doesn't say much about the system. -Now starting to see headlines that recognise flow through the system rather than snapshot in time of just one part. -Can get better understanding of the issues in a system if we can map it as stocks -and flows, but our datasets not designed to give up this information very readily. -This talk is how I have tried to meet that challenge. - -::: - - -## Through the System Dynamics lens - -![](images/cap%20constrained%20model%20transp.png){fig-alt="stock-flow structure of a simple capacity-constrained model"} - -Stock-flow model - -Dynamic behaviour, feedback loops - -::: {.notes} -In a few seconds, what is SD? -An approach to understanding the behaviour of complex systems over time. -A method of mapping a system as stocks, whose levels can only change due to flows in and flows out. Stocks could be people on a waiting list, on a ward, money, ... -Flows are the rate at which things change in a given time period e.g. admissions per day, referrals -per month. -Behaviour of the system is determined by how the components interact with each other, not what -each component does. -Mapping the structure of a system like this leads us to identify feedback loops, -and consequences of an action - both intended and unintended. - -In this capacity-constrained model we only need 3 parameters to run the model (exogenous). All the behaviour -within the grey box is determined by the interactions of those components (indogenous). - -How do we get a value/values for referrals per day? - -(currently use specialist software to build and run our models, aim is to get to a point where we can run -in open source.) -::: - - -## Determining flows - -![](images/adm%20dis%20model%20transp.png){.absolute top="55" right="10" fig-alt="One stock model, with one flow in and one flow out - the flow in, labelled as 'admissions per day', is highlighted." width="800"} - -::: columns -::: {.column width="40%"} -::: {style="font-size:0.65em"} -'admissions per day' is needed to populate the model. - -'discharged' could be used to verify the model against known data - -- How many admissions per day (or week, month...) -::: -::: -::: - -::: incremental -![](images/key%20dates.png){.fragment .absolute bottom="110" left="0" fig-alt="list of first 10 key dates, starting on January 1st 2022." width="153"} -::: - -::: r-stack - ![](images/pat%20dates.png){.fragment .absolute bottom="160" left="340" width="458"} ![](images/admissions.png){.fragment .absolute bottom="110" left="850" width="205"} - ![](images/wk admissions.png){.fragment .absolute bottom="275" left="1100" width="205"} -::: - -::: {.notes} -Going to use very simple model shown to explain how to extract flow data for admissions. -Will start with visual explainer before going into the code. -1. generate list of key dates (in this case daily, could be weekly, monthly) -2. take our patient-level ID with admission and discharge dates -3. count of admissions on that day/week -::: - -## Determining occupancy {.smaller} - -![](images/adm%20dis%20model%20stock%20trans.png){.absolute top="55" right="10" fig-alt="One stock model, with one flow in and one flow out - the stock, labelled as 'on ward', is highlighted." width="800"} - -::: columns -::: {.column width="40%"} - - -'on ward' is used to verify the model against known data - -- Logic statement testing if the key date is wholly between admission and discharge dates -- flag for a match -::: -::: - -::: incremental -![](images/key%20dates.png){.absolute bottom="110" left="50" fig-alt="List of key dates, " width="153"} -::: - -::: r-stack -![](images/pat%20dates.png){.fragment .absolute bottom="148" left="328" width="458"} -![](images/occ%201.png){.fragment .absolute bottom="160" left="330" width="621"} -![](images/occ%202.png){.fragment .absolute bottom="160" left="330" width="756"} -![](images/occ%203.png){.fragment .absolute bottom="160" left="330" width="900"} -![](images/occ%204.png){.fragment .absolute bottom="160" left="330" width="1034"} -![](images/occupancy.png){.fragment .absolute bottom="110" right="0" width="209"} -::: - -::: {.notes} -Might also want to generate occupancy, to compare the model output with actual data -to verify/validate. -1. generate list of key dates -2. take our patient-level ID with admission and discharge dates -3. going to take each date in our list of keydates, and see if there is an admission before that date -and discharge after -4. this creates a wide data frame, the same length as patient data. -5. once run through all the dates in the list, sum each column - -Patient A admitted on 2nd, so only starts being classed as resident on 3rd. -::: - - - -## in R - flows - -Easy to do with `count`, or `group_by` and `summarise` - - - -```{r} -#| output-location: column-fragment - - admit_d <- spell_dates |> - group_by(date_admit) |> - count(date_admit) - -head(admit_d) - -``` - - - -## in R - occupancy - -Generate list of key dates - -::: columns - - -::: {.column width="65%"} -```{r} - -date_start <- dmy(01012022) -date_end <- dmy(31012022) -run_len <- length(seq(from = date_start, to = date_end, by = "day")) - -keydates <- data.frame( - date = c(seq(date_start, by = "day", length.out=run_len))) - - -``` -::: - -::: {.column width="35%"} -```{r} -#| echo: false -head(keydates) -``` -::: -::: - -::: {.notes} -Start by generating the list of keydates. In this example we're running the model -in days, and checking each day in 2022. -Need the run length for the next step, to know how many times to iterate over - -::: - -## in R - occupancy - -Iterate over each date - need to have been admitted before, and discharged after - - -```{r} -#| eval: false - -occupancy_flag <- function(df) { - - # pre-allocate tibble size to speed up iteration in loop - activity_all <- tibble(nrow = nrow(df)) |> - select() - - for (i in 1:run_len) { - - activity_period <- case_when( - - # creates 1 flag if resident for complete day - df$date_admit < keydates$keydate[i] & - df$date_discharge > keydates$keydate[i] ~ 1, - TRUE ~ 0) - - # column bind this day's flags to previous - activity_all <- bind_cols(activity_all, activity_period) - - } - - # rename column to match the day being counted - activity_all <- activity_all |> - setNames(paste0("d_", keydates$date)) - - # bind flags columns to patient data - daily_adm <- bind_cols(df, activity_all) |> - pivot_longer( - cols = starts_with("d_"), - names_to = "date", - values_to = "count" - ) |> - - group_by(date) |> - summarise(resident = sum(count)) |> - ungroup() |> - mutate(date = str_remove(date, "d_")) - - } - - -``` - - -. . . - -Is there a better way than using a `for` loop? - -::: {.notes} -Pre-allocate tibbles - -activity_all will end up as very wide tibble, with a column for each date in list of keydates. - -For each date in the list of key dates, compares with admission date & discharge date; need to be admitted before the key date and discharged after the key date. If match, flag = 1. -Creates a column for each day, then binds this to activity all. -Rename each column with the date it was checking (add a character to start of column name so column doesn't start with numeric) -Pivot long, then group by date and sum the flags (other variables could be added here, such as TFC or provider code) -::: - -## Longer Time Periods - flows - -Use `lubridate::floor_date` to generate the date at start of week/month -```{r} -#| output-location: fragment - -admit_wk <- spell_dates |> - mutate(week_start = floor_date( - date_admit, unit = "week", week_start = 1 # start week on Monday - )) |> - count(week_start) # could add other parameters such as provider code, TFC etc - -head(admit_wk) - -``` - - -::: {.notes} -Might run SD model in weeks or months - e.g. months for care homes -Use lubridate to create new variable with start date of week/month/year etc - -::: - - -## Longer Time Periods - occupancy - -Key dates to include the dates at the start and end of each time period - -::: columns -::: {.column width="65%"} -```{r} - -date_start <- dmy(03012022) # first Monday of the year -date_end <- dmy(01012023) -run_len <- length(seq(from = date_start, to = date_end, by = "week")) - -keydates <- data.frame(wk_start = c(seq(date_start, - by = "week", - length.out=run_len))) |> - mutate( - wk_end = wk_start + 6) # last date in time period - -``` -::: - -::: {.column width="35%"} -```{r} -#| echo: false - -head(keydates) -``` -::: -::: - - -::: {.notes} -Model might make more sense to run in weeks or months (e.g. care home), so list of keydates need a start date and end date for each time period. -::: - -## Longer Time Periods - -More logic required if working in weeks or months - can only be in one place at any given time - -```{r} -#| eval: false - - -# flag for occupancy -activity_period <- case_when( - - # creates 1 flag if resident for complete week - df$date_admit < keydates$wk_start[i] & df$date_discharge > keydates$wk_end[i] ~ 1, - TRUE ~ 0) -``` - -::: {.notes} -And a little bit more logic -Occupancy requires the patient to have been admitted before the start of the week/month, and discharged after the end of the week/month -::: - - -## Applying the data - -![](images/interface%20model%20transp.png){fig-alt="One stock model, with one flow in and one flow out - the stock has a time chart overlaid showing model output compared to 'real' data"} - -::: {.notes} -How to apply this wrangling of data to the system dynamic model? -Admissions data used as an input to the flow - could be reduced to a single figure (average), or there may be variation by season/day of week etc. -Occupancy (and discharges) used to verify the model output against known data. -::: - - - - - - - - -## Next Steps - -- Generalise function to a state where it can be used by others - onto Github - -- Turn this into a package - -- Open-source SD models and interfaces - R Shiny or Python - -## Questions, comments, suggestions? - -| -| -| -| Please get in touch! -| -| [Sally.Thompson37\@nhs.net](mailto:sally.thompson37@nhs.net) - +--- +title: "System Dynamics in health and care" +subtitle: "fitting square data into round models" +author: "[Sally Thompson](mailto:sally.thompson37@nhs.net)" +date: 2023-10-09 +date-format: "DD MMMM YYYY" +format: + + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + self-contained: true + + preview-links: auto + slide-number: false + auto-animate: true + width: 1600 + height: 900 + + footer: | + NHS-R conference 2023 + +execute: + eval: true + echo: true + message: false + warning: false +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + +```{r setup} +#| include: false +#| label: setup + +library(tidyr) +library(dplyr) +library(readr) +library(lubridate) +library(tibble) +library(stringr) + +file_name <- "https://raw.githubusercontent.com/microsoft/r-server-hospital-length-of-stay/master/Data/LengthOfStay.csv" + +data <- read_csv(file = file_name) + +spell_dates <- data |> + mutate(date_admit = mdy(vdate) + years(10), + date_discharge = mdy(discharged) + years(10)) |> + select(facid, date_admit, date_discharge) |> + sample_n(10000) + +``` + +## Health Data in the Headlines + + +![](images/headline%20waitlist%20record.png){.absolute top=75 left=0 fig-alt="newspaper headline waiting list hits record 7.5 million"} + + + +![](images/headline%20ambulance%20ae.png){.absolute top=200 right=0 fig-alt="news headline linking ambulance response times to to A&E waits"} + + + +![](images/headline%20bed%20block.png){.absolute bottom=40 left=275 fig-alt="news headline links delays in discharging people from hospital to full A&E departments"} + +::: {.notes} +Used to seeing headlines that give a snapshot figure but doesn't say much about the system. +Now starting to see headlines that recognise flow through the system rather than snapshot in time of just one part. +Can get better understanding of the issues in a system if we can map it as stocks +and flows, but our datasets not designed to give up this information very readily. +This talk is how I have tried to meet that challenge. + +::: + + +## Through the System Dynamics lens + +![](images/cap%20constrained%20model%20transp.png){fig-alt="stock-flow structure of a simple capacity-constrained model"} + +Stock-flow model + +Dynamic behaviour, feedback loops + +::: {.notes} +In a few seconds, what is SD? +An approach to understanding the behaviour of complex systems over time. +A method of mapping a system as stocks, whose levels can only change due to flows in and flows out. Stocks could be people on a waiting list, on a ward, money, ... +Flows are the rate at which things change in a given time period e.g. admissions per day, referrals +per month. +Behaviour of the system is determined by how the components interact with each other, not what +each component does. +Mapping the structure of a system like this leads us to identify feedback loops, +and consequences of an action - both intended and unintended. + +In this capacity-constrained model we only need 3 parameters to run the model (exogenous). All the behaviour +within the grey box is determined by the interactions of those components (indogenous). + +How do we get a value/values for referrals per day? + +(currently use specialist software to build and run our models, aim is to get to a point where we can run +in open source.) +::: + + +## Determining flows + +![](images/adm%20dis%20model%20transp.png){.absolute top="55" right="10" fig-alt="One stock model, with one flow in and one flow out - the flow in, labelled as 'admissions per day', is highlighted." width="800"} + +::: columns +::: {.column width="40%"} +::: {style="font-size:0.65em"} +'admissions per day' is needed to populate the model. + +'discharged' could be used to verify the model against known data + +- How many admissions per day (or week, month...) +::: +::: +::: + +::: incremental +![](images/key%20dates.png){.fragment .absolute bottom="110" left="0" fig-alt="list of first 10 key dates, starting on January 1st 2022." width="153"} +::: + +::: r-stack + ![](images/pat%20dates.png){.fragment .absolute bottom="160" left="340" width="458"} ![](images/admissions.png){.fragment .absolute bottom="110" left="850" width="205"} + ![](images/wk admissions.png){.fragment .absolute bottom="275" left="1100" width="205"} +::: + +::: {.notes} +Going to use very simple model shown to explain how to extract flow data for admissions. +Will start with visual explainer before going into the code. +1. generate list of key dates (in this case daily, could be weekly, monthly) +2. take our patient-level ID with admission and discharge dates +3. count of admissions on that day/week +::: + +## Determining occupancy {.smaller} + +![](images/adm%20dis%20model%20stock%20trans.png){.absolute top="55" right="10" fig-alt="One stock model, with one flow in and one flow out - the stock, labelled as 'on ward', is highlighted." width="800"} + +::: columns +::: {.column width="40%"} + + +'on ward' is used to verify the model against known data + +- Logic statement testing if the key date is wholly between admission and discharge dates +- flag for a match +::: +::: + +::: incremental +![](images/key%20dates.png){.absolute bottom="110" left="50" fig-alt="List of key dates, " width="153"} +::: + +::: r-stack +![](images/pat%20dates.png){.fragment .absolute bottom="148" left="328" width="458"} +![](images/occ%201.png){.fragment .absolute bottom="160" left="330" width="621"} +![](images/occ%202.png){.fragment .absolute bottom="160" left="330" width="756"} +![](images/occ%203.png){.fragment .absolute bottom="160" left="330" width="900"} +![](images/occ%204.png){.fragment .absolute bottom="160" left="330" width="1034"} +![](images/occupancy.png){.fragment .absolute bottom="110" right="0" width="209"} +::: + +::: {.notes} +Might also want to generate occupancy, to compare the model output with actual data +to verify/validate. +1. generate list of key dates +2. take our patient-level ID with admission and discharge dates +3. going to take each date in our list of keydates, and see if there is an admission before that date +and discharge after +4. this creates a wide data frame, the same length as patient data. +5. once run through all the dates in the list, sum each column + +Patient A admitted on 2nd, so only starts being classed as resident on 3rd. +::: + + + +## in R - flows + +Easy to do with `count`, or `group_by` and `summarise` + + + +```{r} +#| output-location: column-fragment + + admit_d <- spell_dates |> + group_by(date_admit) |> + count(date_admit) + +head(admit_d) + +``` + + + +## in R - occupancy + +Generate list of key dates + +::: columns + + +::: {.column width="65%"} +```{r} + +date_start <- dmy(01012022) +date_end <- dmy(31012022) +run_len <- length(seq(from = date_start, to = date_end, by = "day")) + +keydates <- data.frame( + date = c(seq(date_start, by = "day", length.out=run_len))) + + +``` +::: + +::: {.column width="35%"} +```{r} +#| echo: false +head(keydates) +``` +::: +::: + +::: {.notes} +Start by generating the list of keydates. In this example we're running the model +in days, and checking each day in 2022. +Need the run length for the next step, to know how many times to iterate over + +::: + +## in R - occupancy + +Iterate over each date - need to have been admitted before, and discharged after + + +```{r} +#| eval: false + +occupancy_flag <- function(df) { + + # pre-allocate tibble size to speed up iteration in loop + activity_all <- tibble(nrow = nrow(df)) |> + select() + + for (i in 1:run_len) { + + activity_period <- case_when( + + # creates 1 flag if resident for complete day + df$date_admit < keydates$keydate[i] & + df$date_discharge > keydates$keydate[i] ~ 1, + TRUE ~ 0) + + # column bind this day's flags to previous + activity_all <- bind_cols(activity_all, activity_period) + + } + + # rename column to match the day being counted + activity_all <- activity_all |> + setNames(paste0("d_", keydates$date)) + + # bind flags columns to patient data + daily_adm <- bind_cols(df, activity_all) |> + pivot_longer( + cols = starts_with("d_"), + names_to = "date", + values_to = "count" + ) |> + + group_by(date) |> + summarise(resident = sum(count)) |> + ungroup() |> + mutate(date = str_remove(date, "d_")) + + } + + +``` + + +. . . + +Is there a better way than using a `for` loop? + +::: {.notes} +Pre-allocate tibbles + +activity_all will end up as very wide tibble, with a column for each date in list of keydates. + +For each date in the list of key dates, compares with admission date & discharge date; need to be admitted before the key date and discharged after the key date. If match, flag = 1. +Creates a column for each day, then binds this to activity all. +Rename each column with the date it was checking (add a character to start of column name so column doesn't start with numeric) +Pivot long, then group by date and sum the flags (other variables could be added here, such as TFC or provider code) +::: + +## Longer Time Periods - flows + +Use `lubridate::floor_date` to generate the date at start of week/month +```{r} +#| output-location: fragment + +admit_wk <- spell_dates |> + mutate(week_start = floor_date( + date_admit, unit = "week", week_start = 1 # start week on Monday + )) |> + count(week_start) # could add other parameters such as provider code, TFC etc + +head(admit_wk) + +``` + + +::: {.notes} +Might run SD model in weeks or months - e.g. months for care homes +Use lubridate to create new variable with start date of week/month/year etc + +::: + + +## Longer Time Periods - occupancy + +Key dates to include the dates at the start and end of each time period + +::: columns +::: {.column width="65%"} +```{r} + +date_start <- dmy(03012022) # first Monday of the year +date_end <- dmy(01012023) +run_len <- length(seq(from = date_start, to = date_end, by = "week")) + +keydates <- data.frame(wk_start = c(seq(date_start, + by = "week", + length.out=run_len))) |> + mutate( + wk_end = wk_start + 6) # last date in time period + +``` +::: + +::: {.column width="35%"} +```{r} +#| echo: false + +head(keydates) +``` +::: +::: + + +::: {.notes} +Model might make more sense to run in weeks or months (e.g. care home), so list of keydates need a start date and end date for each time period. +::: + +## Longer Time Periods + +More logic required if working in weeks or months - can only be in one place at any given time + +```{r} +#| eval: false + + +# flag for occupancy +activity_period <- case_when( + + # creates 1 flag if resident for complete week + df$date_admit < keydates$wk_start[i] & df$date_discharge > keydates$wk_end[i] ~ 1, + TRUE ~ 0) +``` + +::: {.notes} +And a little bit more logic +Occupancy requires the patient to have been admitted before the start of the week/month, and discharged after the end of the week/month +::: + + +## Applying the data + +![](images/interface%20model%20transp.png){fig-alt="One stock model, with one flow in and one flow out - the stock has a time chart overlaid showing model output compared to 'real' data"} + +::: {.notes} +How to apply this wrangling of data to the system dynamic model? +Admissions data used as an input to the flow - could be reduced to a single figure (average), or there may be variation by season/day of week etc. +Occupancy (and discharges) used to verify the model output against known data. +::: + + + + + + + + +## Next Steps + +- Generalise function to a state where it can be used by others - onto Github + +- Turn this into a package + +- Open-source SD models and interfaces - R Shiny or Python + +## Questions, comments, suggestions? + +| +| +| +| Please get in touch! +| +| [Sally.Thompson37\@nhs.net](mailto:sally.thompson37@nhs.net) + diff --git a/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/renv.lock b/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/renv.lock new file mode 100644 index 0000000..9cf01ab --- /dev/null +++ b/presentations/2023-10-09_nhs-r_conf_sd_in_health_social_care/renv.lock @@ -0,0 +1,734 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" + }, + "bit64": { + "Package": "bit64", + "Version": "4.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "e84984bf5f12a18628d9a02322128dfd" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "91570bba75d0c9d3f1040c835cee8fba" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2023-10-17_conference-check-in-app/index.qmd b/presentations/2023-10-17_conference-check-in-app/index.qmd index 043c641..18f452a 100644 --- a/presentations/2023-10-17_conference-check-in-app/index.qmd +++ b/presentations/2023-10-17_conference-check-in-app/index.qmd @@ -1,143 +1,149 @@ ---- -title: "Conference Check-in App" -subtitle: "NHS-R/NHS.pycom 2023" -author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" -date: 2023-10-17 -date-format: "MMM D, YYYY" -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [https://tinyurl.com/nhsr23tj][ds_presentations] ---- - -[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-10-17_conference-check-in-app - -# or, on why you should [ignore]{.yellow} your boss, [play]{.yellow} about, and [have fun]{.yellow} {.inverse} - -## {background-image="1.jpg"} - -:::{.footer} -[digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/](https://digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/) -::: - -:::{.notes} - Clark, Junebug. [Registration Desk for the LPC Conference], photograph, 2016-03-17/2016-03-19; (https://digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/: accessed October 16, 2023), University of North Texas Libraries, UNT Digital Library, https://digital.library.unt.edu; crediting UNT Libraries Special Collections. -::: - -# {background-image="2.jpg"} - -:::{.footer} -[unsplash.com/photos/MldQeWmF2_g](https://unsplash.com/photos/MldQeWmF2_g) -::: - -# Can we not do better? - -## QR codes are great - -![](3.png){fig-align="center"} - -## and can be easily generated in R - -```r -install.packages("qrcode") -library(qrcode) - -qr_code("https://www.youtube.com/watch?v=dQw4w9WgXcQ") -``` - -# But can we build a shiny app to read QR codes? {.inverse} - -# No (probably) - -## Why not? - -- `{shiny}` would be doing all the processing on the server side -- we would need to read from a camera client side -- then stream video to the server for `{shiny}` to detect and decode the QR codes - -# Well, can we do it client side? {.inverse} - -# Yes - -. . . - -[github.com/nhs-r-community/conf-23-checkin](https://github.com/nhs-r-community/conf-23-checkin) - -## How does this work? - -:::{.columns} - -::::{.column width=70%} -### Front-end -:::{.incremental} -- uses the [React](https://react.dev) JavaScript framework -- [@yidel/react-qr-scanner](https://github.com/yudielcurbelo/react-qr-scanner) -- App scan's a QR code, then sends this to our backend -- A window pops up to say who has checked in, or shows an error message -::: -:::: - -::::{.column width=30%} -![](4.png) -:::: - -::: - -## How does this work? - -### Back-end - -Uses the `{plumber}` R package to build the API, with endpoints for - -- getting the list of all of the attendees for that day -- uploading a list of attendees in bulk -- adding an attendee individually -- getting an attendee -- checking the attendee in - - -## How does this work? - -### More Back-end Stuff - -- uses a simple SQLite DB that will be thrown away at the end of the conference -- we send personalised emails using `{blastula}` to the attendees with their QR codes -- the QR codes are just random ids (UUIDs) that identify each attendee -- uses websockets to update all of the clients when a user checks in (to update the list of attendees) - -# I've wanted to play about with React for a while... {.inverse} - -. . . - -This was a silly, inconsequential project to get to grips with something new - -# Was it worth it? {.inverse} - -. . . - -Yes! - -# {background-image=5.jpg} - -:::{.footer} -[unsplash.com/photos/WfUxLpncYwI](https://unsplash.com/photos/WfUxLpncYwI) -::: - -## Learning different tools can show you the light {background-image=6.jpg} - -:::{.footer} -[unsplash.com/photos/tMGMINwFOtI](https://unsplash.com/photos/tMGMINwFOtI) -::: - -# Go away, learn something new {.inverse} - -Thanks! - -[thomas.jemmett@nhs.net](mailto:thomas.jemmett@nhs.net) \ No newline at end of file +--- +title: "Conference Check-in App" +subtitle: "NHS-R/NHS.pycom 2023" +author: "[Tom Jemmett](mailto:thomas.jemmett@nhs.net)" +date: 2023-10-17 +date-format: "MMM D, YYYY" +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [https://tinyurl.com/nhsr23tj][ds_presentations] +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +[ds_presentations]: https://the-strategy-unit.github.io/data_science/presentations/2023-10-17_conference-check-in-app + +# or, on why you should [ignore]{.yellow} your boss, [play]{.yellow} about, and [have fun]{.yellow} {.inverse} + +## {background-image="1.jpg"} + +:::{.footer} +[digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/](https://digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/) +::: + +:::{.notes} + Clark, Junebug. [Registration Desk for the LPC Conference], photograph, 2016-03-17/2016-03-19; (https://digital.library.unt.edu/ark:/67531/metadc1039451/m1/1/: accessed October 16, 2023), University of North Texas Libraries, UNT Digital Library, https://digital.library.unt.edu; crediting UNT Libraries Special Collections. +::: + +# {background-image="2.jpg"} + +:::{.footer} +[unsplash.com/photos/MldQeWmF2_g](https://unsplash.com/photos/MldQeWmF2_g) +::: + +# Can we not do better? + +## QR codes are great + +![](3.png){fig-align="center"} + +## and can be easily generated in R + +```r +install.packages("qrcode") +library(qrcode) + +qr_code("https://www.youtube.com/watch?v=dQw4w9WgXcQ") +``` + +# But can we build a shiny app to read QR codes? {.inverse} + +# No (probably) + +## Why not? + +- `{shiny}` would be doing all the processing on the server side +- we would need to read from a camera client side +- then stream video to the server for `{shiny}` to detect and decode the QR codes + +# Well, can we do it client side? {.inverse} + +# Yes + +. . . + +[github.com/nhs-r-community/conf-23-checkin](https://github.com/nhs-r-community/conf-23-checkin) + +## How does this work? + +:::{.columns} + +::::{.column width=70%} +### Front-end +:::{.incremental} +- uses the [React](https://react.dev) JavaScript framework +- [@yidel/react-qr-scanner](https://github.com/yudielcurbelo/react-qr-scanner) +- App scan's a QR code, then sends this to our backend +- A window pops up to say who has checked in, or shows an error message +::: +:::: + +::::{.column width=30%} +![](4.png) +:::: + +::: + +## How does this work? + +### Back-end + +Uses the `{plumber}` R package to build the API, with endpoints for + +- getting the list of all of the attendees for that day +- uploading a list of attendees in bulk +- adding an attendee individually +- getting an attendee +- checking the attendee in + + +## How does this work? + +### More Back-end Stuff + +- uses a simple SQLite DB that will be thrown away at the end of the conference +- we send personalised emails using `{blastula}` to the attendees with their QR codes +- the QR codes are just random ids (UUIDs) that identify each attendee +- uses websockets to update all of the clients when a user checks in (to update the list of attendees) + +# I've wanted to play about with React for a while... {.inverse} + +. . . + +This was a silly, inconsequential project to get to grips with something new + +# Was it worth it? {.inverse} + +. . . + +Yes! + +# {background-image=5.jpg} + +:::{.footer} +[unsplash.com/photos/WfUxLpncYwI](https://unsplash.com/photos/WfUxLpncYwI) +::: + +## Learning different tools can show you the light {background-image=6.jpg} + +:::{.footer} +[unsplash.com/photos/tMGMINwFOtI](https://unsplash.com/photos/tMGMINwFOtI) +::: + +# Go away, learn something new {.inverse} + +Thanks! + +[thomas.jemmett@nhs.net](mailto:thomas.jemmett@nhs.net) diff --git a/presentations/2023-10-17_conference-check-in-app/renv.lock b/presentations/2023-10-17_conference-check-in-app/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2023-10-17_conference-check-in-app/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-01-25_coffee-and-coding/index.qmd b/presentations/2024-01-25_coffee-and-coding/index.qmd index 8055954..ccf5c53 100644 --- a/presentations/2024-01-25_coffee-and-coding/index.qmd +++ b/presentations/2024-01-25_coffee-and-coding/index.qmd @@ -1,148 +1,154 @@ ---- -title: "Coffee and Coding" -subtitle: "Making my analytical workflow more reproducible with {targets}" -author: "[Jacqueline Grout](mailto:jacqueline.grout1@nhs.net)" -date: 2024-01-25 -date-format: "MMM D, YYYY" -knitr: - opts_chunk: - eval: false - echo: true -format: - revealjs: - incremental: true - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations](the-strategy-unit.github.io/data_science/presentations) - width: 1920 - height: 1080 -editor: - markdown: - wrap: 120 ---- - -## `{targets}` for analysts - -::: columns -::: {.column width="50%"} - -- Tom previously presented about {targets} at a coffee and coding last March and you can revisit his presentation and learn about the reasons why you should use the package to manage your pipeline and see a simple demonstration of how to use the package. -- Matt has presented previously about {targets} and making your workflows (pipelines) reproducible. -- So..... if you aren't really even sure why your pipeline needs managing as an analyst or whether you actually have one (you do) then links to their presentations are at the end - - - -::: - -::: {.column width="50%"} -![](logo.png) -::: -::: - -## Aims - -> In this presentation we aim to demonstrate the real-world use of {targets} in an analysis project, but first a brief -> explanation - -::: fragment -### Without {targets} we -::: - - -- Write a script -- Execute script -- Make changes -- Go to step 2 - - -::: fragment -### With {targets} we will -::: - - -- learn how the various stages of our analysis fit together -- save time by only running necessary stages as we cycle through the process -- help future you and colleagues re-visiting the analysis - Matt says "its like a time-capsule" -- make Reproducible Analytical Pipelines - - -::: footer -source: [The `{targets}` R package user manual](https://books.ropensci.org/targets/) -::: - -## Explain the live project - - -- original project had 30+ metrics -- multiple inter-related processing steps -- each time a metric changed or a process was altered it impacted across the project -- there was potential for mistakes, duplication, lots of wasted time -- using targets provides a structure that handles these inter-relationships - - -## How {targets} can help - - -- gets you thinking about your analysis and its building blocks -- targets forces you into a functions approach to workflow -- entire pipeline is reproducible -- visualise on one page -- saves time -- (maybe we need an advanced function writing session in another C&C?) - - -## Demonstration in a live project - -[Let's look at a real life example in a live project...](https://github.com/The-Strategy-Unit/bhf_chd-ethnicity) - -## Visualising - -Current project in {targets} and visualised with tar_visnetwork() - -::: columns -::: {.column width="30%"} -![](vis1.png){fig-alt="A directed graph made with the targets R package where each node is a function or object and arrows between them indicate their dependencies." width="40%"} -::: -::: {.column width="20%"} -![](vis3.png){fig-alt="Legend for graph made with the targets R package" width="30%"} -::: - -::: {.column width="50%"} -![](vis2.png){fig-alt="A directed graph made with the targets R package where each node is a function or object and arrows between them indicate their dependencies. A node has been selected, highlighting all its upstream nodes." width="80%"} -::: -::: - -## Code - -- it's like a recipe of steps -- it's easier to read -- you have built functions which you can transfer and reuse -- it's efficient, good practice -- debugging is easier because if/when it fails you know exactly which target it has failed on -- it creates intermediate cached objects you can fetch at any time - - - -## How can I start using it? - -- You could "retro-fit" it to your project, but ... ideally you should start your project off using {targets} -- There are at least three of us in SU who have used it in our projects. -- We are offering to hand hold you to get started with your next project. -- Matt, Tom, Jacqueline - -## Useful {targets} links - -- [Tom's previous coffee and coding presentation](https://the-strategy-unit.github.io/data_science/presentations/2023-03-23_coffee-and-coding/#/title-slide) -- [Matt's previous presentations](https://www.rostrum.blog/posts/2020-09-27-targets-dsfest/) -- The [{targets} documentation](https://books.ropensci.org/targets/walkthrough.html) is detailed and easy to follow. -- [A demo repository demonstrated in last weeks NHSE C&C](https://github.com/Pablo-source/targets-test) -- [Software Carpentry](https://software-carpentry.org/) are developing a course here [Pre-alpha targets course](https://multimeric.github.io/targets-workshop/instructor/index.html) -- [Live project demonstrated in this presentation using {targets}](https://github.com/The-Strategy-Unit/bhf_chd-ethnicity) - - +--- +title: "Coffee and Coding" +subtitle: "Making my analytical workflow more reproducible with {targets}" +author: "[Jacqueline Grout](mailto:jacqueline.grout1@nhs.net)" +date: 2024-01-25 +date-format: "MMM D, YYYY" +knitr: + opts_chunk: + eval: false + echo: true +format: + revealjs: + incremental: true + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations](the-strategy-unit.github.io/data_science/presentations) + width: 1920 + height: 1080 +editor: + markdown: + wrap: 120 +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +## `{targets}` for analysts + +::: columns +::: {.column width="50%"} + +- Tom previously presented about {targets} at a coffee and coding last March and you can revisit his presentation and learn about the reasons why you should use the package to manage your pipeline and see a simple demonstration of how to use the package. +- Matt has presented previously about {targets} and making your workflows (pipelines) reproducible. +- So..... if you aren't really even sure why your pipeline needs managing as an analyst or whether you actually have one (you do) then links to their presentations are at the end + + + +::: + +::: {.column width="50%"} +![](logo.png) +::: +::: + +## Aims + +> In this presentation we aim to demonstrate the real-world use of {targets} in an analysis project, but first a brief +> explanation + +::: fragment +### Without {targets} we +::: + + +- Write a script +- Execute script +- Make changes +- Go to step 2 + + +::: fragment +### With {targets} we will +::: + + +- learn how the various stages of our analysis fit together +- save time by only running necessary stages as we cycle through the process +- help future you and colleagues re-visiting the analysis - Matt says "its like a time-capsule" +- make Reproducible Analytical Pipelines + + +::: footer +source: [The `{targets}` R package user manual](https://books.ropensci.org/targets/) +::: + +## Explain the live project + + +- original project had 30+ metrics +- multiple inter-related processing steps +- each time a metric changed or a process was altered it impacted across the project +- there was potential for mistakes, duplication, lots of wasted time +- using targets provides a structure that handles these inter-relationships + + +## How {targets} can help + + +- gets you thinking about your analysis and its building blocks +- targets forces you into a functions approach to workflow +- entire pipeline is reproducible +- visualise on one page +- saves time +- (maybe we need an advanced function writing session in another C&C?) + + +## Demonstration in a live project + +[Let's look at a real life example in a live project...](https://github.com/The-Strategy-Unit/bhf_chd-ethnicity) + +## Visualising + +Current project in {targets} and visualised with tar_visnetwork() + +::: columns +::: {.column width="30%"} +![](vis1.png){fig-alt="A directed graph made with the targets R package where each node is a function or object and arrows between them indicate their dependencies." width="40%"} +::: +::: {.column width="20%"} +![](vis3.png){fig-alt="Legend for graph made with the targets R package" width="30%"} +::: + +::: {.column width="50%"} +![](vis2.png){fig-alt="A directed graph made with the targets R package where each node is a function or object and arrows between them indicate their dependencies. A node has been selected, highlighting all its upstream nodes." width="80%"} +::: +::: + +## Code + +- it's like a recipe of steps +- it's easier to read +- you have built functions which you can transfer and reuse +- it's efficient, good practice +- debugging is easier because if/when it fails you know exactly which target it has failed on +- it creates intermediate cached objects you can fetch at any time + + + +## How can I start using it? + +- You could "retro-fit" it to your project, but ... ideally you should start your project off using {targets} +- There are at least three of us in SU who have used it in our projects. +- We are offering to hand hold you to get started with your next project. +- Matt, Tom, Jacqueline + +## Useful {targets} links + +- [Tom's previous coffee and coding presentation](https://the-strategy-unit.github.io/data_science/presentations/2023-03-23_coffee-and-coding/#/title-slide) +- [Matt's previous presentations](https://www.rostrum.blog/posts/2020-09-27-targets-dsfest/) +- The [{targets} documentation](https://books.ropensci.org/targets/walkthrough.html) is detailed and easy to follow. +- [A demo repository demonstrated in last weeks NHSE C&C](https://github.com/Pablo-source/targets-test) +- [Software Carpentry](https://software-carpentry.org/) are developing a course here [Pre-alpha targets course](https://multimeric.github.io/targets-workshop/instructor/index.html) +- [Live project demonstrated in this presentation using {targets}](https://github.com/The-Strategy-Unit/bhf_chd-ethnicity) + + diff --git a/presentations/2024-01-25_coffee-and-coding/renv.lock b/presentations/2024-01-25_coffee-and-coding/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2024-01-25_coffee-and-coding/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-05-16_store-data-safely/index.qmd b/presentations/2024-05-16_store-data-safely/index.qmd index f4a6e86..9a0a0a6 100644 --- a/presentations/2024-05-16_store-data-safely/index.qmd +++ b/presentations/2024-05-16_store-data-safely/index.qmd @@ -19,6 +19,11 @@ format: Learn more about [Data Science at The Strategy Unit ](https://the-strategy-unit.github.io/data_science/) --- +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + # Avoid storing data on GitHub {.inverse} ## Why? @@ -262,4 +267,4 @@ AzureStor::delete_storage_file(container, BLOB_NAME) * Data is not in the repository, it is instead stored in a secure location * Code can be open – sensitive information like Azure container name stored as environment variables * Large filesizes possible, other people can also access the same container. -* Naming conventions can help to keep blobs organised (these create pseudo-folders) +* Naming conventions can help to keep blobs organised (these create pseudo-folders) \ No newline at end of file diff --git a/presentations/2024-05-16_store-data-safely/renv.lock b/presentations/2024-05-16_store-data-safely/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2024-05-16_store-data-safely/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-05-23_github-team-sport/index.qmd b/presentations/2024-05-23_github-team-sport/index.qmd index 2bad44e..116f95e 100644 --- a/presentations/2024-05-23_github-team-sport/index.qmd +++ b/presentations/2024-05-23_github-team-sport/index.qmd @@ -17,6 +17,12 @@ format: Learn more about [The Strategy Unit](https://www.strategyunitwm.nhs.uk/) --- +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + ## tl;dr :::: {.columns} diff --git a/presentations/2024-05-23_github-team-sport/renv.lock b/presentations/2024-05-23_github-team-sport/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2024-05-23_github-team-sport/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-05-30_open-source-licensing/index.qmd b/presentations/2024-05-30_open-source-licensing/index.qmd index f1dc662..f267c1f 100644 --- a/presentations/2024-05-30_open-source-licensing/index.qmd +++ b/presentations/2024-05-30_open-source-licensing/index.qmd @@ -1,153 +1,159 @@ ---- -title: "Open source licensing" -subtitle: "Or: how I learned to stop worrying and love openness" -author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" -date: 2024-05-30 -date-format: "MMM D, YYYY" -knitr: - opts_chunk: - eval: false - echo: true -format: - revealjs: - theme: [default, ../su_presentation.scss] - transition: none - chalkboard: - buttons: false - preview-links: auto - slide-number: false - auto-animate: true - footer: | - view slides at [the-strategy-unit.github.io/data_science/presentations](the-strategy-unit.github.io/data_science/presentations) - width: 1920 - height: 1080 -editor: - markdown: - wrap: 120 ---- - -## A note on Richard Stallman - -* Richard Stallman has been [heavily criticised for some of this views](https://drewdevault.com/2023/11/25/2023-11-26-RMS-on-sex.html) -* He is hard to ignore when talking about open source so I am going to talk about him -* Nothing in this talk should be read as endorsing any of his comments outside (or inside) the world of open source - -## The origin of open source - -* In the 50s and 60s source code was routinely shared with hardware and users were often expected to modify to run on their hardware -* By the late 1960s the production cost of software was rising relative to hardware and proprietary licences became more prevalent -* In 1980 Richard Stallman's department at MIT took delivery of a printer they were not able to modify the source code for -* Richard Stallman launched the GNU project in 1983 to fight for software freedoms -* MIT licence was launched in the late 1980s -* [Cathedral and the bazaar](http://www.catb.org/esr/writings/cathedral-bazaar/) was released in 1997 (more on which later) - -## What is open source? - -> Think free as in free speech, not free beer (Stallman) - -* Open source does not mean free of charge! Software freedom implies the ability to sell code -* Free of charge does not mean open source! Many free to download pieces of software are not open source (Zoom, for example) - -![](software_diagram.svg) - -By Chao-Kuei et al. - https://www.gnu.org/philosophy/categories.en.html, GPL, Link - -## The four freedoms - -* Freedom 0: The freedom to use the program for any purpose. -* Freedom 1: The freedom to study how the program works, and change it to make it do what you wish. -* Freedom 2: The freedom to redistribute and make copies so you can help your neighbor. -* Freedom 3: The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits. - -## Cathedral and the bazaar - -* Every good work of software starts by scratching a developer's personal itch. -* Good programmers know what to write. Great ones know what to rewrite (and reuse). -* Plan to throw one [version] away; you will, anyhow (copied from Frederick Brooks's The Mythical Man-Month). -* If you have the right attitude, interesting problems will find you. -* When you lose interest in a program, your last duty to it is to hand it off to a competent successor. -* Treating your users as co-developers is your least-hassle route to rapid code improvement and effective debugging. -* Release early. Release often. And listen to your customers. -* Given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone. -* Smart data structures and dumb code works a lot better than the other way around. -* If you treat your beta-testers as if they're your most valuable resource, they will respond by becoming your most valuable resource. - -## Cathedral and the bazaar (cont.) - -* The next best thing to having good ideas is recognizing good ideas from your users. Sometimes the latter is better. -* Often, the most striking and innovative solutions come from realizing that your concept of the problem was wrong. -* Perfection (in design) is achieved not when there is nothing more to add, but rather when there is nothing more to take away. (Attributed to Antoine de Saint-Exupéry) -* Any tool should be useful in the expected way, but a truly great tool lends itself to uses you never expected. -* When writing gateway software of any kind, take pains to disturb the data stream as little as possible—and never throw away information unless the recipient forces you to! -* When your language is nowhere near Turing-complete, syntactic sugar can be your friend. -* A security system is only as secure as its secret. Beware of pseudo-secrets. -* To solve an interesting problem, start by finding a problem that is interesting to you. -* Provided the development coordinator has a communications medium at least as good as the Internet, and knows how to lead without coercion, many heads are inevitably better than one. - -## The disciplines of open source are the disciplines of good data science - -* Meaningful README -* Meaningful commit messages -* Modularity -* Separating data code from analytic code from interactive code -* Assigning issues and pull requests for action/ review -* Don't forget one of the most lazy and incompetent developers you will ever work with is yourself, six months later - -## What licences exist? - -* Permissive - * Such as MIT but there are others. Recommended by NHSX [draft guidelines on open source](https://github.com/nhsx/open-source-policy) - * Apache is a notable permissive licence- includes a patent licence - * In our work the [OGL](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) is also relevant- civil servant publish stuff under OGL (and MIT- it isn't particularly recommended for code) -* Copyleft - * GPL2, GPL3, AGPL [("the GPL of the web")](https://www.tldrlegal.com/license/gnu-affero-general-public-license-v3-agpl-3-0) - * Note that the provisions of the GPL only apply when you **distribute** the code - * At a certain point it all gets too complicated and you need a lawyer - * MPL is a notable copyleft licence- can combine with proprietary code as long as kept separate -* Arguments for permissive/ copyleft- getting your code used versus preserving software freedoms for other people -* Note that most of the licences are impossible to read! There is a website to explain [tl;dr](https://www.tldrlegal.com/) - -## What is copyright and why does it matter - -* Copyright is assigned at the moment of creation -* If you made it in your own time, it's yours (usually!) -* If you made it at work, it belongs to your employer -* If someone paid you to make it ("work for hire") it belongs to them -* Crucially, the copyright holder can relicence software - * If it's jointly authored it depends if it's a "collective" or "joint" work - * Honestly it's pretty complicated. Just vest copyright in an organisation or group of individuals you trust - * Goldacre review suggests using Crown copyright for copyright in the NHS because it's a "shoal, not a big fish" (with apologies to Ben whom I am misquoting) - -## Iceweasel - -* Iceweasel is a story of **trademark** rather than copyright -* Debian (a Linux flavour) had the permission to use the source code of Firefox, but not the logo -* So they took the source code and made their own version -* This sounds very obscure and unimportant but it could become important in future projects of ours, like... - -## What we have learned in recent projects - -* The huge benefits of being open - * Transparency - * Working with customers - * Goodwill -* Nonfree mitigators -* Different licences for different repos - -## Software freedom means allowing people to do stuff you don't like - -* Freedom 0: The freedom to use the program for any purpose. -* Freedom 3: The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits. -* The code isn't the only thing with worth in the project -* This is why there are whole businesses founded on "here's the Linux source code" -* So when we're sharing code we are **letting** people do stupid things with it but we're not **recommending** that they do stupid things with it -* People do stupid things with Excel and Microsoft don't accept liability for that, and neither should we -* This issue of sharing analytic code and merchantability for a particular purpose is poorly understood and I think everyone needs to be clearer on it (us, and our customers) -* In my view a world where consultants are selling **our** code is better than a world where they're selling **their** spreadsheets - -## "Open source as in piano" - -* The patient experience QDC project -* Our current project -* Open source code is not necessarily to be run, but understood and learned from -* Building a group of people who can use and contribute to your code is arguably as important as writing it +--- +title: "Open source licensing" +subtitle: "Or: how I learned to stop worrying and love openness" +author: "[Chris Beeley](mailto:chris.beeley1@nhs.net)" +date: 2024-05-30 +date-format: "MMM D, YYYY" +knitr: + opts_chunk: + eval: false + echo: true +format: + revealjs: + theme: [default, ../su_presentation.scss] + transition: none + chalkboard: + buttons: false + preview-links: auto + slide-number: false + auto-animate: true + footer: | + view slides at [the-strategy-unit.github.io/data_science/presentations](the-strategy-unit.github.io/data_science/presentations) + width: 1920 + height: 1080 +editor: + markdown: + wrap: 120 +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +## A note on Richard Stallman + +* Richard Stallman has been [heavily criticised for some of this views](https://drewdevault.com/2023/11/25/2023-11-26-RMS-on-sex.html) +* He is hard to ignore when talking about open source so I am going to talk about him +* Nothing in this talk should be read as endorsing any of his comments outside (or inside) the world of open source + +## The origin of open source + +* In the 50s and 60s source code was routinely shared with hardware and users were often expected to modify to run on their hardware +* By the late 1960s the production cost of software was rising relative to hardware and proprietary licences became more prevalent +* In 1980 Richard Stallman's department at MIT took delivery of a printer they were not able to modify the source code for +* Richard Stallman launched the GNU project in 1983 to fight for software freedoms +* MIT licence was launched in the late 1980s +* [Cathedral and the bazaar](http://www.catb.org/esr/writings/cathedral-bazaar/) was released in 1997 (more on which later) + +## What is open source? + +> Think free as in free speech, not free beer (Stallman) + +* Open source does not mean free of charge! Software freedom implies the ability to sell code +* Free of charge does not mean open source! Many free to download pieces of software are not open source (Zoom, for example) + +![](software_diagram.svg) + +By Chao-Kuei et al. - https://www.gnu.org/philosophy/categories.en.html, GPL, Link + +## The four freedoms + +* Freedom 0: The freedom to use the program for any purpose. +* Freedom 1: The freedom to study how the program works, and change it to make it do what you wish. +* Freedom 2: The freedom to redistribute and make copies so you can help your neighbor. +* Freedom 3: The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits. + +## Cathedral and the bazaar + +* Every good work of software starts by scratching a developer's personal itch. +* Good programmers know what to write. Great ones know what to rewrite (and reuse). +* Plan to throw one [version] away; you will, anyhow (copied from Frederick Brooks's The Mythical Man-Month). +* If you have the right attitude, interesting problems will find you. +* When you lose interest in a program, your last duty to it is to hand it off to a competent successor. +* Treating your users as co-developers is your least-hassle route to rapid code improvement and effective debugging. +* Release early. Release often. And listen to your customers. +* Given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone. +* Smart data structures and dumb code works a lot better than the other way around. +* If you treat your beta-testers as if they're your most valuable resource, they will respond by becoming your most valuable resource. + +## Cathedral and the bazaar (cont.) + +* The next best thing to having good ideas is recognizing good ideas from your users. Sometimes the latter is better. +* Often, the most striking and innovative solutions come from realizing that your concept of the problem was wrong. +* Perfection (in design) is achieved not when there is nothing more to add, but rather when there is nothing more to take away. (Attributed to Antoine de Saint-Exupéry) +* Any tool should be useful in the expected way, but a truly great tool lends itself to uses you never expected. +* When writing gateway software of any kind, take pains to disturb the data stream as little as possible—and never throw away information unless the recipient forces you to! +* When your language is nowhere near Turing-complete, syntactic sugar can be your friend. +* A security system is only as secure as its secret. Beware of pseudo-secrets. +* To solve an interesting problem, start by finding a problem that is interesting to you. +* Provided the development coordinator has a communications medium at least as good as the Internet, and knows how to lead without coercion, many heads are inevitably better than one. + +## The disciplines of open source are the disciplines of good data science + +* Meaningful README +* Meaningful commit messages +* Modularity +* Separating data code from analytic code from interactive code +* Assigning issues and pull requests for action/ review +* Don't forget one of the most lazy and incompetent developers you will ever work with is yourself, six months later + +## What licences exist? + +* Permissive + * Such as MIT but there are others. Recommended by NHSX [draft guidelines on open source](https://github.com/nhsx/open-source-policy) + * Apache is a notable permissive licence- includes a patent licence + * In our work the [OGL](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) is also relevant- civil servant publish stuff under OGL (and MIT- it isn't particularly recommended for code) +* Copyleft + * GPL2, GPL3, AGPL [("the GPL of the web")](https://www.tldrlegal.com/license/gnu-affero-general-public-license-v3-agpl-3-0) + * Note that the provisions of the GPL only apply when you **distribute** the code + * At a certain point it all gets too complicated and you need a lawyer + * MPL is a notable copyleft licence- can combine with proprietary code as long as kept separate +* Arguments for permissive/ copyleft- getting your code used versus preserving software freedoms for other people +* Note that most of the licences are impossible to read! There is a website to explain [tl;dr](https://www.tldrlegal.com/) + +## What is copyright and why does it matter + +* Copyright is assigned at the moment of creation +* If you made it in your own time, it's yours (usually!) +* If you made it at work, it belongs to your employer +* If someone paid you to make it ("work for hire") it belongs to them +* Crucially, the copyright holder can relicence software + * If it's jointly authored it depends if it's a "collective" or "joint" work + * Honestly it's pretty complicated. Just vest copyright in an organisation or group of individuals you trust + * Goldacre review suggests using Crown copyright for copyright in the NHS because it's a "shoal, not a big fish" (with apologies to Ben whom I am misquoting) + +## Iceweasel + +* Iceweasel is a story of **trademark** rather than copyright +* Debian (a Linux flavour) had the permission to use the source code of Firefox, but not the logo +* So they took the source code and made their own version +* This sounds very obscure and unimportant but it could become important in future projects of ours, like... + +## What we have learned in recent projects + +* The huge benefits of being open + * Transparency + * Working with customers + * Goodwill +* Nonfree mitigators +* Different licences for different repos + +## Software freedom means allowing people to do stuff you don't like + +* Freedom 0: The freedom to use the program for any purpose. +* Freedom 3: The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits. +* The code isn't the only thing with worth in the project +* This is why there are whole businesses founded on "here's the Linux source code" +* So when we're sharing code we are **letting** people do stupid things with it but we're not **recommending** that they do stupid things with it +* People do stupid things with Excel and Microsoft don't accept liability for that, and neither should we +* This issue of sharing analytic code and merchantability for a particular purpose is poorly understood and I think everyone needs to be clearer on it (us, and our customers) +* In my view a world where consultants are selling **our** code is better than a world where they're selling **their** spreadsheets + +## "Open source as in piano" + +* The patient experience QDC project +* Our current project +* Open source code is not necessarily to be run, but understood and learned from +* Building a group of people who can use and contribute to your code is arguably as important as writing it diff --git a/presentations/2024-05-30_open-source-licensing/renv.lock b/presentations/2024-05-30_open-source-licensing/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2024-05-30_open-source-licensing/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-08-22_agile-and-scrum/renv.lock b/presentations/2024-08-22_agile-and-scrum/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/presentations/2024-08-22_agile-and-scrum/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/presentations/2024-09-05_earl-nhp/renv.lock b/presentations/2024-09-05_earl-nhp/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/presentations/2024-09-05_earl-nhp/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/presentations/2024-10-09-rap-cautionary-tales/renv.lock b/presentations/2024-10-09-rap-cautionary-tales/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/presentations/2024-10-09-rap-cautionary-tales/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/presentations/2024-10-10_what-is-ai-chris/renv.lock b/presentations/2024-10-10_what-is-ai-chris/renv.lock new file mode 100644 index 0000000..a9166db --- /dev/null +++ b/presentations/2024-10-10_what-is-ai-chris/renv.lock @@ -0,0 +1,334 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-10-10_what-is-ai-matt/renv.lock b/presentations/2024-10-10_what-is-ai-matt/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/presentations/2024-10-10_what-is-ai-matt/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/presentations/2024-10-10_what-is-ai-tom/renv.lock b/presentations/2024-10-10_what-is-ai-tom/renv.lock new file mode 100644 index 0000000..81572e7 --- /dev/null +++ b/presentations/2024-10-10_what-is-ai-tom/renv.lock @@ -0,0 +1,716 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "MASS": { + "Package": "MASS", + "Version": "7.3-61", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "0cafd6f0500e5deba33be22c46bf6055" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "5122bb14d8736372411f955e1b16bc8a" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "digest": { + "Package": "digest", + "Version": "0.6.37", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "33698c4b3127fc9f506654607fb73676" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "3fd29944b231036ad67c3edb32e02201" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "7f48af39fa27711ea5fbd183b399920d" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "44c6a2f8202d5b7e878ea274b1092426" + }, + "glue": { + "Package": "glue", + "Version": "1.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "5899f1eaa825580172bb56c08266f37c" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang", + "stats" + ], + "Hash": "de949855009e2d4d0e52a844e30617ae" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + }, + "knitr": { + "Package": "knitr", + "Version": "1.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9fcb189926d93c636dea94fbe4f44480" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lattice": { + "Package": "lattice", + "Version": "0.22-6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "110ee9d83b496279960e162ac97764ce" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-166", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "ccbb8846be320b627e6aa2b4616a2ded" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "df99277f63d01c34e95e3d2f06a79736" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.54", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "cc2d62c76458d425210d1eb1478b30b4" + }, + "xfun": { + "Package": "xfun", + "Version": "0.49", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "stats", + "tools" + ], + "Hash": "8687398773806cfff9401a2feca96298" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/presentations/2024-10-10_what-is-ai-yiwen/renv.lock b/presentations/2024-10-10_what-is-ai-yiwen/renv.lock new file mode 100644 index 0000000..0fca091 --- /dev/null +++ b/presentations/2024-10-10_what-is-ai-yiwen/renv.lock @@ -0,0 +1,23 @@ +{ + "R": { + "Version": "4.4.2", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + } + } +} diff --git a/presentations/index.qmd b/presentations/index.qmd index 6254267..402a51b 100644 --- a/presentations/index.qmd +++ b/presentations/index.qmd @@ -1,77 +1,83 @@ ---- -title: Presentations -format: - html: - grid: - sidebar-width: 200px - body-width: 1100px - margin-width: 200px - gutter-width: 1.5rem -execute: - freeze: false ---- - -```{r} -#| echo: false - -# note: if you are running interactively then you will be in / -# but quarto will be running this in /presentations - -path <- if (!getwd() |> stringr::str_ends("presentations")) { - path <- "presentations" -} else { - path <- "." -} - -dir(path, pattern = "index.qmd", recursive = TRUE, full.names = TRUE) |> - stringr::str_subset(paste0("^", path, "/index.qmd$"), TRUE) |> - purrr::set_names() |> - purrr::map( - \(f) { - i <- readr::read_lines(f) - i <- i[do.call(seq, as.list(which(i == "---")[1:2] + c(1, -1)))] - yaml::read_yaml(text = i) |> - purrr::keep(is.atomic) |> - purrr::map(paste, collapse = ", ") |> - tibble::as_tibble() - } - ) |> - dplyr::bind_rows(.id = "filename") |> - dplyr::mutate( - dplyr::across( - "author", - \(.x) stringr::str_replace_all(.x, "\\[([^\\]]*)\\]\\([^\\)]*\\)", "\\1") - ), - dplyr::across( - "date", - lubridate::ymd - ), - title = purrr::map( - paste0( - "", - ifelse( - is.na(subtitle), - title, - paste0(title, ": ", subtitle) - ), - "" - ), - gt::html - ) - ) |> - dplyr::select(-"date-format", -"subtitle", -"filename") |> - dplyr::arrange(desc(date)) |> - gt::gt() |> - gt::cols_align("left") |> - gt::cols_width( - title ~ px(650), - author ~ px(150), - date ~ px(100) - ) |> - gt::tab_style(style = gt::cell_text(weight = "bold"), locations = gt::cells_column_labels(columns = everything())) |> - gt::cols_label( - starts_with("date") ~ "Date", - starts_with("author") ~ "Author", - starts_with("title") ~ "Title" - ) -``` +--- +title: Presentations +format: + html: + grid: + sidebar-width: 200px + body-width: 1100px + margin-width: 200px + gutter-width: 1.5rem +execute: + freeze: false +--- + +```{r lockfile} +#| include: FALSE +renv::use(lockfile = "renv.lock") +``` + + +```{r} +#| echo: false + +# note: if you are running interactively then you will be in / +# but quarto will be running this in /presentations + +path <- if (!getwd() |> stringr::str_ends("presentations")) { + path <- "presentations" +} else { + path <- "." +} + +dir(path, pattern = "index.qmd", recursive = TRUE, full.names = TRUE) |> + stringr::str_subset(paste0("^", path, "/index.qmd$"), TRUE) |> + purrr::set_names() |> + purrr::map( + \(f) { + i <- readr::read_lines(f) + i <- i[do.call(seq, as.list(which(i == "---")[1:2] + c(1, -1)))] + yaml::read_yaml(text = i) |> + purrr::keep(is.atomic) |> + purrr::map(paste, collapse = ", ") |> + tibble::as_tibble() + } + ) |> + dplyr::bind_rows(.id = "filename") |> + dplyr::mutate( + dplyr::across( + "author", + \(.x) stringr::str_replace_all(.x, "\\[([^\\]]*)\\]\\([^\\)]*\\)", "\\1") + ), + dplyr::across( + "date", + lubridate::ymd + ), + title = purrr::map( + paste0( + "", + ifelse( + is.na(subtitle), + title, + paste0(title, ": ", subtitle) + ), + "" + ), + gt::html + ) + ) |> + dplyr::select(-"date-format", -"subtitle", -"filename") |> + dplyr::arrange(desc(date)) |> + gt::gt() |> + gt::cols_align("left") |> + gt::cols_width( + title ~ px(650), + author ~ px(150), + date ~ px(100) + ) |> + gt::tab_style(style = gt::cell_text(weight = "bold"), locations = gt::cells_column_labels(columns = everything())) |> + gt::cols_label( + starts_with("date") ~ "Date", + starts_with("author") ~ "Author", + starts_with("title") ~ "Title" + ) +``` diff --git a/presentations/renv.lock b/presentations/renv.lock new file mode 100644 index 0000000..362374b --- /dev/null +++ b/presentations/renv.lock @@ -0,0 +1,958 @@ +{ + "R": { + "Version": "4.4.1", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://packagemanager.posit.co/cran/latest" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.13", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "f27411eb6d9c3dada5edd444b8416675" + }, + "V8": { + "Package": "V8", + "Version": "4.4.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "Rcpp", + "curl", + "jsonlite", + "utils" + ], + "Hash": "ca98390ad1cef2a5a609597b49d3d042" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bigD": { + "Package": "bigD", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "93637e906f3fe962413912c956eb44db" + }, + "bit": { + "Package": "bit", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "d242abec29412ce988848d0294b208fd" + }, + "bit64": { + "Package": "bit64", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "9fe98599ca456d6552421db0d6772d8f" + }, + "bitops": { + "Package": "bitops", + "Version": "1.0-8", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "da69e6b6f8feebec0827205aad3fdbd8" + }, + "bslib": { + "Package": "bslib", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + }, + "cachem": { + "Package": "cachem", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "cd9a672193789068eb5a2aad65a0dedf" + }, + "cli": { + "Package": "cli", + "Version": "3.6.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" + }, + "commonmark": { + "Package": "commonmark", + "Version": "1.9.1", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "5d8225445acb167abf7797de48b2ee3c" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.4.7", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "5a295d7d963cc5035284dcdbaf334f4e" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "859d96e65ef198fd43e82b9628d593ef" + }, + "curl": { + "Package": "curl", + "Version": "5.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "411ca2c03b1ce5f548345d2fc2685f7a" + }, + "digest": { + "Package": "digest", + "Version": "0.6.36", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "fd6824ad91ede64151e93af67df6376b" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "0.24.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "a1066cbc05caee9a4bf6d90f194ff4da" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "farver": { + "Package": "farver", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "680887028577f3fa2a81e410ed0d6e42" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15aeb8c27f5ea5161f9f6a641fafd93a" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "glue": { + "Package": "glue", + "Version": "1.7.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "e0b3a53876554bd45879e596cdb10a52" + }, + "gt": { + "Package": "gt", + "Version": "0.11.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "bigD", + "bitops", + "cli", + "commonmark", + "dplyr", + "fs", + "glue", + "htmltools", + "htmlwidgets", + "juicyjuice", + "magrittr", + "markdown", + "reactable", + "rlang", + "sass", + "scales", + "tidyselect", + "vctrs", + "xml2" + ], + "Hash": "3470c2eb1123db6a2c54ec812de38284" + }, + "highr": { + "Package": "highr", + "Version": "0.11", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "d65ba49117ca223614f71b60d85b8ab7" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.8", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "methods" + ], + "Hash": "e1b9c55281c5adc4dd113652d9e26768" + }, + "juicyjuice": { + "Package": "juicyjuice", + "Version": "0.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "V8" + ], + "Hash": "3bcd11943da509341838da9399e18bce" + }, + "knitr": { + "Package": "knitr", + "Version": "1.48", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "acf380f300c721da9fde7df115a5f86f" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "b64ec208ac5bc1852b285f665d6368b3" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "markdown": { + "Package": "markdown", + "Version": "1.13", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "commonmark", + "utils", + "xfun" + ], + "Hash": "074efab766a9d6360865ad39512f2a7e" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "4fd8900853b746af55b81fda99da7695" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "reactR": { + "Package": "reactR", + "Version": "0.6.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "10f4d661c235181648a5958c02c0382a" + }, + "reactable": { + "Package": "reactable", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "digest", + "htmltools", + "htmlwidgets", + "jsonlite", + "reactR" + ], + "Hash": "6069eb2a6597963eae0605c1875ff14c" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "renv": { + "Package": "renv", + "Version": "1.0.7", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "397b7b2a265bc5a7a06852524dabae20" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.28", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "062470668513dcda416927085ee9bdc7" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "scales": { + "Package": "scales", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "cli", + "farver", + "glue", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "c19df082ba346b0ffa6f833e92de34d1" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "39e1144fd75428983dc3f63aa53dfa91" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.52", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "xfun" + ], + "Hash": "cfbad971a71f0e27cec22e544a08bc3b" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "withr": { + "Package": "withr", + "Version": "3.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "07909200e8bbe90426fbfeb73e1e27aa" + }, + "xfun": { + "Package": "xfun", + "Version": "0.46", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "grDevices", + "stats", + "tools" + ], + "Hash": "00ce32f398db0415dde61abfef11300c" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.10", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" + } + } +} diff --git a/renv.lock b/renv.lock index 54e7dcb..a9166db 100644 --- a/renv.lock +++ b/renv.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.4.1", + "Version": "4.4.2", "Repositories": [ { "Name": "CRAN", @@ -9,2915 +9,310 @@ ] }, "Packages": { - "BH": { - "Package": "BH", - "Version": "1.84.0-0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "a8235afbcd6316e6e91433ea47661013" - }, - "DBI": { - "Package": "DBI", - "Version": "1.2.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "065ae649b05f1ff66bb0c793107508f5" - }, - "DT": { - "Package": "DT", - "Version": "0.33", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "crosstalk", - "htmltools", - "htmlwidgets", - "httpuv", - "jquerylib", - "jsonlite", - "magrittr", - "promises" - ], - "Hash": "64ff3427f559ce3f2597a4fe13255cb6" - }, - "Deriv": { - "Package": "Deriv", - "Version": "4.1.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "methods" - ], - "Hash": "bc727aba82bcda74db0bbe188ec6757c" - }, - "KernSmooth": { - "Package": "KernSmooth", - "Version": "2.23-24", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "stats" - ], - "Hash": "9f33a1ee37bbe8919eb2ec4b9f2473a5" - }, - "MASS": { - "Package": "MASS", - "Version": "7.3-61", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "methods", - "stats", - "utils" - ], - "Hash": "0cafd6f0500e5deba33be22c46bf6055" - }, - "Matrix": { - "Package": "Matrix", - "Version": "1.7-0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "grid", - "lattice", - "methods", - "stats", - "utils" - ], - "Hash": "1920b2f11133b12350024297d8a4ff4a" - }, - "MatrixModels": { - "Package": "MatrixModels", - "Version": "0.5-3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Matrix", - "R", - "methods", - "stats" - ], - "Hash": "0776bf7526869e0286b0463cb72fb211" - }, - "NHSRdatasets": { - "Package": "NHSRdatasets", - "Version": "0.3.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "tibble" - ], - "Hash": "02c9417179975a83e37329f40c451591" - }, - "PostcodesioR": { - "Package": "PostcodesioR", - "Version": "0.3.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "httr" - ], - "Hash": "6fd5043a1023796198ad74afe58a1eee" - }, - "R6": { - "Package": "R6", - "Version": "2.5.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "470851b6d5d0ac559e9d01bb352b4021" - }, - "RColorBrewer": { - "Package": "RColorBrewer", - "Version": "1.1-3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "45f0398006e83a5b10b72a90663d8d8c" - }, - "Rcpp": { - "Package": "Rcpp", - "Version": "1.0.13", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "methods", - "utils" - ], - "Hash": "f27411eb6d9c3dada5edd444b8416675" - }, - "RcppEigen": { - "Package": "RcppEigen", - "Version": "0.3.4.0.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rcpp", - "stats", - "utils" - ], - "Hash": "598ee071e758b5f667b0ebe83f5d5085" - }, - "RcppSimdJson": { - "Package": "RcppSimdJson", - "Version": "0.1.12", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Rcpp", - "utils" - ], - "Hash": "444ca3d3bcf460a5e8f45d96431fb7a5" - }, - "Rttf2pt1": { - "Package": "Rttf2pt1", - "Version": "1.3.12", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "a60168d094ca7e4de5106d60001c3964" - }, - "SparseM": { - "Package": "SparseM", - "Version": "1.84-2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "graphics", - "methods", - "stats", - "utils" - ], - "Hash": "e78499cbcbbca98200254bd171379165" - }, - "StrategyUnitTheme": { - "Package": "StrategyUnitTheme", - "Version": "0.1.7", - "Source": "GitHub", - "RemoteType": "github", - "RemoteHost": "api.github.com", - "RemoteRepo": "StrategyUnitTheme", - "RemoteUsername": "The-Strategy-Unit", - "RemoteRef": "HEAD", - "RemoteSha": "09397568ef66d2cf757b677f34dbd9f7fd4b02de", - "Requirements": [ - "ggplot2", - "rmarkdown", - "xaringan" - ], - "Hash": "1f9ccb68578b48c14be80c2d3cf8dd51" - }, - "V8": { - "Package": "V8", - "Version": "5.0.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Rcpp", - "curl", - "jsonlite", - "utils" - ], - "Hash": "9eb7b2df315593e726b029200fc0276c" - }, - "abind": { - "Package": "abind", - "Version": "1.4-5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods", - "utils" - ], - "Hash": "4f57884290cc75ab22f4af9e9d4ca862" - }, - "askpass": { - "Package": "askpass", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "sys" - ], - "Hash": "cad6cf7f1d5f6e906700b9d3e718c796" - }, - "backports": { - "Package": "backports", - "Version": "1.5.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "e1e1b9d75c37401117b636b7ae50827a" - }, - "base64enc": { - "Package": "base64enc", - "Version": "0.1-3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "543776ae6848fde2f48ff3816d0628bc" - }, - "base64url": { - "Package": "base64url", - "Version": "1.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "backports" - ], - "Hash": "0c54cf3a08cc0e550fbd64ad33166143" - }, - "bigD": { - "Package": "bigD", - "Version": "0.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "93637e906f3fe962413912c956eb44db" - }, - "bit": { - "Package": "bit", - "Version": "4.0.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "d242abec29412ce988848d0294b208fd" - }, - "bit64": { - "Package": "bit64", - "Version": "4.0.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "bit", - "methods", - "stats", - "utils" - ], - "Hash": "9fe98599ca456d6552421db0d6772d8f" - }, - "bitops": { - "Package": "bitops", - "Version": "1.0-8", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "da69e6b6f8feebec0827205aad3fdbd8" - }, - "blob": { - "Package": "blob", - "Version": "1.2.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "methods", - "rlang", - "vctrs" - ], - "Hash": "40415719b5a479b87949f3aa0aee737c" - }, - "boot": { - "Package": "boot", - "Version": "1.3-30", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "graphics", - "stats" - ], - "Hash": "96abeed416a286d4a0f52e550b612343" - }, - "brio": { - "Package": "brio", - "Version": "1.1.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "c1ee497a6d999947c2c224ae46799b1a" - }, - "broom": { - "Package": "broom", - "Version": "1.0.6", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "backports", - "dplyr", - "generics", - "glue", - "lifecycle", - "purrr", - "rlang", - "stringr", - "tibble", - "tidyr" - ], - "Hash": "a4652c36d1f8abfc3ddf4774f768c934" - }, - "bslib": { - "Package": "bslib", - "Version": "0.8.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "base64enc", - "cachem", - "fastmap", - "grDevices", - "htmltools", - "jquerylib", - "jsonlite", - "lifecycle", - "memoise", - "mime", - "rlang", - "sass" - ], - "Hash": "b299c6741ca9746fb227debcb0f9fb6c" - }, - "cachem": { - "Package": "cachem", - "Version": "1.1.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "fastmap", - "rlang" - ], - "Hash": "cd9a672193789068eb5a2aad65a0dedf" - }, - "callr": { - "Package": "callr", - "Version": "3.7.6", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "processx", - "utils" - ], - "Hash": "d7e13f49c19103ece9e58ad2d83a7354" - }, - "car": { - "Package": "car", - "Version": "3.1-2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "R", - "abind", - "carData", - "grDevices", - "graphics", - "lme4", - "mgcv", - "nlme", - "nnet", - "pbkrtest", - "quantreg", - "scales", - "stats", - "utils" - ], - "Hash": "839b351f31d56e0147439eb22c00a66a" - }, - "carData": { - "Package": "carData", - "Version": "3.0-5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "ac6cdb8552c61bd36b0e54d07cf2aab7" - }, - "cellranger": { - "Package": "cellranger", - "Version": "1.1.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "rematch", - "tibble" - ], - "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" - }, - "class": { - "Package": "class", - "Version": "7.3-22", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "R", - "stats", - "utils" - ], - "Hash": "f91f6b29f38b8c280f2b9477787d4bb2" - }, - "classInt": { - "Package": "classInt", - "Version": "0.4-10", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "KernSmooth", - "R", - "class", - "e1071", - "grDevices", - "graphics", - "stats" - ], - "Hash": "f5a40793b1ae463a7ffb3902a95bf864" - }, - "cli": { - "Package": "cli", - "Version": "3.6.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "utils" - ], - "Hash": "b21916dd77a27642b447374a5d30ecf3" - }, - "clipr": { - "Package": "clipr", - "Version": "0.8.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "utils" - ], - "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" - }, - "codetools": { - "Package": "codetools", - "Version": "0.2-20", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "61e097f35917d342622f21cdc79c256e" - }, - "colorspace": { - "Package": "colorspace", - "Version": "2.1-1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "methods", - "stats" - ], - "Hash": "d954cb1c57e8d8b756165d7ba18aa55a" - }, - "commonmark": { - "Package": "commonmark", - "Version": "1.9.1", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "5d8225445acb167abf7797de48b2ee3c" - }, - "conflicted": { - "Package": "conflicted", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "memoise", - "rlang" - ], - "Hash": "bb097fccb22d156624fd07cd2894ddb6" - }, - "corrplot": { - "Package": "corrplot", - "Version": "0.94", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "b575018abd67c252491ffce5ecb94630" - }, - "cowplot": { - "Package": "cowplot", - "Version": "1.1.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "ggplot2", - "grDevices", - "grid", - "gtable", - "methods", - "rlang", - "scales" - ], - "Hash": "8ef2084dd7d28847b374e55440e4f8cb" - }, - "cpp11": { - "Package": "cpp11", - "Version": "0.4.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "5a295d7d963cc5035284dcdbaf334f4e" - }, - "crayon": { - "Package": "crayon", - "Version": "1.5.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "grDevices", - "methods", - "utils" - ], - "Hash": "859d96e65ef198fd43e82b9628d593ef" - }, - "crosstalk": { - "Package": "crosstalk", - "Version": "1.2.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R6", - "htmltools", - "jsonlite", - "lazyeval" - ], - "Hash": "ab12c7b080a57475248a30f4db6298c0" - }, - "curl": { - "Package": "curl", - "Version": "5.2.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "411ca2c03b1ce5f548345d2fc2685f7a" - }, - "cyclocomp": { - "Package": "cyclocomp", - "Version": "1.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "callr", - "crayon", - "desc", - "remotes", - "withr" - ], - "Hash": "cdc4a473222b0112d4df0bcfbed12d44" - }, - "data.table": { - "Package": "data.table", - "Version": "1.15.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "8ee9ac56ef633d0c7cab8b2ca87d683e" - }, - "dbplyr": { - "Package": "dbplyr", - "Version": "2.5.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "DBI", - "R", - "R6", - "blob", - "cli", - "dplyr", - "glue", - "lifecycle", - "magrittr", - "methods", - "pillar", - "purrr", - "rlang", - "tibble", - "tidyr", - "tidyselect", - "utils", - "vctrs", - "withr" - ], - "Hash": "39b2e002522bfd258039ee4e889e0fd1" - }, - "desc": { - "Package": "desc", - "Version": "1.4.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "cli", - "utils" - ], - "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" - }, - "diffobj": { - "Package": "diffobj", - "Version": "0.3.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "crayon", - "methods", - "stats", - "tools", - "utils" - ], - "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" - }, - "digest": { - "Package": "digest", - "Version": "0.6.37", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "utils" - ], - "Hash": "33698c4b3127fc9f506654607fb73676" - }, - "doBy": { - "Package": "doBy", - "Version": "4.6.22", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Deriv", - "MASS", - "Matrix", - "R", - "boot", - "broom", - "cowplot", - "dplyr", - "ggplot2", - "methods", - "microbenchmark", - "modelr", - "rlang", - "tibble", - "tidyr" - ], - "Hash": "a9b56885b9596c284168df26d6179c40" - }, - "dplyr": { - "Package": "dplyr", - "Version": "1.1.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "cli", - "generics", - "glue", - "lifecycle", - "magrittr", - "methods", - "pillar", - "rlang", - "tibble", - "tidyselect", - "utils", - "vctrs" - ], - "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" - }, - "dtplyr": { - "Package": "dtplyr", - "Version": "1.3.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "data.table", - "dplyr", - "glue", - "lifecycle", - "rlang", - "tibble", - "tidyselect", - "vctrs" - ], - "Hash": "54ed3ea01b11e81a86544faaecfef8e2" - }, - "e1071": { - "Package": "e1071", - "Version": "1.7-14", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "class", - "grDevices", - "graphics", - "methods", - "proxy", - "stats", - "utils" - ], - "Hash": "4ef372b716824753719a8a38b258442d" - }, - "evaluate": { - "Package": "evaluate", - "Version": "0.24.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "a1066cbc05caee9a4bf6d90f194ff4da" - }, - "extrafont": { - "Package": "extrafont", - "Version": "0.19", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rttf2pt1", - "extrafontdb", - "grDevices", - "utils" - ], - "Hash": "03d9939b37164f34e0522fef13e63158" - }, - "extrafontdb": { - "Package": "extrafontdb", - "Version": "1.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "a861555ddec7451c653b40e713166c6f" - }, - "fansi": { - "Package": "fansi", - "Version": "1.0.6", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "utils" - ], - "Hash": "962174cf2aeb5b9eea581522286a911f" - }, - "farver": { - "Package": "farver", - "Version": "2.1.2", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "680887028577f3fa2a81e410ed0d6e42" - }, - "fastmap": { - "Package": "fastmap", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" - }, - "fontawesome": { - "Package": "fontawesome", - "Version": "0.5.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "htmltools", - "rlang" - ], - "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" - }, - "forcats": { - "Package": "forcats", - "Version": "1.0.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "glue", - "lifecycle", - "magrittr", - "rlang", - "tibble" - ], - "Hash": "1a0a9a3d5083d0d573c4214576f1e690" - }, - "fs": { - "Package": "fs", - "Version": "1.6.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "15aeb8c27f5ea5161f9f6a641fafd93a" - }, - "gargle": { - "Package": "gargle", - "Version": "1.5.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "fs", - "glue", - "httr", - "jsonlite", - "lifecycle", - "openssl", - "rappdirs", - "rlang", - "stats", - "utils", - "withr" - ], - "Hash": "fc0b272e5847c58cd5da9b20eedbd026" - }, - "generics": { - "Package": "generics", - "Version": "0.1.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "15e9634c0fcd294799e9b2e929ed1b86" - }, - "ggplot2": { - "Package": "ggplot2", - "Version": "3.5.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "R", - "cli", - "glue", - "grDevices", - "grid", - "gtable", - "isoband", - "lifecycle", - "mgcv", - "rlang", - "scales", - "stats", - "tibble", - "vctrs", - "withr" - ], - "Hash": "44c6a2f8202d5b7e878ea274b1092426" - }, - "ggpubr": { - "Package": "ggpubr", - "Version": "0.6.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cowplot", - "dplyr", - "ggplot2", - "ggrepel", - "ggsci", - "ggsignif", - "glue", - "grid", - "gridExtra", - "magrittr", - "polynom", - "purrr", - "rlang", - "rstatix", - "scales", - "stats", - "tibble", - "tidyr", - "utils" - ], - "Hash": "c957612b8bb1ee9ab7b2450d26663e7e" - }, - "ggrepel": { - "Package": "ggrepel", - "Version": "0.9.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rcpp", - "ggplot2", - "grid", - "rlang", - "scales", - "withr" - ], - "Hash": "cc3361e234c4a5050e29697d675764aa" - }, - "ggsci": { - "Package": "ggsci", - "Version": "3.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "ggplot2", - "grDevices", - "scales" - ], - "Hash": "0c3268cddf4d3a3ce4e7e6330f8e92c8" - }, - "ggsignif": { - "Package": "ggsignif", - "Version": "0.6.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "ggplot2" - ], - "Hash": "a57f0f5dbcfd0d77ad4ff33032f5dc79" - }, - "glue": { - "Package": "glue", - "Version": "1.7.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "methods" - ], - "Hash": "e0b3a53876554bd45879e596cdb10a52" - }, - "googlePolylines": { - "Package": "googlePolylines", - "Version": "0.8.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "BH", - "Rcpp" - ], - "Hash": "d01f12d522d359286ac745c815dd8201" - }, - "googledrive": { - "Package": "googledrive", - "Version": "2.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "gargle", - "glue", - "httr", - "jsonlite", - "lifecycle", - "magrittr", - "pillar", - "purrr", - "rlang", - "tibble", - "utils", - "uuid", - "vctrs", - "withr" - ], - "Hash": "e99641edef03e2a5e87f0a0b1fcc97f4" - }, - "googlesheets4": { - "Package": "googlesheets4", - "Version": "1.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cellranger", - "cli", - "curl", - "gargle", - "glue", - "googledrive", - "httr", - "ids", - "lifecycle", - "magrittr", - "methods", - "purrr", - "rematch2", - "rlang", - "tibble", - "utils", - "vctrs", - "withr" - ], - "Hash": "d6db1667059d027da730decdc214b959" - }, - "gridExtra": { - "Package": "gridExtra", - "Version": "2.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "grDevices", - "graphics", - "grid", - "gtable", - "utils" - ], - "Hash": "7d7f283939f563670a697165b2cf5560" - }, - "gt": { - "Package": "gt", - "Version": "0.11.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "base64enc", - "bigD", - "bitops", - "cli", - "commonmark", - "dplyr", - "fs", - "glue", - "htmltools", - "htmlwidgets", - "juicyjuice", - "magrittr", - "markdown", - "reactable", - "rlang", - "sass", - "scales", - "tidyselect", - "vctrs", - "xml2" - ], - "Hash": "3470c2eb1123db6a2c54ec812de38284" - }, - "gtable": { - "Package": "gtable", - "Version": "0.3.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "glue", - "grid", - "lifecycle", - "rlang" - ], - "Hash": "e18861963cbc65a27736e02b3cd3c4a0" - }, - "haven": { - "Package": "haven", - "Version": "2.5.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "cpp11", - "forcats", - "hms", - "lifecycle", - "methods", - "readr", - "rlang", - "tibble", - "tidyselect", - "vctrs" - ], - "Hash": "9171f898db9d9c4c1b2c745adc2c1ef1" - }, - "here": { - "Package": "here", - "Version": "1.0.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "rprojroot" - ], - "Hash": "24b224366f9c2e7534d2344d10d59211" - }, - "highr": { - "Package": "highr", - "Version": "0.11", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "xfun" - ], - "Hash": "d65ba49117ca223614f71b60d85b8ab7" - }, - "hms": { - "Package": "hms", - "Version": "1.1.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "lifecycle", - "methods", - "pkgconfig", - "rlang", - "vctrs" - ], - "Hash": "b59377caa7ed00fa41808342002138f9" - }, - "htmltools": { - "Package": "htmltools", - "Version": "0.5.8.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "base64enc", - "digest", - "fastmap", - "grDevices", - "rlang", - "utils" - ], - "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" - }, - "htmlwidgets": { - "Package": "htmlwidgets", - "Version": "1.6.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "grDevices", - "htmltools", - "jsonlite", - "knitr", - "rmarkdown", - "yaml" - ], - "Hash": "04291cc45198225444a397606810ac37" - }, - "httpuv": { - "Package": "httpuv", - "Version": "1.6.15", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "Rcpp", - "later", - "promises", - "utils" - ], - "Hash": "d55aa087c47a63ead0f6fc10f8fa1ee0" - }, - "httr": { - "Package": "httr", - "Version": "1.4.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "curl", - "jsonlite", - "mime", - "openssl" - ], - "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" - }, - "ids": { - "Package": "ids", - "Version": "1.0.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "openssl", - "uuid" - ], - "Hash": "99df65cfef20e525ed38c3d2577f7190" - }, - "igraph": { - "Package": "igraph", - "Version": "2.0.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Matrix", - "R", - "cli", - "cpp11", - "grDevices", - "graphics", - "lifecycle", - "magrittr", - "methods", - "pkgconfig", - "rlang", - "stats", - "utils", - "vctrs" - ], - "Hash": "c3b7d801d722e26e4cd888e042bf9af5" - }, - "isoband": { - "Package": "isoband", - "Version": "0.2.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "grid", - "utils" - ], - "Hash": "0080607b4a1a7b28979aecef976d8bc2" - }, - "janitor": { - "Package": "janitor", - "Version": "2.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "dplyr", - "hms", - "lifecycle", - "lubridate", - "magrittr", - "purrr", - "rlang", - "snakecase", - "stringi", - "stringr", - "tidyr", - "tidyselect" - ], - "Hash": "5baae149f1082f466df9d1442ba7aa65" - }, - "jquerylib": { - "Package": "jquerylib", - "Version": "0.1.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "htmltools" - ], - "Hash": "5aab57a3bd297eee1c1d862735972182" - }, - "jsonlite": { - "Package": "jsonlite", - "Version": "1.8.8", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "methods" - ], - "Hash": "e1b9c55281c5adc4dd113652d9e26768" - }, - "juicyjuice": { - "Package": "juicyjuice", - "Version": "0.1.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "V8" - ], - "Hash": "3bcd11943da509341838da9399e18bce" - }, - "knitr": { - "Package": "knitr", - "Version": "1.48", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "evaluate", - "highr", - "methods", - "tools", - "xfun", - "yaml" - ], - "Hash": "acf380f300c721da9fde7df115a5f86f" - }, - "labeling": { - "Package": "labeling", - "Version": "0.4.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "graphics", - "stats" - ], - "Hash": "b64ec208ac5bc1852b285f665d6368b3" - }, - "later": { - "Package": "later", - "Version": "1.3.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Rcpp", - "rlang" - ], - "Hash": "a3e051d405326b8b0012377434c62b37" - }, - "lattice": { - "Package": "lattice", - "Version": "0.22-6", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "grid", - "stats", - "utils" - ], - "Hash": "cc5ac1ba4c238c7ca9fa6a87ca11a7e2" - }, - "lazyeval": { - "Package": "lazyeval", - "Version": "0.2.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "d908914ae53b04d4c0c0fd72ecc35370" - }, - "leaflet": { - "Package": "leaflet", - "Version": "2.2.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "RColorBrewer", - "crosstalk", - "htmltools", - "htmlwidgets", - "jquerylib", - "leaflet.providers", - "magrittr", - "methods", - "png", - "raster", - "scales", - "sp", - "stats", - "viridisLite", - "xfun" - ], - "Hash": "ca012d4a706e21ce217ba15f22d402b2" - }, - "leaflet.providers": { - "Package": "leaflet.providers", - "Version": "2.0.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "htmltools" - ], - "Hash": "c0b81ad9d5d932772f7a457ac398cf36" - }, - "lifecycle": { - "Package": "lifecycle", - "Version": "1.0.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "glue", - "rlang" - ], - "Hash": "b8552d117e1b808b09a832f589b79035" - }, - "lintr": { - "Package": "lintr", - "Version": "3.1.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "backports", - "codetools", - "cyclocomp", - "digest", - "glue", - "knitr", - "rex", - "stats", - "utils", - "xml2", - "xmlparsedata" - ], - "Hash": "08cff46381a242d44c0d8dd0aabd9f71" - }, - "lme4": { - "Package": "lme4", - "Version": "1.1-35.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "Matrix", - "R", - "Rcpp", - "RcppEigen", - "boot", - "graphics", - "grid", - "lattice", - "methods", - "minqa", - "nlme", - "nloptr", - "parallel", - "splines", - "stats", - "utils" - ], - "Hash": "16a08fc75007da0d08e0c0388c7c33e6" - }, - "lubridate": { - "Package": "lubridate", - "Version": "1.9.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "generics", - "methods", - "timechange" - ], - "Hash": "680ad542fbcf801442c83a6ac5a2126c" - }, - "magrittr": { - "Package": "magrittr", - "Version": "2.0.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "7ce2733a9826b3aeb1775d56fd305472" - }, - "mapiso": { - "Package": "mapiso", - "Version": "0.3.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "isoband", - "sf" - ], - "Hash": "ba0165748eb593d7276567def5b2228e" - }, - "markdown": { - "Package": "markdown", - "Version": "1.13", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "commonmark", - "utils", - "xfun" - ], - "Hash": "074efab766a9d6360865ad39512f2a7e" - }, - "memoise": { - "Package": "memoise", - "Version": "2.0.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "cachem", - "rlang" - ], - "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" - }, - "mgcv": { - "Package": "mgcv", - "Version": "1.9-1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Matrix", - "R", - "graphics", - "methods", - "nlme", - "splines", - "stats", - "utils" - ], - "Hash": "110ee9d83b496279960e162ac97764ce" - }, - "microbenchmark": { - "Package": "microbenchmark", - "Version": "1.4.10", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "graphics", - "stats" - ], - "Hash": "db81b552e393ed092872cf7023469bc2" - }, - "mime": { - "Package": "mime", - "Version": "0.12", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "tools" - ], - "Hash": "18e9c28c1d3ca1560ce30658b22ce104" - }, - "minqa": { - "Package": "minqa", - "Version": "1.2.8", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Rcpp" - ], - "Hash": "785ef8e22389d4a7634c6c944f2dc07d" - }, - "modelr": { - "Package": "modelr", - "Version": "0.1.11", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "broom", - "magrittr", - "purrr", - "rlang", - "tibble", - "tidyr", - "tidyselect", - "vctrs" - ], - "Hash": "4f50122dc256b1b6996a4703fecea821" - }, - "munsell": { - "Package": "munsell", - "Version": "0.5.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "colorspace", - "methods" - ], - "Hash": "4fd8900853b746af55b81fda99da7695" - }, - "nlme": { - "Package": "nlme", - "Version": "3.1-166", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "graphics", - "lattice", - "stats", - "utils" - ], - "Hash": "ccbb8846be320b627e6aa2b4616a2ded" - }, - "nloptr": { - "Package": "nloptr", - "Version": "2.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "27550641889a3abf3aec4d91186311ec" - }, - "nnet": { - "Package": "nnet", - "Version": "7.3-19", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "stats", - "utils" - ], - "Hash": "2c797b46eea7fb58ede195bc0b1f1138" - }, - "numDeriv": { - "Package": "numDeriv", - "Version": "2016.8-1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "df58958f293b166e4ab885ebcad90e02" - }, - "openssl": { - "Package": "openssl", - "Version": "2.2.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "askpass" - ], - "Hash": "c62edf62de70cadf40553e10c739049d" - }, - "osrm": { - "Package": "osrm", - "Version": "4.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "RcppSimdJson", - "curl", - "googlePolylines", - "mapiso", - "sf", - "utils" - ], - "Hash": "1507ab660b3f838680e205fd4f4d2de2" - }, - "patchwork": { - "Package": "patchwork", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "cli", - "ggplot2", - "grDevices", - "graphics", - "grid", - "gtable", - "rlang", - "stats", - "utils" - ], - "Hash": "9c8ab14c00ac07e9e04d1664c0b74486" - }, - "pbkrtest": { - "Package": "pbkrtest", - "Version": "0.5.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "Matrix", - "R", - "broom", - "doBy", - "dplyr", - "lme4", - "methods", - "numDeriv" - ], - "Hash": "938e6bbc4ac57534f8b43224506a8966" - }, - "pillar": { - "Package": "pillar", - "Version": "1.9.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "cli", - "fansi", - "glue", - "lifecycle", - "rlang", - "utf8", - "utils", - "vctrs" - ], - "Hash": "15da5a8412f317beeee6175fbc76f4bb" - }, - "pkgbuild": { - "Package": "pkgbuild", - "Version": "1.4.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "callr", - "cli", - "desc", - "processx" - ], - "Hash": "a29e8e134a460a01e0ca67a4763c595b" - }, - "pkgconfig": { - "Package": "pkgconfig", - "Version": "2.0.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "utils" - ], - "Hash": "01f28d4278f15c76cddbea05899c5d6f" - }, - "pkgload": { - "Package": "pkgload", - "Version": "1.4.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "desc", - "fs", - "glue", - "lifecycle", - "methods", - "pkgbuild", - "processx", - "rlang", - "rprojroot", - "utils", - "withr" - ], - "Hash": "2ec30ffbeec83da57655b850cf2d3e0e" - }, - "plotly": { - "Package": "plotly", - "Version": "4.10.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "RColorBrewer", - "base64enc", - "crosstalk", - "data.table", - "digest", - "dplyr", - "ggplot2", - "htmltools", - "htmlwidgets", - "httr", - "jsonlite", - "lazyeval", - "magrittr", - "promises", - "purrr", - "rlang", - "scales", - "tibble", - "tidyr", - "tools", - "vctrs", - "viridisLite" - ], - "Hash": "a1ac5c03ad5ad12b9d1597e00e23c3dd" - }, - "plyr": { - "Package": "plyr", - "Version": "1.8.9", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rcpp" - ], - "Hash": "6b8177fd19982f0020743fadbfdbd933" - }, - "png": { - "Package": "png", - "Version": "0.1-8", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "bd54ba8a0a5faded999a7aab6e46b374" - }, - "polynom": { - "Package": "polynom", - "Version": "1.4-1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "graphics", - "stats" - ], - "Hash": "ceb5c2a59ba33d42d051285a3e8a5118" - }, - "praise": { - "Package": "praise", - "Version": "1.0.0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "a555924add98c99d2f411e37e7d25e9f" - }, - "prettyunits": { - "Package": "prettyunits", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" - }, - "processx": { - "Package": "processx", - "Version": "3.8.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "ps", - "utils" - ], - "Hash": "0c90a7d71988856bad2a2a45dd871bb9" - }, - "progress": { - "Package": "progress", - "Version": "1.2.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "crayon", - "hms", - "prettyunits" - ], - "Hash": "f4625e061cb2865f111b47ff163a5ca6" - }, - "promises": { - "Package": "promises", - "Version": "1.3.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R6", - "Rcpp", - "fastmap", - "later", - "magrittr", - "rlang", - "stats" - ], - "Hash": "434cd5388a3979e74be5c219bcd6e77d" - }, - "proxy": { - "Package": "proxy", - "Version": "0.4-27", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "stats", - "utils" - ], - "Hash": "e0ef355c12942cf7a6b91a6cfaea8b3e" - }, - "ps": { - "Package": "ps", - "Version": "1.7.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "utils" - ], - "Hash": "878b467580097e9c383acbb16adab57a" - }, - "purrr": { - "Package": "purrr", - "Version": "1.0.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "lifecycle", - "magrittr", - "rlang", - "vctrs" - ], - "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" - }, - "quantreg": { - "Package": "quantreg", - "Version": "5.98", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "MASS", - "Matrix", - "MatrixModels", - "R", - "SparseM", - "graphics", - "methods", - "stats", - "survival" - ], - "Hash": "017561f17632c065388b7062da030952" - }, - "ragg": { - "Package": "ragg", - "Version": "1.3.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "systemfonts", - "textshaping" - ], - "Hash": "e3087db406e079a8a2fd87f413918ed3" - }, - "rappdirs": { - "Package": "rappdirs", - "Version": "0.3.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "5e3c5dc0b071b21fa128676560dbe94d" - }, - "raster": { - "Package": "raster", - "Version": "3.6-26", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rcpp", - "methods", - "sp", - "terra" - ], - "Hash": "7d6eda494f34a644420ac1bfd2a8023a" - }, - "reactR": { - "Package": "reactR", - "Version": "0.6.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "htmltools" - ], - "Hash": "10f4d661c235181648a5958c02c0382a" - }, - "reactable": { - "Package": "reactable", - "Version": "0.4.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "digest", - "htmltools", - "htmlwidgets", - "jsonlite", - "reactR" - ], - "Hash": "6069eb2a6597963eae0605c1875ff14c" - }, - "readr": { - "Package": "readr", - "Version": "2.1.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "cli", - "clipr", - "cpp11", - "crayon", - "hms", - "lifecycle", - "methods", - "rlang", - "tibble", - "tzdb", - "utils", - "vroom" - ], - "Hash": "9de96463d2117f6ac49980577939dfb3" - }, - "readxl": { - "Package": "readxl", - "Version": "1.4.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cellranger", - "cpp11", - "progress", - "tibble", - "utils" - ], - "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" - }, - "rematch": { - "Package": "rematch", - "Version": "2.0.0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "cbff1b666c6fa6d21202f07e2318d4f1" - }, - "rematch2": { - "Package": "rematch2", - "Version": "2.1.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "tibble" - ], - "Hash": "76c9e04c712a05848ae7a23d2f170a40" - }, - "remotes": { - "Package": "remotes", - "Version": "2.5.0.9000", - "Source": "GitHub", - "RemoteType": "github", - "RemoteHost": "api.github.com", - "RemoteUsername": "r-lib", - "RemoteRepo": "remotes", - "RemoteRef": "main", - "RemoteSha": "5b7eb08f70ecc4f885f2c75330ce77f87e7dd14e", - "Requirements": [ - "R", - "methods", - "stats", - "tools", - "utils" - ], - "Hash": "e4e425e2cd35ed8630b40fae3de75fec" - }, - "renv": { - "Package": "renv", - "Version": "1.0.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "utils" - ], - "Hash": "397b7b2a265bc5a7a06852524dabae20" - }, - "reprex": { - "Package": "reprex", - "Version": "2.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "callr", - "cli", - "clipr", - "fs", - "glue", - "knitr", - "lifecycle", - "rlang", - "rmarkdown", - "rstudioapi", - "utils", - "withr" - ], - "Hash": "97b1d5361a24d9fb588db7afe3e5bcbf" - }, - "rex": { - "Package": "rex", - "Version": "1.2.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "lazyeval" - ], - "Hash": "ae34cd56890607370665bee5bd17812f" - }, - "rlang": { - "Package": "rlang", - "Version": "1.1.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "utils" - ], - "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" - }, - "rmarkdown": { - "Package": "rmarkdown", - "Version": "2.28", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "bslib", - "evaluate", - "fontawesome", - "htmltools", - "jquerylib", - "jsonlite", - "knitr", - "methods", - "tinytex", - "tools", - "utils", - "xfun", - "yaml" - ], - "Hash": "062470668513dcda416927085ee9bdc7" - }, - "rprojroot": { - "Package": "rprojroot", - "Version": "2.0.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" - }, - "rstatix": { - "Package": "rstatix", - "Version": "0.7.2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "broom", - "car", - "corrplot", - "dplyr", - "generics", - "magrittr", - "purrr", - "rlang", - "stats", - "tibble", - "tidyr", - "tidyselect", - "utils" - ], - "Hash": "5045fbb71b143878d8c51975d1d7d56d" - }, - "rstudioapi": { - "Package": "rstudioapi", - "Version": "0.16.0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "96710351d642b70e8f02ddeb237c46a7" - }, - "rvest": { - "Package": "rvest", - "Version": "1.0.4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "glue", - "httr", - "lifecycle", - "magrittr", - "rlang", - "selectr", - "tibble", - "xml2" - ], - "Hash": "0bcf0c6f274e90ea314b812a6d19a519" - }, - "s2": { - "Package": "s2", - "Version": "1.1.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "Rcpp", - "wk" - ], - "Hash": "3c8013cdd7f1d20de5762e3f97e5e274" - }, - "sass": { - "Package": "sass", - "Version": "0.4.9", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R6", - "fs", - "htmltools", - "rappdirs", - "rlang" - ], - "Hash": "d53dbfddf695303ea4ad66f86e99b95d" - }, - "scales": { - "Package": "scales", - "Version": "1.3.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "RColorBrewer", - "cli", - "farver", - "glue", - "labeling", - "lifecycle", - "munsell", - "rlang", - "viridisLite" - ], - "Hash": "c19df082ba346b0ffa6f833e92de34d1" - }, - "secretbase": { - "Package": "secretbase", - "Version": "1.0.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "28495386ab3f1e83c237c6d519508fe7" - }, - "selectr": { - "Package": "selectr", - "Version": "0.4-2", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "methods", - "stringr" - ], - "Hash": "3838071b66e0c566d55cc26bd6e27bf4" - }, - "servr": { - "Package": "servr", - "Version": "0.30", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "httpuv", - "jsonlite", - "mime", - "xfun" - ], - "Hash": "562294886e51319dfad3f3e6e61fb0b8" - }, - "sf": { - "Package": "sf", - "Version": "1.0-16", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "DBI", - "R", - "Rcpp", - "classInt", - "grDevices", - "graphics", - "grid", - "magrittr", - "methods", - "s2", - "stats", - "tools", - "units", - "utils" - ], - "Hash": "ad57b543f7c3fca05213ba78ff63df9b" - }, - "snakecase": { - "Package": "snakecase", - "Version": "0.11.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "stringi", - "stringr" - ], - "Hash": "58767e44739b76965332e8a4fe3f91f1" - }, - "sp": { - "Package": "sp", - "Version": "2.1-4", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "grid", - "lattice", - "methods", - "stats", - "utils" - ], - "Hash": "75940133cca2e339afce15a586f85b11" - }, - "stringi": { - "Package": "stringi", - "Version": "1.8.4", - "Source": "Repository", - "Repository": "RSPM", - "Requirements": [ - "R", - "stats", - "tools", - "utils" - ], - "Hash": "39e1144fd75428983dc3f63aa53dfa91" - }, - "stringr": { - "Package": "stringr", - "Version": "1.5.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "glue", - "lifecycle", - "magrittr", - "rlang", - "stringi", - "vctrs" - ], - "Hash": "960e2ae9e09656611e0b8214ad543207" - }, - "survival": { - "Package": "survival", - "Version": "3.7-0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "Matrix", - "R", - "graphics", - "methods", - "splines", - "stats", - "utils" - ], - "Hash": "5aaa9cbaf4aba20f8e06fdea1850a398" - }, - "sys": { - "Package": "sys", - "Version": "3.4.2", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" - }, - "systemfonts": { - "Package": "systemfonts", - "Version": "1.1.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cpp11", - "lifecycle" - ], - "Hash": "213b6b8ed5afbf934843e6c3b090d418" - }, - "targets": { - "Package": "targets", - "Version": "1.7.1", + "R6": { + "Package": "R6", + "Version": "2.5.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "R6", - "base64url", - "callr", - "cli", - "codetools", - "data.table", - "igraph", - "knitr", - "ps", - "rlang", - "secretbase", - "stats", - "tibble", - "tidyselect", - "tools", - "utils", - "vctrs", - "yaml" + "R" ], - "Hash": "d79b21ef513564351577d7064e1fc4dd" + "Hash": "470851b6d5d0ac559e9d01bb352b4021" }, - "terra": { - "Package": "terra", - "Version": "1.7-78", + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "Rcpp", - "methods" + "R" ], - "Hash": "8f020def0792119cb98bd8f696dab22d" + "Hash": "543776ae6848fde2f48ff3816d0628bc" }, - "testthat": { - "Package": "testthat", - "Version": "3.2.1.1", + "bslib": { + "Package": "bslib", + "Version": "0.8.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "R6", - "brio", - "callr", - "cli", - "desc", - "digest", - "evaluate", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", "jsonlite", "lifecycle", - "magrittr", - "methods", - "pkgload", - "praise", - "processx", - "ps", + "memoise", + "mime", "rlang", - "utils", - "waldo", - "withr" + "sass" ], - "Hash": "3f6e7e5e2220856ff865e4834766bf2b" + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" }, - "textshaping": { - "Package": "textshaping", - "Version": "0.4.0", + "cachem": { + "Package": "cachem", + "Version": "1.1.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "cpp11", - "lifecycle", - "systemfonts" + "fastmap", + "rlang" ], - "Hash": "5142f8bc78ed3d819d26461b641627ce" + "Hash": "cd9a672193789068eb5a2aad65a0dedf" }, - "tibble": { - "Package": "tibble", - "Version": "3.2.1", + "cli": { + "Package": "cli", + "Version": "3.6.3", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "fansi", - "lifecycle", - "magrittr", - "methods", - "pillar", - "pkgconfig", - "rlang", - "utils", - "vctrs" + "utils" ], - "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + "Hash": "b21916dd77a27642b447374a5d30ecf3" }, - "tidygeocoder": { - "Package": "tidygeocoder", - "Version": "1.0.5", + "digest": { + "Package": "digest", + "Version": "0.6.37", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "dplyr", - "httr", - "jsonlite", - "lifecycle", - "progress", - "tibble" + "utils" ], - "Hash": "44fd552dae5b20c4224e895449e3827a" + "Hash": "33698c4b3127fc9f506654607fb73676" }, - "tidygraph": { - "Package": "tidygraph", - "Version": "1.3.1", + "evaluate": { + "Package": "evaluate", + "Version": "1.0.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R6", - "cli", - "cpp11", - "dplyr", - "igraph", - "lifecycle", - "magrittr", - "pillar", - "rlang", - "stats", - "tibble", - "tidyr", - "tools", - "utils" + "R" ], - "Hash": "2149824d406f233b57b087be72c5f163" + "Hash": "3fd29944b231036ad67c3edb32e02201" }, - "tidyr": { - "Package": "tidyr", - "Version": "1.3.1", + "fastmap": { + "Package": "fastmap", + "Version": "1.2.0", "Source": "Repository", "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "cpp11", - "dplyr", - "glue", - "lifecycle", - "magrittr", - "purrr", - "rlang", - "stringr", - "tibble", - "tidyselect", - "utils", - "vctrs" - ], - "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" }, - "tidyselect": { - "Package": "tidyselect", - "Version": "1.2.1", + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "cli", - "glue", - "lifecycle", - "rlang", - "vctrs", - "withr" + "htmltools", + "rlang" ], - "Hash": "829f27b9c4919c16b593794a6344d6c0" + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" }, - "tidyverse": { - "Package": "tidyverse", - "Version": "2.0.0", + "fs": { + "Package": "fs", + "Version": "1.6.5", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "broom", - "cli", - "conflicted", - "dbplyr", - "dplyr", - "dtplyr", - "forcats", - "ggplot2", - "googledrive", - "googlesheets4", - "haven", - "hms", - "httr", - "jsonlite", - "lubridate", - "magrittr", - "modelr", - "pillar", - "purrr", - "ragg", - "readr", - "readxl", - "reprex", - "rlang", - "rstudioapi", - "rvest", - "stringr", - "tibble", - "tidyr", - "xml2" + "methods" ], - "Hash": "c328568cd14ea89a83bd4ca7f54ae07e" + "Hash": "7f48af39fa27711ea5fbd183b399920d" }, - "timechange": { - "Package": "timechange", - "Version": "0.3.0", + "glue": { + "Package": "glue", + "Version": "1.8.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "cpp11" + "methods" ], - "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + "Hash": "5899f1eaa825580172bb56c08266f37c" }, - "tinytex": { - "Package": "tinytex", - "Version": "0.52", + "highr": { + "Package": "highr", + "Version": "0.11", "Source": "Repository", "Repository": "CRAN", "Requirements": [ + "R", "xfun" ], - "Hash": "cfbad971a71f0e27cec22e544a08bc3b" + "Hash": "d65ba49117ca223614f71b60d85b8ab7" }, - "tzdb": { - "Package": "tzdb", - "Version": "0.4.0", + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "cpp11" + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" ], - "Hash": "f561504ec2897f4d46f0c7657e488ae1" + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" }, - "units": { - "Package": "units", - "Version": "0.8-5", + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "Rcpp" + "htmltools" ], - "Hash": "119d19da480e873f72241ff6962ffd83" + "Hash": "5aab57a3bd297eee1c1d862735972182" }, - "utf8": { - "Package": "utf8", - "Version": "1.2.4", + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.9", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R" + "methods" ], - "Hash": "62b65c52671e6665f803ff02954446e9" + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" }, - "uuid": { - "Package": "uuid", - "Version": "1.2-1", + "knitr": { + "Package": "knitr", + "Version": "1.49", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R" + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" ], - "Hash": "34e965e62a41fcafb1ca60e9b142085b" + "Hash": "9fcb189926d93c636dea94fbe4f44480" }, - "vctrs": { - "Package": "vctrs", - "Version": "0.6.5", + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "cli", "glue", - "lifecycle", "rlang" ], - "Hash": "c03fa420630029418f7e6da3667aac4a" + "Hash": "b8552d117e1b808b09a832f589b79035" }, - "viridisLite": { - "Package": "viridisLite", - "Version": "0.4.2", + "memoise": { + "Package": "memoise", + "Version": "2.0.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R" + "cachem", + "rlang" ], - "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" }, - "vroom": { - "Package": "vroom", - "Version": "1.6.5", + "mime": { + "Package": "mime", + "Version": "0.12", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "bit64", - "cli", - "cpp11", - "crayon", - "glue", - "hms", - "lifecycle", - "methods", - "progress", - "rlang", - "stats", - "tibble", - "tidyselect", - "tzdb", - "vctrs", - "withr" + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" ], - "Hash": "390f9315bc0025be03012054103d227c" + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" }, - "waffle": { - "Package": "waffle", - "Version": "1.0.2", + "renv": { + "Package": "renv", + "Version": "1.0.7", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "DT", - "R", - "RColorBrewer", - "curl", - "extrafont", - "ggplot2", - "grid", - "gridExtra", - "gtable", - "htmlwidgets", - "plyr", - "rlang", - "stats", - "stringr", "utils" ], - "Hash": "b4a5d1a3036a7e6a63439b16b3ad39fc" + "Hash": "397b7b2a265bc5a7a06852524dabae20" }, - "waldo": { - "Package": "waldo", - "Version": "0.5.2", + "rlang": { + "Package": "rlang", + "Version": "1.1.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "cli", - "diffobj", - "fansi", - "glue", - "methods", - "rematch2", - "rlang", - "tibble" + "utils" ], - "Hash": "c7d3fd6d29ab077cbac8f0e2751449e6" + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" }, - "withr": { - "Package": "withr", - "Version": "3.0.1", + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.29", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", - "grDevices", - "graphics" + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" ], - "Hash": "07909200e8bbe90426fbfeb73e1e27aa" + "Hash": "df99277f63d01c34e95e3d2f06a79736" }, - "wk": { - "Package": "wk", - "Version": "0.9.2", + "sass": { + "Package": "sass", + "Version": "0.4.9", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R" + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" ], - "Hash": "877644b9b942d429f3708e12c98d1a22" + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" }, - "xaringan": { - "Package": "xaringan", - "Version": "0.30", + "tinytex": { + "Package": "tinytex", + "Version": "0.54", "Source": "Repository", "Repository": "CRAN", "Requirements": [ - "R", - "htmltools", - "knitr", - "rmarkdown", - "servr", "xfun" ], - "Hash": "40a1e30d3fb323f249a5e2fbec50c3b1" + "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" }, "xfun": { "Package": "xfun", - "Version": "0.47", + "Version": "0.49", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -2926,30 +321,7 @@ "stats", "tools" ], - "Hash": "36ab21660e2d095fef0d83f689e0477c" - }, - "xml2": { - "Package": "xml2", - "Version": "1.3.6", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "cli", - "methods", - "rlang" - ], - "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" - }, - "xmlparsedata": { - "Package": "xmlparsedata", - "Version": "1.0.5", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "45e4bf3c46476896e821fc0a408fb4fc" + "Hash": "8687398773806cfff9401a2feca96298" }, "yaml": { "Package": "yaml", @@ -2957,21 +329,6 @@ "Source": "Repository", "Repository": "CRAN", "Hash": "51dab85c6c98e50a18d7551e9d49f76c" - }, - "zoo": { - "Package": "zoo", - "Version": "1.8-12", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "grDevices", - "graphics", - "lattice", - "stats", - "utils" - ], - "Hash": "5c715954112b45499fb1dadc6ee6ee3e" } } }