├── .gitignore
├── 01-map.R
├── 02-typed-functions.R
├── 03-challenges.R
├── 04-map2.R
├── 05-purrr-list-columns.R
├── 06-other-features.R
├── LICENSE.md
├── README.md
├── challenges
├── 01-mtcars.R
├── 02-word_count.R
├── 03-starwars.R
├── 04-weather.R
├── 05-swapi.R
└── word_count_files
│ ├── geospatial-README.md
│ ├── nass-README.md
│ └── purrr-workshop-README.md
├── code
├── neiss.R
├── star_wars_tbl.R
└── swapi.R
├── data
├── neiss_by_day.rda
├── planet_lookup.rda
├── swapi.rda
└── weather
│ ├── dec8.csv
│ └── plots
│ └── dec8.png
├── purrr_workshop.Rproj
├── slides.pdf
├── slides_with_transitions.pdf
└── useR-abstract.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | _hide
6 | *.key
--------------------------------------------------------------------------------
/01-map.R:
--------------------------------------------------------------------------------
1 | library(purrr)
2 |
3 | library(repurrrsive)
4 | # includes objects: sw_films, sw_people, sw_vehicles,
5 | # sw_starships, sw_planets & sw_species
6 |
7 | # How many elements are in sw_people?
8 | length(sw_people)
9 |
10 | # Who is the first person in sw_people?
11 | sw_people[[1]] # Luke!
12 |
13 | # A list inside a list
14 | sw_people[1]
15 |
16 | # A list, dropped one level of heirachy
17 | sw_people[[1]]
18 |
19 | # How many starships has each character been on?
20 | map(sw_people, ~ length(.x$starships))
21 |
22 | # For later
23 | planet_lookup <- map_chr(sw_planets, "name") %>%
24 | set_names(map_chr(sw_planets, "url"))
25 | planet_lookup
26 | save(planet_lookup, file = "data/planet_lookup.rda", compress = FALSE)
27 |
--------------------------------------------------------------------------------
/02-typed-functions.R:
--------------------------------------------------------------------------------
1 | library(purrr)
2 | library(repurrrsive)
3 |
4 | # Typed functions
5 | sw_people <- sw_people %>% set_names(map_chr(sw_people, "name"))
6 |
7 | # How many starships has each character been in?
8 | map(sw_people, ~ length(.x[["starships"]]))
9 |
10 | # What color is each characters hair?
11 | map(sw_people, ~ .x[["hair_color"]])
12 |
13 | # Is the character male?
14 | map(sw_people, ~ .x[["gender"]] == "male")
15 |
16 | # How heavy is each character?
17 | map(sw_people, ~ .x[["mass"]])
18 |
19 |
20 |
21 |
22 | # Solutions ---------------------------------------------------------------
23 |
24 | # How many starships has each character been in?
25 | map_int(sw_people, ~ length(.x[["starships"]]))
26 |
27 | # What color is each characters hair?
28 | map_chr(sw_people, ~ .x[["hair_color"]])
29 |
30 | # Is the character male?
31 | map_lgl(sw_people, ~ .x[["gender"]] == "male")
32 |
33 | # How heavy is each character?
34 | map_dbl(sw_people, ~ .x[["mass"]])
35 | # Doesn't work...because we get a string back
36 | map(sw_people, ~ .x[["mass"]])
37 |
38 | # A little risky
39 | map_dbl(sw_people, ~ as.numeric(.x[["mass"]]))
40 |
41 | # Probably want something like:
42 | map_chr(sw_people, ~ .x[["mass"]]) %>%
43 | readr::parse_number(na = "unknown")
44 |
45 |
--------------------------------------------------------------------------------
/03-challenges.R:
--------------------------------------------------------------------------------
1 | # Star wars challenges
2 | library(purrr)
3 | library(repurrrsive)
4 |
5 | # Which film (see films) has the most characters?
6 | map(sw_films, "characters") %>%
7 | map_int(length) %>%
8 | set_names(map_chr(sw_films, "title")) %>%
9 | sort()
10 |
11 | # Which species has the most possible eye colors?
12 | sw_species[[1]]$eye_colors
13 |
14 | map_chr(sw_species, "eye_colors") %>%
15 | strsplit(", ") %>%
16 | map_int(length) %>%
17 | set_names(map_chr(sw_species, "name"))
18 | # this is lazy, what about n/a and unknown?
19 |
20 | # Which planets do we know the least about?
21 | # For one, entry 61
22 | map_lgl(sw_planets[[61]], ~ "unknown" %in% .x) %>%
23 | sum()
24 |
25 | # For all
26 | map_int(sw_planets,
27 | ~ map_lgl(.x, ~ "unknown" %in% .x) %>% sum()) %>%
28 | set_names(map_chr(sw_planets, "name")) %>%
29 | sort(decreasing = TRUE)
30 |
--------------------------------------------------------------------------------
/04-map2.R:
--------------------------------------------------------------------------------
1 | library(repurrrsive)
2 | gap_split_small <- gap_split[1:10]
3 | countries <- names(gap_split_small)
4 |
5 | # For each country create a ggplot of Life Expectancy through time
6 | # with a title
7 |
8 | # For one country
9 | ggplot(gap_split_small[[1]], aes(year, lifeExp)) +
10 | geom_line() +
11 | labs(title = countries[[1]])
12 |
13 | # For all countries
14 | plots <- map2(gap_split_small, countries,
15 | ~ ggplot(.x, aes(year, lifeExp)) +
16 | geom_line() +
17 | labs(title = .y))
18 |
19 | plots[[1]]
20 | # Display all plots
21 | walk(plots, print) # this might take awhile
22 |
23 | # Save all plots
24 | walk2(.x = plots, .y = countries,
25 | ~ ggsave(filename = paste0(.y, ".pdf"), plot = .x))
26 |
27 | # Argh! I didn't want all those pictures in this directory,
28 | # remove them all
29 | file.remove(paste0(countries, ".pdf"))
30 |
31 |
32 |
--------------------------------------------------------------------------------
/05-purrr-list-columns.R:
--------------------------------------------------------------------------------
1 | # star wars people list to data frame
2 | library(tidyverse)
3 | library(repurrrsive)
4 |
5 | # A useful lookup table -----------------------------------------------
6 | film_number_lookup <- map_chr(sw_films, "url") %>%
7 | map(~ stringr::str_split_fixed(.x, "/", 7)[, 6]) %>%
8 | as.numeric() %>%
9 | set_names(map_chr(sw_films, "url"))
10 |
11 | # Putting a few vars in a tibble --------------------------------------
12 | people_tbl <- tibble(
13 | name = sw_people %>% map_chr("name"),
14 | films = sw_people %>% map("films"),
15 | height = sw_people %>% map_chr("height") %>%
16 | readr::parse_number(na = "unknown"),
17 | species = sw_people %>% map_chr("species", .null = NA_character_)
18 | )
19 |
20 | # Turning parts of our list to a tibble -----------------------------------
21 | people_tbl$films
22 |
23 | # Use map with mutate to manipulate list columns
24 | people_tbl <- people_tbl %>%
25 | mutate(
26 | film_numbers = map(films,
27 | ~ film_number_lookup[.x]),
28 | n_films = map_int(films, length)
29 | )
30 |
31 | people_tbl %>% select(name, film_numbers, n_films)
32 |
33 |
34 | # Your Turn ---------------------------------------------------------------
35 |
36 | # Create a new character column that collapses the film numbers in a single string,
37 | # e.g. for Luke " 6, 3, 2, 1, 7"
38 |
39 | people_tbl <- people_tbl %>%
40 | mutate(films_squashed = map_chr(film_numbers, paste,
41 | collapse = ", "))
42 |
43 | people_tbl %>% select(name, n_films, films_squashed)
44 |
45 |
--------------------------------------------------------------------------------
/06-other-features.R:
--------------------------------------------------------------------------------
1 | library(purrr)
2 | library(readr)
3 |
4 | # safely() and transpose() -------------------------------------------------
5 |
6 | urls <- list(
7 | example = "http://example.org",
8 | asdf = "http://asdfasdasdkfjlda"
9 | )
10 |
11 | map(urls, read_lines)
12 |
13 | safe_readlines <- safely(readLines)
14 | safe_readlines
15 |
16 | # Use the safe_readLines() function with map(): html
17 | html <- map(urls, safe_readlines)
18 |
19 | # Call str() on html
20 | str(html)
21 |
22 | # Extract the result from the element that was successful
23 | html[["example"]][["result"]]
24 |
25 | # Extract the error from the element that was unsuccessful
26 | html[["asdf"]][["error"]]
27 |
28 | str(transpose(html))
29 |
30 | # Extract the results: res
31 | res <- transpose(html)[["result"]]
32 |
33 | # Extract the errors: errs
34 | errs <- transpose(html)[["error"]]
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | 
purrr tutorial by Charlotte Wickham is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A purrr tutorial
2 |
3 | This repo hosts the materials for a [purrr](http://purrr.tidyverse.org/) tutorial. The materials currently reflect the version planned for the useR! Brussels, Belgium, July 2017.
4 |
5 | ## Upcoming in-person tutorials
6 |
7 | * [useR!](https://user2017.brussels/) Brussels, Belgium, July 4 2017
8 | * [London R-ladies](https://www.meetup.com/rladies-london/) London, U.K., July 12 2017
9 |
10 | Older versions of the materials, from prior in-person tutorials, are also available:
11 |
12 | * [Cascadia R Conf June 3 2017 (1.75 hours)](https://github.com/cwickham/purrr-tutorial/tree/v0.2)
13 |
14 | * [rstudio::conf Jan 2017 (2.25 hours)](https://github.com/cwickham/purrr-tutorial/tree/v0.1)
15 |
16 |
17 | ## Outline
18 |
19 | Code with a lot of duplication is harder to understand, troubleshoot and maintain. The goal of this tutorial is help you remove duplication in your code by using functions that write `for` loops for you.
20 |
21 | You'll learn to use the functions in the `purrr` package to perform iterative tasks: tasks that look like "for each _____ do _____".
22 |
23 | By the end of the tutorial you'll be writing code that is more readable and easier to update and you'll be ready to solve new iteration problems faster and with fewer mistakes.
24 |
25 | ## Learning Objectives
26 |
27 | By the end of the tutorial, you'll be able to:
28 |
29 | * Move from solving a problem on a single element, to iterating that solution over many elements with `map()`.
30 | * Identify when to use the typed variants of `map()`: `map_lgl()`, `map_int()`, `walk()` etc.
31 | * Iterate over two arguments with `map2()`.
32 | * Leverage `purrr` to get list data into tibbles.
33 | * Use `purrr` to work with list columns in tibbles.
34 |
35 | ## Pre-requisites
36 |
37 | Don't worry if you have never written a `for` loop, used `lapply()`, written your own function or heard of a `tibble`, this tutorial is designed to be accessible to beginners.
38 |
39 | That said, you should be familiar with exploring and subsetting the basic data structures in R including lists and data frames.
40 |
41 | This is a hands-on tutorial, you'll need your laptop with R installed, as well as a few packages:
42 |
43 | ```{r}
44 | install.packages("tidyverse")
45 |
46 | # install.packages("devtools")
47 | devtools::install_github("jennybc/repurrrsive")
48 | ```
49 |
50 | ## License
51 |
52 | 
This purrr tutorial by Charlotte Wickham is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
53 |
--------------------------------------------------------------------------------
/challenges/01-mtcars.R:
--------------------------------------------------------------------------------
1 | # Many models challenge
2 | library(purrr)
3 |
4 | ?mtcars
5 |
6 | # Your Task ---
7 | # Fit a regression model, of mpg against disp, for cars
8 | # of the same number of cylinders. Then summarise those
9 | # models using r^2 and the estimated slope.
10 |
11 | # A list of data frames
12 | mtcars_by_cyl <- mtcars %>% split(mtcars$cyl)
13 | mtcars_by_cyl
14 |
15 | # Fit a regression model to each element
16 |
17 |
18 | # Extract R^2 from each regression model
19 |
20 |
21 | # Extract the slope from each regression model
--------------------------------------------------------------------------------
/challenges/02-word_count.R:
--------------------------------------------------------------------------------
1 | # Word count challenge
2 | library(purrr)
3 |
4 | # Your task ----
5 | # Produce a named numeric vector that contains the word counts for all
6 | # files with a *.md extension:
7 | ## geospatial-README.md nass-README.md purrr-workshop-README.md
8 | ## 232 312 216
9 |
10 | # To get you started
11 | files <- dir("challenges/word_count_files", pattern = "*.md",
12 | full.names = TRUE)
13 | files
14 |
15 | ?readr::read_lines
16 | ?stringi::stri_stats_latex
17 |
--------------------------------------------------------------------------------
/challenges/03-starwars.R:
--------------------------------------------------------------------------------
1 | # Star Wars challenge
2 | library(purrr)
3 |
4 | # Your task -----
5 | # There are only 11 instances of a character using a vehicle.
6 | # Your task is to produce text for each of these instances like
7 | # the following:
8 | # "Luke Skywalker used a Snowspeeder"
9 |
10 | # To get you started
11 | load("data/swapi.rds")
12 |
13 | luke <- people[[1]]
14 |
15 | luke[["vehicles"]]
16 |
17 | vehicles[[5]][["url"]]
18 | vehicles[[5]][["name"]]
19 |
20 |
21 | # Extra challenge -----
22 | # Add the film to your text, e.g.
23 | # "Luke Skywalker used a Snowspeeder in The Empire Strikes Back"
--------------------------------------------------------------------------------
/challenges/04-weather.R:
--------------------------------------------------------------------------------
1 | # Weather data challenge
2 | library(purrr)
3 |
4 | # Your task ---
5 | # Below you will find code that downloads, manipulates, plots and saves
6 | # one day, Dec 8, of weather data for Corvallis. Your job is to rewrite
7 | # this to replicate for Dec 6 - Dec 10
8 |
9 | library(tidyverse)
10 |
11 | # Downloading data --------------------------------------------------------
12 | # note date is part of url
13 | download.file("https://www.wunderground.com/history/airport/KCVO/2016/12/8/DailyHistory.html?format=1", "data/weather/dec8.csv")
14 |
15 | # Read in file -----------------------------------------------------------
16 | col_types <- c("cnnnnnccccccnc")
17 |
18 | dec8 <- read_csv("data/weather/dec8.csv", skip = 1, na = c("-", "N/A"),
19 | col_types = col_types)
20 |
21 | # Add date columns --------------------------------------------------------
22 |
23 | dec8 <- mutate(dec8, year = 2016, month = 12, day = 8)
24 | # add add a datetime variable
25 | dec8 <- mutate(dec8,
26 | datetime = as.POSIXct(strptime(
27 | paste(year, month, day, TimePST, sep = " "), "%Y %m %d %I:%M %p")))
28 |
29 | # Make and save plot -------------------------------------------------------
30 |
31 | qplot(datetime, TemperatureF, data = dec8,
32 | geom = "line") +
33 | ggtitle("Dec 8")
34 | ggsave("data/weather/plots/dec8.png")
35 |
36 |
--------------------------------------------------------------------------------
/challenges/05-swapi.R:
--------------------------------------------------------------------------------
1 | # Star Wars API Challenge
2 | # install_github("ironholds/rwars", ref = "0.5.0")
3 | library(rwars)
4 | library(purrr)
5 |
6 | # Your task ----
7 | # You should use the rwars package to download all the data, i.e. replicate
8 | # what I did to create the data you've been using. That is, at the end
9 | # you should have a people, species, films, vehicles, starships and planets
10 | # lists.
11 |
12 | # Here's a function I wrote that requests the "next" page of
13 | # results, until there is no next page
14 | get_all <- function(x = NULL, old_data = NULL, fun, ...){
15 | data <- fun(x, ...)
16 | next_url <- getElement(data, "next")
17 | if(is_null(next_url)) {
18 | return(append(old_data, list(data)))
19 | } else {
20 | get_all(next_url, append(old_data, list(data)), fun, ...)
21 | }
22 | }
23 |
24 | ?get_all_people
25 |
26 | # For example getting all the people results
27 | people <- get_all(fun = get_all_people, parse_result = TRUE)
28 |
29 | listviewer::jsonedit(people, mode = "view")
30 |
31 | entities <- c("species", "people", "films", "vehicles", "starships",
32 | "planets")
33 |
--------------------------------------------------------------------------------
/challenges/word_count_files/geospatial-README.md:
--------------------------------------------------------------------------------
1 | # geospatial
2 |
3 | [](https://travis-ci.org/cwickham/geospatial)
4 |
5 | An R package with the data sets used in the [DataCamp](www.datacamp.com) course [Working with Geospatial Data in R](https://www.datacamp.com/courses/working-with-geospatial-data-in-r).
6 |
7 | ## Installation
8 |
9 | `geospatial` isn't on CRAN but you can get it with:
10 |
11 | ```R
12 | # install.packages("devtools")
13 | devtools::install_github("cwickham/geospatial")
14 | ```
15 |
16 | ## Usage
17 |
18 | Datasets in this package have the same names as those in the course. For example,
19 | to replicate the very first plot in the first video:
20 |
21 | ```R
22 | library(geospatial)
23 | library(ggplot2)
24 | ?sales # Chapter 1 sales data
25 | head(sales)
26 |
27 | ggplot(sales, aes(lon, lat)) +
28 | geom_point()
29 | ```
30 |
31 | ## Contents
32 |
33 | This package contains the data sets corresponding to each chapter in the course
34 | as listed below.
35 |
36 | Chapter 1:
37 |
38 | * `sales` - point data on sales in Corvallis
39 | * `ward_sales` - sales summarized at the ward level
40 | * `preds` - a grid of predictions over Corvallis
41 |
42 | Chapter 2:
43 |
44 | * `countries_sp`
45 | * `countries_spdf`
46 | * `tiny_countries_spdf`
47 |
48 | Chapter 3:
49 |
50 | * `pop`
51 | * `pop_by_age`
52 | * `prop_by_age`
53 | * `migration`
54 | * `land_cover`
55 |
56 | Chapter 4:
57 |
58 | * `neighborhoods`
59 | * `nyc_income`
60 | * `water`
61 | * `income_grid`
62 |
63 | ## Citations
64 |
65 | If you use the data in this package please cite both this package and
66 | the original data source as listed in the relevant help file.
67 |
68 | ## Data generation
69 |
70 | If you are interested in how the data sets were constructed you'll find the
71 | scripts in the `data-raw` directory.
72 |
--------------------------------------------------------------------------------
/challenges/word_count_files/nass-README.md:
--------------------------------------------------------------------------------
1 | # nass
2 |
3 | Data from: http://www.nhtsa.gov/Data/National-Automotive-Sampling-System-(NASS)
4 |
5 | In development: **Currently only CDS from 2015**
6 |
7 | ## Crashworthiness Data System (CDS)
8 | http://www.nhtsa.gov/Data/National-Automotive-Sampling-System-(NASS)/NASS-Crashworthiness-Data-System
9 |
10 | > NASS CDS has detailed data on a representative, random sample of thousands of minor, serious, and fatal crashes. Field research teams located at Primary Sampling Units (PSU's) across the country study about 5,000 crashes a year involving passenger cars, light trucks, vans, and utility vehicles. Trained crash investigators obtain data from crash sites, studying evidence such as skid marks, fluid spills, broken glass, and bent guard rails. They locate the vehicles involved, photograph them, measure the crash damage, and identify interior locations that were struck by the occupants. These researchers follow up on their on-site investigations by interviewing crash victims and reviewing medical records to determine the nature and severity of injuries.
11 |
12 | ## General Estimates System (GES)
13 | http://www.nhtsa.gov/Data/National-Automotive-Sampling-System-(NASS)/NASS-General-Estimates-System
14 |
15 | > In order for a crash to be eligible for the GES sample a police accident report (PAR) must be completed, it must involve at least one motor vehicle traveling on a traffic way, and the result must be property damage, injury, or death.
16 | >
17 | > These accident reports are chosen from 60 areas that reflect the geography, roadway mileage, population, and traffic density of the U.S. GES data collectors make weekly visits to approximately 400 police jurisdictions in the 60 areas across the United States, where they randomly sample about 50,000 PARs each year. The collectors obtain copies of the PARs and send them to a central contractor for coding. No other data are collected beyond the selected PARs.
18 |
19 | ## To Do List
20 |
21 | * Tidy `cds_acc_desc` to have one record per case (e.g. concatenate multi-row description)
22 | * Improve documentation
23 | * Add more years of CDS data
24 | * Add GES data
25 |
26 |
--------------------------------------------------------------------------------
/challenges/word_count_files/purrr-workshop-README.md:
--------------------------------------------------------------------------------
1 | # Abstract
2 |
3 | Happy R users purrr: using functional programming to solve iteration problems
4 |
5 | Code with a lot of duplication is harder to understand, troubleshoot and maintain. The goal of this tutorial is help you remove duplication in your code with two strategies: writing your own functions and using functions that write `for` loops for you.
6 |
7 | We'll assume you already know the mechanics of defining a function but give you some tips on when and how to write a function emphasizing that good functions aren't just correct, they are also understandable. Then we'll talk about using functions as inputs to other functions, a key idea in functional programming. In the rest of the tutorial you'll learn to use the functions in the `purrr` package that remove code that is duplicated across for loops. By the end of the tutorial you'll be writing code that is more readable and easier to update and you'll be ready to solve new iteration problems faster and with fewer mistakes.
8 |
9 | # Prerequisites
10 |
11 | * You know how to define your own function, and have written a few.
12 | * You know how to write a for loop.
13 | * You have seen `sapply()` and `lapply()`, but it's OK if you don't use them (or don't know why you should use them).
14 |
--------------------------------------------------------------------------------
/code/neiss.R:
--------------------------------------------------------------------------------
1 | library(neiss)
2 | library(tidyverse)
3 | library(lubridate)
4 |
5 |
6 | # Add time vars -----------------------------------------
7 |
8 | injuries <- injuries %>% mutate(
9 | wday = factor(wday(trmt_date, label = TRUE)),
10 | month = factor(month(trmt_date, label = TRUE))
11 | )
12 |
13 | # most indicated products -------------------------------------------------
14 |
15 | # can use products df to look up
16 | product_lookup <- products$title %>% set_names(products$code)
17 |
18 | product_counts <- table(c(injuries$prod1, injuries$prod2))
19 | # at least 10000 occurances
20 | common_codes <- names(product_counts[product_counts > 50000])
21 | common_names <- product_lookup[common_codes]
22 |
23 |
24 | # split by common products -------------------------------------------------
25 | # SLOW! probably find a better way, summarise first?
26 | by_prod <- map(common_codes, ~ filter(injuries, prod1 == .x | prod2 == .x))
27 |
28 | # summarise to day counts
29 | per_day <- map(by_prod,
30 | ~ .x %>% group_by(trmt_date, month, wday) %>% summarise(count = n()))
31 |
32 | per_day <- set_names(per_day, common_codes)
33 | save(per_day, common_codes, common_names, file = "data/neiss_by_day.rda",
34 | compress = TRUE)
35 |
36 |
--------------------------------------------------------------------------------
/code/star_wars_tbl.R:
--------------------------------------------------------------------------------
1 | # star wars people list to data frame
2 | library(purrr)
3 | library(tibble)
4 | library(dplyr)
5 |
6 | load("data/swapi.rda")
7 |
8 | # Luke has all fields
9 | names(people[[1]])
10 |
11 | # Putting it all in a tibble
12 | people_tbl <- tibble(
13 | name = people %>% map_chr("name"),
14 | height = people %>% map_chr("height") %>% readr::parse_number(na = "unknown"),
15 | mass = people %>% map_chr("mass") %>% readr::parse_number(na = "unknown"),
16 | hair_color = people %>% map_chr("hair_color"),
17 | skin_color = people %>% map_chr("skin_color"),
18 | eye_color = people %>% map_chr("eye_color"),
19 | birth_year = people %>% map_chr("birth_year"),
20 | gender = people %>% map_chr("gender"),
21 | homeworld = people %>% map_chr("homeworld"),
22 | films = people %>% map("films"),
23 | species = people %>% map_chr("species", .null = NA_character_),
24 | vehicles = people %>% map("vehicles"),
25 | starships = people %>% map("starships"),
26 | created = people %>% map_chr("created") %>% readr::parse_datetime(),
27 | edited = people %>% map_chr("edited") %>% readr::parse_datetime(),
28 | url = people %>% map_chr("url")
29 | )
30 |
31 |
32 | # Some useful lookup tables -----------------------------------------------
33 |
34 | film_lookup <- map_chr(films, "title") %>% set_names(map_chr(films, "url"))
35 | film_number_lookup <- stringr::str_split_fixed(names(film_lookup), "/", 7)[, 6] %>%
36 | as.numeric() %>%
37 | set_names(map_chr(films, "url"))
38 |
39 |
40 | # use map() on a list column ----------------------------------------------
41 |
42 | # finding film info for each person
43 | people_tbl <- people_tbl %>%
44 | mutate(
45 | n_films = map_int(films, length),
46 | film_names = map(films, ~ film_lookup[.x]),
47 | film_numbers = map(films, ~ film_number_lookup[.x])
48 | )
49 |
50 | people_tbl %>% select(n_films, film_names)
51 | people_tbl$film_names
52 |
53 | # Your Turn ---------------------------------------------------------------
54 |
55 | # Create a new character column that collapses the film numbers in a single string,
56 | # e.g. for Luke " 6, 3, 2, 1, 7"
57 |
58 | # Do it for luke
59 | paste(people_tbl$film_numbers[[1]],
60 | collapse = ", ")
61 |
62 | # Do it for all
63 | people_tbl <- people_tbl %>%
64 | mutate(films_squashed = map_chr(film_numbers,
65 | ~ paste(.x, collapse = ", ")))
66 |
67 | people_tbl %>% select(name, films_squashed)
68 |
69 | # In order would be better
70 | people_tbl <- people_tbl %>%
71 | mutate(films_squashed = map_chr(film_numbers,
72 | ~ paste(sort(.x), collapse = ", ")))
73 |
74 | people_tbl %>% select(name, films_squashed)
75 |
--------------------------------------------------------------------------------
/code/swapi.R:
--------------------------------------------------------------------------------
1 | ## http://swapi.co/
2 | # install.packages("rwars")
3 | library(rwars)
4 | library(purrr)
5 |
6 | # recursive function to travel down next pages
7 | get_all <- function(x = NULL, old_data = NULL, fun, ...){
8 | data <- fun(x, ...)
9 | next_url <- getElement(data, "next")
10 | if(is_null(next_url)) {
11 | return(append(old_data, list(data)))
12 | } else {
13 | get_all(next_url, append(old_data, list(data)), fun, ...)
14 | }
15 | }
16 |
17 | entities <- c("species", "people", "films", "vehicles", "starships",
18 | "planets")
19 | entity_funs <- map(paste0("get_all_", entities), match.fun)
20 |
21 | all_ents <- map(entity_funs, ~ get_all(fun = .x, parse_result = TRUE))
22 |
23 | all_ents_res <- map(all_ents,
24 | ~ transpose(.x)[["results"]] %>% flatten())
25 |
26 | map2(entities, all_ents, ~ assign(.x, .y, envir = globalenv()))
27 |
28 | save(list = entities, file = "data/swapi.rda", compress = FALSE)
29 |
--------------------------------------------------------------------------------
/data/neiss_by_day.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/data/neiss_by_day.rda
--------------------------------------------------------------------------------
/data/planet_lookup.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/data/planet_lookup.rda
--------------------------------------------------------------------------------
/data/swapi.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/data/swapi.rda
--------------------------------------------------------------------------------
/data/weather/dec8.csv:
--------------------------------------------------------------------------------
1 |
2 | TimePST,TemperatureF,Dew PointF,Humidity,Sea Level PressureIn,VisibilityMPH,Wind Direction,Wind SpeedMPH,Gust SpeedMPH,PrecipitationIn,Events,Conditions,WindDirDegrees,DateUTC
3 | 12:15 AM,28.4,24.8,86,30.05,10.0,NNW,6.9,-,N/A,,Unknown,330,2016-12-08 08:15:00
4 | 12:35 AM,28.4,24.8,86,30.04,10.0,NW,4.6,-,N/A,,Unknown,310,2016-12-08 08:35:00
5 | 12:55 AM,28.4,24.8,86,30.04,10.0,WNW,6.9,-,N/A,,Unknown,300,2016-12-08 08:55:00
6 | 1:15 AM,28.4,24.8,86,30.04,10.0,NW,5.8,-,N/A,,Unknown,310,2016-12-08 09:15:00
7 | 1:35 AM,28.4,24.8,86,30.03,10.0,NW,5.8,-,N/A,,Unknown,310,2016-12-08 09:35:00
8 | 1:55 AM,28.4,24.8,86,30.02,10.0,WNW,5.8,-,N/A,,Unknown,300,2016-12-08 09:55:00
9 | 2:15 AM,30.2,24.8,80,30.01,10.0,WNW,4.6,-,N/A,,Unknown,300,2016-12-08 10:15:00
10 | 2:35 AM,28.4,24.8,86,30.00,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-08 10:35:00
11 | 2:55 AM,28.4,24.8,86,30.00,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-08 10:55:00
12 | 3:15 AM,28.4,24.8,86,29.99,10.0,West,3.5,-,N/A,,Unknown,280,2016-12-08 11:15:00
13 | 3:35 AM,30.2,24.8,80,29.98,10.0,NW,4.6,-,N/A,,Unknown,320,2016-12-08 11:35:00
14 | 3:55 AM,30.2,26.6,86,29.97,10.0,WNW,3.5,-,N/A,,Unknown,290,2016-12-08 11:55:00
15 | 4:15 AM,30.2,26.6,86,29.97,10.0,NW,4.6,-,N/A,,Unknown,310,2016-12-08 12:15:00
16 | 4:35 AM,30.2,24.8,80,29.96,10.0,NW,5.8,-,N/A,,Unknown,320,2016-12-08 12:35:00
17 | 4:55 AM,30.2,26.6,86,29.96,10.0,WNW,4.6,-,N/A,,Unknown,300,2016-12-08 12:55:00
18 | 5:15 AM,30.2,26.6,86,29.95,10.0,WNW,5.8,-,N/A,,Unknown,300,2016-12-08 13:15:00
19 | 5:35 AM,30.2,26.6,86,29.94,10.0,NW,3.5,-,N/A,,Unknown,320,2016-12-08 13:35:00
20 | 5:55 AM,30.2,26.6,86,29.94,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-08 13:55:00
21 | 6:15 AM,30.2,24.8,80,29.94,10.0,NW,3.5,-,N/A,,Unknown,320,2016-12-08 14:15:00
22 | 6:35 AM,30.2,26.6,86,29.94,10.0,WNW,3.5,-,N/A,,Unknown,290,2016-12-08 14:35:00
23 | 6:55 AM,30.2,26.6,86,29.94,10.0,NW,4.6,-,N/A,,Unknown,320,2016-12-08 14:55:00
24 | 7:15 AM,30.2,26.6,86,29.94,7.0,NW,4.6,-,N/A,,Unknown,320,2016-12-08 15:15:00
25 | 7:35 AM,30.2,26.6,86,29.93,9.0,NW,3.5,-,N/A,,Unknown,320,2016-12-08 15:35:00
26 | 7:55 AM,30.2,26.6,86,29.93,5.0,North,5.8,-,N/A,,Unknown,360,2016-12-08 15:55:00
27 | 8:15 AM,32.0,28.4,87,29.92,3.0,North,5.8,-,N/A,,Unknown,360,2016-12-08 16:15:00
28 | 8:35 AM,32.0,28.4,87,29.92,2.5,North,5.8,-,0.01,,Unknown,10,2016-12-08 16:35:00
29 | 8:55 AM,32.0,28.4,87,29.91,1.5,North,3.5,-,0.02,,Unknown,360,2016-12-08 16:55:00
30 | 9:15 AM,32.0,28.4,87,29.91,2.0,North,3.5,-,0.06,,Unknown,350,2016-12-08 17:15:00
31 | 9:35 AM,33.8,30.2,87,29.90,2.5,North,3.5,-,0.09,,Unknown,360,2016-12-08 17:35:00
32 | 9:55 AM,33.8,30.2,87,29.90,2.0,North,4.6,-,0.13,,Unknown,360,2016-12-08 17:55:00
33 | 10:15 AM,33.8,30.2,87,29.90,1.2,Calm,Calm,-,0.04,,Unknown,0,2016-12-08 18:15:00
34 | 10:35 AM,33.8,30.2,87,29.88,1.5,NNE,5.8,-,0.08,,Unknown,30,2016-12-08 18:35:00
35 | 10:55 AM,33.8,30.2,87,29.88,3.0,NNE,6.9,-,0.10,,Unknown,20,2016-12-08 18:55:00
36 | 11:15 AM,33.8,30.2,87,29.88,2.0,NNE,5.8,-,0.01,,Unknown,20,2016-12-08 19:15:00
37 | 11:35 AM,33.8,30.2,87,29.87,1.8,North,8.1,-,0.06,,Unknown,10,2016-12-08 19:35:00
38 | 11:55 AM,33.8,30.2,87,29.87,2.5,NNE,4.6,-,0.10,,Unknown,20,2016-12-08 19:55:00
39 | 12:15 PM,33.8,30.2,87,29.86,2.5,NNE,4.6,-,0.02,,Unknown,20,2016-12-08 20:15:00
40 | 12:35 PM,33.8,30.2,87,29.85,1.8,North,4.6,-,0.03,,Unknown,10,2016-12-08 20:35:00
41 | 12:55 PM,33.8,32.0,93,29.84,2.0,Calm,Calm,-,0.03,,Unknown,0,2016-12-08 20:55:00
42 | 1:15 PM,35.6,32.0,87,29.84,1.8,Calm,Calm,-,0.01,,Unknown,0,2016-12-08 21:15:00
43 | 1:35 PM,35.6,32.0,87,29.83,1.8,Calm,Calm,-,0.02,,Unknown,0,2016-12-08 21:35:00
44 | 1:55 PM,35.6,32.0,87,29.83,1.8,NE,3.5,-,0.04,,Unknown,40,2016-12-08 21:55:00
45 | 2:15 PM,35.6,33.8,93,29.83,1.8,Calm,Calm,-,0.01,,Unknown,0,2016-12-08 22:15:00
46 | 2:35 PM,35.6,32.0,87,29.83,2.0,ENE,3.5,-,0.03,,Unknown,60,2016-12-08 22:35:00
47 | 2:55 PM,35.6,32.0,87,29.83,2.5,East,4.6,-,0.05,,Unknown,90,2016-12-08 22:55:00
48 | 3:15 PM,35.6,32.0,87,29.83,2.0,ENE,6.9,-,0.01,,Unknown,60,2016-12-08 23:15:00
49 | 3:35 PM,35.6,30.2,81,29.83,1.5,NE,6.9,-,0.02,,Unknown,50,2016-12-08 23:35:00
50 | 3:55 PM,35.6,30.2,81,29.84,4.0,NE,6.9,-,0.05,,Unknown,50,2016-12-08 23:55:00
51 | 4:15 PM,35.6,30.2,81,29.84,2.5,ENE,6.9,-,0.02,,Unknown,60,2016-12-09 00:15:00
52 | 4:35 PM,35.6,30.2,81,29.85,5.0,ENE,4.6,-,0.03,,Unknown,70,2016-12-09 00:35:00
53 | 4:55 PM,35.6,30.2,81,29.86,4.0,East,5.8,-,0.03,,Unknown,90,2016-12-09 00:55:00
54 | 5:15 PM,35.6,30.2,81,29.86,2.5,East,4.6,-,N/A,,Unknown,80,2016-12-09 01:15:00
55 | 5:35 PM,35.6,30.2,81,29.87,2.5,East,5.8,-,0.01,,Unknown,90,2016-12-09 01:35:00
56 | 5:55 PM,35.6,30.2,81,29.88,4.0,East,5.8,-,0.01,,Unknown,90,2016-12-09 01:55:00
57 | 6:15 PM,35.6,30.2,81,29.88,3.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 02:15:00
58 | 6:35 PM,35.6,30.2,81,29.89,3.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 02:35:00
59 | 6:55 PM,35.6,30.2,81,29.89,3.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 02:55:00
60 | 7:15 PM,35.6,30.2,81,29.91,4.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 03:15:00
61 | 7:35 PM,35.6,30.2,81,29.91,4.0,ENE,3.5,-,0.01,,Unknown,70,2016-12-09 03:35:00
62 | 7:55 PM,35.6,30.2,81,29.93,7.0,ENE,3.5,-,0.01,,Unknown,60,2016-12-09 03:55:00
63 | 8:15 PM,35.6,32.0,87,29.94,4.0,East,3.5,-,N/A,,Unknown,80,2016-12-09 04:15:00
64 | 8:35 PM,35.6,30.2,81,29.95,10.0,East,4.6,-,N/A,,Unknown,80,2016-12-09 04:35:00
65 | 8:55 PM,35.6,30.2,81,29.96,10.0,East,3.5,-,N/A,,Unknown,90,2016-12-09 04:55:00
66 | 9:15 PM,35.6,30.2,81,29.98,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 05:15:00
67 | 9:35 PM,35.6,30.2,81,29.97,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 05:35:00
68 | 9:55 PM,35.6,30.2,81,29.98,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 05:55:00
69 | 10:15 PM,35.6,30.2,81,29.99,10.0,ENE,3.5,-,N/A,,Unknown,60,2016-12-09 06:15:00
70 | 10:35 PM,35.6,32.0,87,29.99,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 06:35:00
71 | 10:55 PM,35.6,30.2,81,30.01,10.0,Calm,Calm,-,N/A,,Unknown,0,2016-12-09 06:55:00
72 | 11:15 PM,35.6,32.0,87,30.02,5.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 07:15:00
73 | 11:35 PM,35.6,32.0,87,30.01,10.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 07:35:00
74 | 11:55 PM,35.6,32.0,87,30.02,10.0,Calm,Calm,-,0.01,,Unknown,0,2016-12-09 07:55:00
75 |
--------------------------------------------------------------------------------
/data/weather/plots/dec8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/data/weather/plots/dec8.png
--------------------------------------------------------------------------------
/purrr_workshop.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: Default
4 | SaveWorkspace: Default
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: knitr
13 | LaTeX: pdfLaTeX
14 |
--------------------------------------------------------------------------------
/slides.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/slides.pdf
--------------------------------------------------------------------------------
/slides_with_transitions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwickham/purrr-tutorial/98e9a5913535dbbabcf1889ebf2cc716f66a7b41/slides_with_transitions.pdf
--------------------------------------------------------------------------------
/useR-abstract.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Solving iteration problems with purrr"
3 | author: |
4 | | Charlotte Wickham^1^
5 | |
6 | | 1. Oregon State University
7 | output: html_document
8 | ---
9 |
10 | **Keywords**: purrr, tidyverse, tibble
11 |
12 | ## Outline
13 |
14 | Code with a lot of duplication is harder to understand, troubleshoot and maintain. The goal of this tutorial is help you remove duplication in your code by using functions that write `for` loops for you.
15 |
16 | You'll learn to use the functions in the `purrr` package to perform iterative tasks: tasks that look like "for each _____ do _____".
17 |
18 | By the end of the tutorial you'll be writing code that is more readable and easier to update and you'll be ready to solve new iteration problems faster and with fewer mistakes.
19 |
20 | ## Learning Objectives
21 |
22 | By the end of the tutorial, you'll be able to:
23 |
24 | * Move from solving a problem on a single element, to iterating that solution over many elements with `map()`.
25 | * Identify when to use the typed variants of `map()`: `map_lgl()`, `map_int()`, `walk()` etc.
26 | * Iterate over two arguments with `map2()`.
27 | * Leverage `purrr` to get list data into tibbles.
28 | * Use `purrr` to work with list columns in tibbles.
29 |
30 | ## Pre-requisites
31 |
32 | Don't worry if you have never written a `for` loop, used `lapply()`, written your own function or heard of a `tibble`, this tutorial is designed to be accessible to beginners.
33 |
34 | That said, you should be familiar with exploring and subsetting the basic data structures in R including lists and data frames.
35 |
36 | This is a hands-on tutorial, you'll need your laptop with R and `purrr` installed. Check http://bit.ly/purrr-user17 closer to the date for more details.
37 |
38 | ## Instructor Info
39 |
40 | Charlotte Wickham
41 |
42 | * [cwickham@gmail.com](cwickham@gmail.com)
43 | * [cwick.co.nz](http://www.cwick.co.nz)
44 | * @[cvwickham](http://www.twitter.com/cvwickham)
45 |
46 |
--------------------------------------------------------------------------------