class: center, middle, inverse ## Iteration <img src="img/hero_wall_pink.png" width="800px"/> ### Kelly McConville .large[Math 241 | Week 8 | Spring 2021] --- ## Announcements/Reminders * Lab 5 is due on Thursday. * Mini Project 2 is due next Thursday. --- ## Looking Ahead... * Schedule is all filled out for the rest of the semester. --- ## Goals for Today * Answer question * Iteration + `for()` loops + `purrr` --- ## Reducing Code Duplication * Have already reduced code duplication by creating functions. ```r library(pdxTrees) get_pdxTrees_parks() %>% rename(ht = Tree_Height, pol = Pollution_Removal_oz) %>% summarize(DBH_cv = sd(DBH, na.rm = TRUE)/mean(DBH, na.rm = TRUE), ht_cv = sd(ht, na.rm = TRUE)/mean(ht, na.rm = TRUE), pol_cv = sd(pol, na.rm = TRUE)/mean(ht, na.rm = TRUE)) ``` ``` ## # A tibble: 1 x 3 ## DBH_cv ht_cv pol_cv ## <dbl> <dbl> <dbl> ## 1 0.650 0.613 0.243 ``` --- ## Reducing Code Duplication * Have already reduced code duplication by creating functions. ```r coef_of_var <- function(x, trim = 0, na.rm = FALSE){ stopifnot(is.numeric(x)) sd(x, na.rm = na.rm)/mean(x, na.rm = na.rm, trim = trim) } get_pdxTrees_parks() %>% rename(ht = Tree_Height, pol = Pollution_Removal_oz) %>% summarize(DBH_cv = coef_of_var(DBH, na.rm = TRUE), ht_cv = coef_of_var(ht, na.rm = TRUE), pol_cv = coef_of_var(pol, na.rm = TRUE)) ``` ``` ## # A tibble: 1 x 3 ## DBH_cv ht_cv pol_cv ## <dbl> <dbl> <dbl> ## 1 0.650 0.613 0.884 ``` --- ## Reducing Code Duplication * Can also reduced duplication through iteration. * **Iteration**: Do the same thing for multiple inputs. -- * `dplyr` style: ```r get_pdxTrees_parks() %>% rename(ht = Tree_Height, pol = Pollution_Removal_oz) %>% summarize_at(.vars = vars(DBH, ht, pol), .funs = coef_of_var, na.rm = TRUE) ``` ``` ## # A tibble: 1 x 3 ## DBH ht pol ## <dbl> <dbl> <dbl> ## 1 0.650 0.613 0.884 ``` --- ## Reducing Code Duplication * Can also reduced duplication through iteration. * **Iteration**: Do the same thing for multiple inputs. * `dplyr` style: ```r get_pdxTrees_parks() %>% rename(ht = Tree_Height, pol = Pollution_Removal_oz) %>% select(DBH, ht, pol) %>% summarize_all(.funs = coef_of_var, na.rm = TRUE) ``` ``` ## # A tibble: 1 x 3 ## DBH ht pol ## <dbl> <dbl> <dbl> ## 1 0.650 0.613 0.884 ``` --- ## Explicit Iterating: `for()` Loops * Language: + Iterating = Looping ```r pdxTrees_coef_of_vars <- rep(NA, 3) pdxTrees <- get_pdxTrees_parks() vars <- c("DBH", "Tree_Height", "Pollution_Removal_oz") for(i in 1:3){ pdxTrees_coef_of_vars[i] <- coef_of_var(pdxTrees[[vars[i]]], na.rm = TRUE) } ``` --- Components: * **Output**: What you want to produce + Good to specify size of output object + Fill with `NA` so clear if hasn't been filled + Determine if you want to store the output as a vector or data frame or something else. ```r # Output options pdxTrees_coef_of_vars <- vector("double", 3) pdxTrees_coef_of_vars ``` ``` ## [1] 0 0 0 ``` ```r pdxTrees_coef_of_vars <- vector() pdxTrees_coef_of_vars ``` ``` ## logical(0) ``` ```r pdxTrees_coef_of_vars <- rep(NA, 3) pdxTrees_coef_of_vars ``` ``` ## [1] NA NA NA ``` --- ## Explicit Iterating: `for()` Loops Components: * **Sequence**: The vector you want to iterate over ```r # Sequence Options 1:3 ``` ``` ## [1] 1 2 3 ``` ```r 1:length(pdxTrees_coef_of_vars) ``` ``` ## [1] 1 2 3 ``` ```r seq_along(pdxTrees_coef_of_vars) ``` ``` ## [1] 1 2 3 ``` --- ## Explicit Iterating: `for()` Loops Components: * **Body**: The code that we iterate. ```r for(i in seq_along(pdxTrees_coef_of_vars)){ pdxTrees_coef_of_vars[i] <- coef_of_var(pdxTrees[[vars[i]]], na.rm = TRUE) } pdxTrees_coef_of_vars ``` ``` ## [1] 0.65 0.61 0.88 ``` --- ## Another example: Building Up a `for()` Loop I want to construct a bootstrap distribution for the mean `DBH` for our sample of Portland trees. * How is looping/iterating helpful here? -- * Let's start with a minimal viable product. ```r mean(sample(pdxTrees$DBH, replace = TRUE)) ``` ``` ## [1] 21 ``` --- ## Another example: Building Up a `for()` Loop * What do I want to store? + What type of R object do I want? + What's the required size? -- ```r boot <- data.frame(avg = rep(NA, 1000)) ``` --- ## Another example: Building Up a `for()` Loop * How do I save this in `boot`? ```r mean(sample(pdxTrees$DBH, replace = TRUE)) ``` ``` ## [1] 21 ``` -- ```r # Set i for now i <- 1 # Option 1 boot[i,] <- mean(sample(pdxTrees$DBH, replace = TRUE)) boot[i,] ``` ``` ## [1] 21 ``` ```r # Option 2 boot$avg[i] <- mean(sample(pdxTrees$DBH, replace = TRUE)) boot$avg[i] ``` ``` ## [1] 21 ``` --- ## Another example: Building Up a `for()` Loop ```r boot <- data.frame(avg = rep(NA, 1000)) for(i in seq_along(boot$avg)){ boot[i,] <- mean(sample(pdxTrees$DBH, replace = TRUE)) } ggplot(data = boot, mapping = aes(x = avg)) + geom_density() ``` <img src="slidesWk8Tu_files/figure-html/unnamed-chunk-13-1.png" width="360" /> --- Can nest loops ```r boot <- data.frame(avg_n_20 = rep(NA, 1000), avg_n_50 = rep(NA, 1000), avg_n_100 = rep(NA, 1000)) sizes <- c(20, 50, 100) for(j in 1:ncol(boot)){ # Generate sample samp <- sample(pdxTrees$DBH, size = sizes[j]) for(i in 1:nrow(boot)){ boot[i,j] <- mean(sample(samp, replace = TRUE)) } } glimpse(boot) ``` ``` ## Rows: 1,000 ## Columns: 3 ## $ avg_n_20 <dbl> 29, 27, 23, 18, 22, 23, 24, 24, 23, 20, 24, 21, 26, 21, 19, … ## $ avg_n_50 <dbl> 25, 20, 24, 20, 22, 23, 19, 25, 19, 23, 22, 22, 22, 22, 19, … ## $ avg_n_100 <dbl> 22, 24, 20, 23, 22, 23, 23, 21, 22, 23, 21, 22, 21, 18, 22, … ``` --- ## Need an Unknown Number of Iterations * If want to iterate `while` some condition is true, then you don't necessarily know how many iterations to do. * Will then use a `while()` loop instead of a `for()` loop. ```r for(i in 1:???){ # Do Something } while(condition) { # Do Something } ``` * Keeps iterating as long as the condition holds. --- ## `while()` Loop Example * Want 20 random draws from a normal that are positive numbers ```r norm_pos <- vector() i <- 0 while(length(norm_pos) < 20){ # Random draw draw <- rnorm(n = 1) # Only add positive draws if(draw > 0){ norm_pos <- c(norm_pos, draw) } } glimpse(norm_pos) ``` ``` ## num [1:20] 0.3787 0.9308 0.0672 1.2588 1.4901 ... ``` --- ## Loops * `while()` loops aren't nearly as common as `for()` loops. * For those who go into statistics research, you will end up often using `for()` loops to run simulation studies. * The internet hates `for()` loops in R. + But they usually aren't as slow as people say. * Alternatives: + `apply()` functions + `purrr` functions ```r library(purrr) ``` --- **Functional Programming**: Functions that allow us to pass other functions as arguments ```r pdxTrees %>% select(DBH, Tree_Height, Pollution_Removal_oz) %>% map(.f = mean, na.rm = TRUE) ``` ``` ## $DBH ## [1] 21 ## ## $Tree_Height ## [1] 66 ## ## $Pollution_Removal_oz ## [1] 18 ``` ```r map(pdxTrees[c("DBH", "Tree_Height")], .f = mean, na.rm = TRUE) ``` ``` ## $DBH ## [1] 21 ## ## $Tree_Height ## [1] 66 ``` ```r pdxTrees %>% select(DBH, Tree_Height, Pollution_Removal_oz) %>% map_dbl(.f = mean, na.rm = TRUE) ``` ``` ## DBH Tree_Height Pollution_Removal_oz ## 21 66 18 ``` --- Problem? ```r pdxTrees %>% select(DBH, Condition, Pollution_Removal_oz) %>% map_dbl(mean, na.rm = TRUE) ``` ``` ## DBH Condition Pollution_Removal_oz ## 21 NA 18 ``` --- ## `map_XXX` Functions * Loop over an R object * Do something * Save results * Function for each type of output: + `map()` makes a list. + `map_lgl()` makes a logical vector. + `map_int()` makes an integer vector. + `map_dbl()` makes a double vector. + `map_chr()` makes a character vector. + `map_df()` makes a data frame. * General structure ```r map_XXX(data, function, arguments) ``` --- * Input: vector + What did it iterate over? ```r x <- c(1, 3, 6, 2) x %>% map_lgl(.f = function(.){. > 5 }) ``` ``` ## [1] FALSE FALSE TRUE FALSE ``` --- * Input: data frame + What did it iterate over? ```r pdxTrees %>% select(DBH, Tree_Height, Pollution_Removal_oz) %>% map_dbl(.f = mean, na.rm = TRUE) ``` ``` ## DBH Tree_Height Pollution_Removal_oz ## 21 66 18 ``` * Lists iterate over its elements. --- ## `map_XXX` Functions ```r pdxTrees %>% select(DBH, Condition, Pollution_Removal_oz) %>% map(class) ``` ``` ## $DBH ## [1] "numeric" ## ## $Condition ## [1] "character" ## ## $Pollution_Removal_oz ## [1] "numeric" ``` ```r pdxTrees %>% select(DBH, Condition, Pollution_Removal_oz) %>% map_chr(class) ``` ``` ## DBH Condition Pollution_Removal_oz ## "numeric" "character" "numeric" ``` --- ```r pdxTrees %>% select(Condition, Native) %>% map_int(n_distinct) ``` ``` ## Condition Native ## 4 3 ``` ```r map_lgl(c(1, 4, NA, 3), is.na) ``` ``` ## [1] FALSE FALSE TRUE FALSE ``` ```r pdxTrees %>% select(DBH, Condition, Native) %>% map_df(as.factor) %>% glimpse() ``` ``` ## Rows: 25,534 ## Columns: 3 ## $ DBH <fct> 37.4, 32.5, 9.7, 10.3, 33.2, 32.1, 28.4, 27.2, 35.2, 32.4, 3… ## $ Condition <fct> Fair, Fair, Fair, Poor, Fair, Fair, Fair, Fair, Fair, Fair, … ## $ Native <fct> Yes, Yes, No, No, Yes, Yes, Yes, Yes, Yes, Yes, Yes, No, Yes… ``` --- ## `map_XXX()` using `...` ```r pdxTrees %>% select(DBH, Tree_Height, Pollution_Removal_oz) %>% map_dbl(mean, na.rm = TRUE, trim = 0.1) ``` ``` ## DBH Tree_Height Pollution_Removal_oz ## 20 63 16 ``` --- ## Anonymous Functions * Anonymous function is a temporary function that you want to feed to `map()`. ```r pdxTrees %>% select(DBH, Tree_Height, Pollution_Removal_oz) %>% map_dbl(.f = function(.x){ mean(.x, na.rm = TRUE) }) ``` ``` ## DBH Tree_Height Pollution_Removal_oz ## 21 66 18 ``` --- ## Anonymous Functions ```r pdxTrees %>% select(Condition, Native, Tree_Height) %>% map_df(.f = function(.x) { data.frame(n_distinct = n_distinct(.x), class = class(.x)) }) ``` ``` ## n_distinct class ## 1 4 character ## 2 3 character ## 3 185 numeric ``` * What useful information did we lose? --- ## Anonymous Functions ```r pdxTrees %>% select(Condition, Native, Tree_Height) %>% map_df(.f = function(.x) { data.frame(n_distinct = n_distinct(.x), class = class(.x)) }, .id = "variable") ``` ``` ## variable n_distinct class ## 1 Condition 4 character ## 2 Native 3 character ## 3 Tree_Height 185 numeric ``` --- ## Anonymous Functions * Function shorthand: `~` ```r pdxTrees %>% select(Condition, Native, Tree_Height) %>% map_df(.f = ~(data.frame(n_distinct = n_distinct(.x), class = class(.x))), .id = "variable") ``` ``` ## variable n_distinct class ## 1 Condition 4 character ## 2 Native 3 character ## 3 Tree_Height 185 numeric ``` --- ## Useful function: `rerun()` ```r boot_mean <- function(x){ samp <- sample(x, replace = TRUE) mean(samp) } boot_mean(pdxTrees$DBH) ``` ``` ## [1] 21 ``` ```r rerun(5, boot_mean(pdxTrees$DBH)) %>% flatten_dbl() ``` ``` ## [1] 21 21 21 21 21 ``` --- ## Scary `list()` to Friendly `data.frame()` ```r # Examples of nested lists library(repurrrsive) ``` * `gh_users`: recursive list + One element for each of the 6 GitHub users + Info on each user * Want to put the login, name and location into a data frame. ```r class(gh_users) ``` ``` ## [1] "list" ``` ```r glimpse(gh_users, max.level = 1) ``` ``` ## List of 6 ## $ :List of 30 ## $ :List of 30 ## $ :List of 30 ## $ :List of 30 ## $ :List of 30 ## $ :List of 30 ``` --- ## Scary `list()` to Friendly `data.frame()` ```r class(gh_users[[1]]) ``` ``` ## [1] "list" ``` ```r glimpse(gh_users[[1]], max.level = 1) ``` ``` ## List of 30 ## $ login : chr "gaborcsardi" ## $ id : int 660288 ## $ avatar_url : chr "https://avatars.githubusercontent.com/u/660288?v=3" ## $ gravatar_id : chr "" ## $ url : chr "https://api.github.com/users/gaborcsardi" ## $ html_url : chr "https://github.com/gaborcsardi" ## $ followers_url : chr "https://api.github.com/users/gaborcsardi/followers" ## $ following_url : chr "https://api.github.com/users/gaborcsardi/following{/other_user}" ## $ gists_url : chr "https://api.github.com/users/gaborcsardi/gists{/gist_id}" ## $ starred_url : chr "https://api.github.com/users/gaborcsardi/starred{/owner}{/repo}" ## $ subscriptions_url : chr "https://api.github.com/users/gaborcsardi/subscriptions" ## $ organizations_url : chr "https://api.github.com/users/gaborcsardi/orgs" ## $ repos_url : chr "https://api.github.com/users/gaborcsardi/repos" ## $ events_url : chr "https://api.github.com/users/gaborcsardi/events{/privacy}" ## $ received_events_url: chr "https://api.github.com/users/gaborcsardi/received_events" ## $ type : chr "User" ## $ site_admin : logi FALSE ## $ name : chr "Gábor Csárdi" ## $ company : chr "Mango Solutions, @MangoTheCat " ## $ blog : chr "http://gaborcsardi.org" ## $ location : chr "Chippenham, UK" ## $ email : chr "csardi.gabor@gmail.com" ## $ hireable : NULL ## $ bio : NULL ## $ public_repos : int 52 ## $ public_gists : int 6 ## $ followers : int 303 ## $ following : int 22 ## $ created_at : chr "2011-03-09T17:29:25Z" ## $ updated_at : chr "2016-10-11T11:05:06Z" ``` --- ## Scary `list()` to Friendly `data.frame()` How can I extract the `login` for the first person? -- ```r gh_users[[1]]["login"] ``` ``` ## $login ## [1] "gaborcsardi" ``` ```r gh_users[[1]][1] ``` ``` ## $login ## [1] "gaborcsardi" ``` --- ## Scary `list()` to Friendly `data.frame()` How can I extract the `login`, `name`, and `location` for the first person? ```r gh_users[[1]][c("login", "name", "location")] ``` ``` ## $login ## [1] "gaborcsardi" ## ## $name ## [1] "Gábor Csárdi" ## ## $location ## [1] "Chippenham, UK" ``` --- ```r names(gh_users[[1]]) ``` ``` ## [1] "login" "id" "avatar_url" ## [4] "gravatar_id" "url" "html_url" ## [7] "followers_url" "following_url" "gists_url" ## [10] "starred_url" "subscriptions_url" "organizations_url" ## [13] "repos_url" "events_url" "received_events_url" ## [16] "type" "site_admin" "name" ## [19] "company" "blog" "location" ## [22] "email" "hireable" "bio" ## [25] "public_repos" "public_gists" "followers" ## [28] "following" "created_at" "updated_at" ``` ```r gh_users[[1]][c(1, 18, 21)] ``` ``` ## $login ## [1] "gaborcsardi" ## ## $name ## [1] "Gábor Csárdi" ## ## $location ## [1] "Chippenham, UK" ``` --- * What do I want to store? + What type of R object do I want? + What's the required size? -- ```r gh_users_df <- data.frame(login = rep(NA, 6), name = rep(NA, 6), location = rep(NA, 6)) dim(gh_users_df) ``` ``` ## [1] 6 3 ``` --- How should I save the information in `gh_users_df`? -- ```r # Set i and j to test i <- 1 j <- 2 # Variable locations locations <- c(1, 18, 21) # Grab the jth variable of the ith person gh_users[[i]][locations[j]] ``` ``` ## $name ## [1] "Gábor Csárdi" ``` ```r # Another option k <- locations[j] gh_users[[i]][k] ``` ``` ## $name ## [1] "Gábor Csárdi" ``` ```r # Store gh_users_df[i, j] <- gh_users[[i]][k] ``` --- ## Scary `list()` to Friendly `data.frame()` How can I extract the `login`, `name`, and `location` for the first person? ```r gh_users_df <- data.frame(login = rep(NA, 6), name = rep(NA, 6), location = rep(NA, 6)) locations <- c(1, 18, 21) for(j in 1:ncol(gh_users_df)){ for(i in 1:nrow(gh_users_df)){ k <- locations[j] gh_users_df[i, j] <- gh_users[[i]][k] } } ``` --- ## Scary `list()` to Friendly `data.frame()` * We did it! ```r gh_users_df ``` ``` ## login name location ## 1 gaborcsardi Gábor Csárdi Chippenham, UK ## 2 jennybc Jennifer (Jenny) Bryan Vancouver, BC, Canada ## 3 jtleek Jeff L. Baltimore,MD ## 4 juliasilge Julia Silge Salt Lake City, UT ## 5 leeper Thomas J. Leeper London, United Kingdom ## 6 masalmon Maëlle Salmon Barcelona, Spain ``` -- * Now how can we do it with `purrr`? --- ## Scary `list()` to Friendly `data.frame()` Let's start with `login`. * What should we feed into `map()`? -- ```r map(gh_users, function(x) x[[1]]) ``` ``` ## [[1]] ## [1] "gaborcsardi" ## ## [[2]] ## [1] "jennybc" ## ## [[3]] ## [1] "jtleek" ## ## [[4]] ## [1] "juliasilge" ## ## [[5]] ## [1] "leeper" ## ## [[6]] ## [1] "masalmon" ``` --- ## Scary `list()` to Friendly `data.frame()` * Shortcut ```r map(gh_users, 1) ``` ``` ## [[1]] ## [1] "gaborcsardi" ## ## [[2]] ## [1] "jennybc" ## ## [[3]] ## [1] "jtleek" ## ## [[4]] ## [1] "juliasilge" ## ## [[5]] ## [1] "leeper" ## ## [[6]] ## [1] "masalmon" ``` --- ## Scary `list()` to Friendly `data.frame()` * Another shortcut ```r map(gh_users, "login") ``` ``` ## [[1]] ## [1] "gaborcsardi" ## ## [[2]] ## [1] "jennybc" ## ## [[3]] ## [1] "jtleek" ## ## [[4]] ## [1] "juliasilge" ## ## [[5]] ## [1] "leeper" ## ## [[6]] ## [1] "masalmon" ``` --- ## Scary `list()` to Friendly `data.frame()` How do we change this to a vector output instead of a list? ```r map(gh_users, "login") ``` ``` ## [[1]] ## [1] "gaborcsardi" ## ## [[2]] ## [1] "jennybc" ## ## [[3]] ## [1] "jtleek" ## ## [[4]] ## [1] "juliasilge" ## ## [[5]] ## [1] "leeper" ## ## [[6]] ## [1] "masalmon" ``` --- ## Scary `list()` to Friendly `data.frame()` How do we change this to a vector output instead of a list? ```r map_chr(gh_users, "login") ``` ``` ## [1] "gaborcsardi" "jennybc" "jtleek" "juliasilge" "leeper" ## [6] "masalmon" ``` --- ## Scary `list()` to Friendly `data.frame()` How do we grab the `login`, `name` and `location` for the first user? -- ```r gh_users[[1]][c("login", "name", "location")] ``` ``` ## $login ## [1] "gaborcsardi" ## ## $name ## [1] "Gábor Csárdi" ## ## $location ## [1] "Chippenham, UK" ``` --- ## Scary `list()` to Friendly `data.frame()` Now for all users? -- * Function: `[` ```r map(gh_users, `[`, c("login", "name", "location")) ``` ``` ## [[1]] ## [[1]]$login ## [1] "gaborcsardi" ## ## [[1]]$name ## [1] "Gábor Csárdi" ## ## [[1]]$location ## [1] "Chippenham, UK" ## ## ## [[2]] ## [[2]]$login ## [1] "jennybc" ## ## [[2]]$name ## [1] "Jennifer (Jenny) Bryan" ## ## [[2]]$location ## [1] "Vancouver, BC, Canada" ## ## ## [[3]] ## [[3]]$login ## [1] "jtleek" ## ## [[3]]$name ## [1] "Jeff L." ## ## [[3]]$location ## [1] "Baltimore,MD" ## ## ## [[4]] ## [[4]]$login ## [1] "juliasilge" ## ## [[4]]$name ## [1] "Julia Silge" ## ## [[4]]$location ## [1] "Salt Lake City, UT" ## ## ## [[5]] ## [[5]]$login ## [1] "leeper" ## ## [[5]]$name ## [1] "Thomas J. Leeper" ## ## [[5]]$location ## [1] "London, United Kingdom" ## ## ## [[6]] ## [[6]]$login ## [1] "masalmon" ## ## [[6]]$name ## [1] "Maëlle Salmon" ## ## [[6]]$location ## [1] "Barcelona, Spain" ``` --- ## Scary `list()` to Friendly `data.frame()` **Finally**: Let's make it into a data frame. * `dfr`: `r` stands for row binding. ```r map_dfr(gh_users, `[`, c("login", "name", "location")) ``` ``` ## # A tibble: 6 x 3 ## login name location ## <chr> <chr> <chr> ## 1 gaborcsardi Gábor Csárdi Chippenham, UK ## 2 jennybc Jennifer (Jenny) Bryan Vancouver, BC, Canada ## 3 jtleek Jeff L. Baltimore,MD ## 4 juliasilge Julia Silge Salt Lake City, UT ## 5 leeper Thomas J. Leeper London, United Kingdom ## 6 masalmon Maëlle Salmon Barcelona, Spain ``` --- ```r gh_users_df <- map_dfr(gh_users, `[`, c("login", "name", "location")) ``` **Versus** -- ```r gh_users_df <- data.frame(login = rep(NA, 6), name = rep(NA, 6), location = rep(NA, 6)) locations <- c(1, 18, 21) for(j in 1:ncol(gh_users_df)){ for(i in 1:nrow(gh_users_df)){ k <- locations[j] gh_users_df[i, j] <- gh_users[[i]][k] } } ``` **Pros/Cons?** --- ```r system.time({gh_users_df <- map_dfr(gh_users, `[`, c("login", "name", "location"))}) ``` ``` ## user system elapsed ## 0.001 0.001 0.002 ``` ```r system.time({gh_users_df <- data.frame(login = rep(NA, 6), name = rep(NA, 6), location = rep(NA, 6)) locations <- c(1, 18, 21) for(j in 1:ncol(gh_users_df)){ for(i in 1:nrow(gh_users_df)){ k <- locations[j] gh_users_df[i, j] <- gh_users[[i]][k] } } }) ``` ``` ## user system elapsed ## 0.011 0.000 0.011 ``` --- ## Closing Sage Thoughts > "But you should never feel bad about using a for loop instead of a map function. The map functions are a step up a tower of abstraction, and it can take a long time to get your head around how they work. The important thing is that you solve the problem that you’re working on, not write the most concise and elegant code (although that’s definitely something you want to strive towards!)." -- Hadley Wickham & Garrett Grolemund