class: center, middle, inverse, title-slide .title[ # Iteration ] .subtitle[ ##
Data Science in a Box ] .author[ ###
datasciencebox.org
] --- layout: true <div class="my-footer"> <span> <a href="https://datasciencebox.org" target="_blank">datasciencebox.org</a> </span> </div> --- class: middle # First Minister's COVID speeches --- ## š Start with <img src="img/fm-speeches.png" width="75%" style="display: block; margin: auto;" /> --- ## End with š ``` ## # A tibble: 218 Ć 6 ## title date location abstract text url ## <chr> <date> <chr> <chr> <chr> <chr> ## 1 Coronavirus (COVID-1ā¦ 2021-04-20 St Andrā¦ Statemeā¦ "Gooā¦ httpā¦ ## 2 Coronavirus (COVID-1ā¦ 2021-04-13 St Andrā¦ Statemeā¦ "Thaā¦ httpā¦ ## 3 Coronavirus (COVID-1ā¦ 2021-04-06 St Andrā¦ Statemeā¦ "Gooā¦ httpā¦ ## 4 Coronavirus (COVID-1ā¦ 2021-03-30 St Andrā¦ Statemeā¦ "Thaā¦ httpā¦ ## 5 Coronavirus (COVID-1ā¦ 2021-03-24 Scottisā¦ Statemeā¦ "Thaā¦ httpā¦ ## 6 Coronavirus (Covid-1ā¦ 2021-03-23 The Scoā¦ Statemeā¦ "Preā¦ httpā¦ ## 7 Coronavirus (COVID-1ā¦ 2021-03-18 Scottisā¦ Statemeā¦ "Thaā¦ httpā¦ ## 8 Coronavirus (COVID-1ā¦ 2021-03-17 St Andrā¦ Statemeā¦ "\nGā¦ httpā¦ ## 9 Coronavirus (COVID-1ā¦ 2021-03-16 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## 10 Coronavirus (COVID-1ā¦ 2021-03-15 St Andrā¦ Statemeā¦ "\nGā¦ httpā¦ ## 11 Coronavirus (COVID-1ā¦ 2021-03-11 Scottisā¦ Statemeā¦ "I cā¦ httpā¦ ## 12 Coronavirus (COVID-1ā¦ 2021-03-09 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## 13 Coronavirus (COVID-1ā¦ 2021-03-05 Scottisā¦ Parliamā¦ "Helā¦ httpā¦ ## 14 Coronavirus (COVID-1ā¦ 2021-03-04 Scottisā¦ Parliamā¦ "I wā¦ httpā¦ ## 15 Coronavirus (COVID-1ā¦ 2021-03-02 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## # ā¦ with 203 more rows ``` --- ## Define `scrape_speech()` .pull-left-wide[ .small[ ```r scrape_speech <- function(url) { speech_page <- read_html(url) title <- speech_page %>% html_node(".article-header__title") %>% html_text() date <- speech_page %>% html_node(".content-data__list:nth-child(1) strong") %>% html_text() %>% dmy() location <- speech_page %>% html_node(".content-data__list+ .content-data__list strong") %>% html_text() abstract <- speech_page %>% html_node(".leader--first-para p") %>% html_text() text <- speech_page %>% html_nodes("#preamble p") %>% html_text() %>% list() tibble( title = title, date = date, location = location, abstract = abstract, text = text, url = url ) } ``` ] ] --- ## Use `scrape_speech()` ```r url_26_oct <- "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-26-october/" scrape_speech(url = url_26_oct) ``` ``` ## # A tibble: 1 Ć 6 ## title date location abstract text url ## <chr> <date> <chr> <chr> <list> <chr> ## 1 <NA> NA <NA> <NA> <chr [47]> https://www.gov.scotā¦ ``` ```r url_23_oct <- "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-23-october/" scrape_speech(url = url_23_oct) ``` ``` ## # A tibble: 1 Ć 6 ## title date location abstract text url ## <chr> <date> <chr> <chr> <list> <chr> ## 1 <NA> NA <NA> <NA> <chr [134]> https://www.gov.scoā¦ ``` --- class: middle # Inputs --- ## Inputs .question[ You now have a function that will scrape the relevant info on speeches given the URL of the page of the speech. Where can we get a list of URLs of each of the speeches? ] <img src="img/fm-speeches-links.png" width="60%" style="display: block; margin: auto;" /> --- ## All URLs ```r all_speeches_page <- read_html("https://www.gov.scot/collections/first-ministers-speeches/") all_speeches_page %>% html_nodes(".collections-list a") %>% html_attr("href") ``` ``` ## [1] "/publications/gathering-first-ministers-speech-15-june-2022/" ## [2] "/publications/scdi-forum-first-ministers-speech-13-june-2022/" ## [3] "/publications/queens-platinum-jubilee-debate-first-ministers-statement/" ## [4] "/publications/federation-small-businesses-awards-2022-first-ministers-speech-19-2022/" ## [5] "/publications/scotlands-place-world-first-ministers-speech-16-2022-1/" ## [6] "/publications/energy-conference-first-ministers-speech-11-2022/" ## [7] "/publications/fm-statement-parliament-march-30-2022/" ## [8] "/publications/first-minister-speech-national-economic-forum-2022/" ## [9] "/publications/ukraine-update-first-ministers-statement-16-march-2022/" ## [10] "/publications/fm-statement-parliament-march-15-2022/" ... ``` --- ## COVID-19 URLs *fragments* ```r all_speeches_page %>% html_nodes(".collections-list a") %>% html_attr("href") %>% str_subset("covid-19") ``` ``` ## [1] "/publications/coronavirus-covid-19-update-first-ministers-speech-tuesday-22-february-2022/" ## [2] "/publications/coronavirus-covid-19-update-first-ministers-statement-8-february-2022/" ## [3] "/publications/coronavirus-covid-19-update-first-ministers-statement-1-february-2022/" ## [4] "/publications/coronavirus-covid-19-update-first-ministers-statement-25-january-2022/" ## [5] "/publications/coronavirus-covid-19-update-first-ministers-statement-18-january-2022/" ## [6] "/publications/coronavirus-covid-19-update-first-ministers-statement-11-january-2022/" ## [7] "/publications/coronavirus-covid-19-update-first-ministers-statement-5-january-2022/" ## [8] "/publications/coronavirus-covid-19-update-first-ministers-statement-21-december-2021/" ## [9] "/publications/coronavirus-covid-19-update-first-ministers-speech-17-december-2021/" ## [10] "/publications/coronavirus-covid-19-update-first-ministers-statement-14-december-2021/" ... ``` --- ## COVID-19 URLs ```r all_speeches_page %>% html_nodes(".collections-list a") %>% html_attr("href") %>% str_subset("covid-19") %>% str_c("https://www.gov.scot", .) ``` ``` ## [1] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-tuesday-22-february-2022/" ## [2] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-8-february-2022/" ## [3] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-1-february-2022/" ## [4] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-25-january-2022/" ## [5] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-18-january-2022/" ## [6] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-11-january-2022/" ## [7] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-5-january-2022/" ## [8] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-21-december-2021/" ## [9] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-17-december-2021/" ## [10] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-14-december-2021/" ... ``` --- ## Save COVID-19 URLs ```r covid_speech_urls <- all_speeches_page %>% html_nodes(".collections-list a") %>% html_attr("href") %>% str_subset("covid-19") %>% str_c("https://www.gov.scot", .) covid_speech_urls ``` ``` ## [1] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-tuesday-22-february-2022/" ## [2] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-8-february-2022/" ## [3] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-1-february-2022/" ## [4] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-25-january-2022/" ## [5] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-18-january-2022/" ## [6] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-11-january-2022/" ## [7] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-5-january-2022/" ## [8] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-21-december-2021/" ## [9] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-speech-17-december-2021/" ## [10] "https://www.gov.scot/publications/coronavirus-covid-19-update-first-ministers-statement-14-december-2021/" ... ``` --- class: middle # Iteration --- ## Define the task - Goal: Scrape info on all COVID-19 speeches of the First Minister - So far: ```r scrape_speech(covid_speech_urls[1]) scrape_speech(covid_speech_urls[2]) scrape_speech(covid_speech_urls[3]) ``` - What else do we need to do? - Run the `scrape_speech()` function on all COVID-19 speech links - Combine the resulting data frames from each run into one giant data frame --- ## Iteration .question[ How can we tell R to apply the `scrape_speech()` function to each link in `covid_speech_urls`? ] -- - Option 1: Write a **for loop**, i.e. explicitly tell R to visit a link, apply the function, store the result, then visit the next link, apply the function, append the result to the stored result from the previous link, and so on and so forth. -- - Option 2: **Map** the function to each element in the list of links, and let R take care of the storing and appending of results. -- - We'll go with Option 2! --- ## How does mapping work? Suppose we have exam 1 and exam 2 scores of 4 students stored in a list... ```r exam_scores <- list( exam1 <- c(80, 90, 70, 50), exam2 <- c(85, 83, 45, 60) ) ``` -- ...and we find the mean score in each exam ```r map(exam_scores, mean) ``` ``` ## [[1]] ## [1] 72.5 ## ## [[2]] ## [1] 68.25 ``` --- ...and suppose we want the results as a numeric (double) vector ```r map_dbl(exam_scores, mean) ``` ``` ## [1] 72.50 68.25 ``` ...or as a character string ```r map_chr(exam_scores, mean) ``` ``` ## [1] "72.500000" "68.250000" ``` --- ## `map_something` Functions for looping over an object and returning a value (of a specific type): * `map()` - returns a list * `map_lgl()` - returns a logical vector * `map_int()` - returns a integer vector * `map_dbl()` - returns a double vector * `map_chr()` - returns a character vector * `map_df()` / `map_dfr()` - returns a data frame by row binding * `map_dfc()` - returns a data frame by column binding * ... --- ## Go to each page, scrape speech - Map the `scrape_speech()` function - to each element of `covid_speech_urls` - and return a data frame by row binding ```r covid_speeches <- map_dfr(covid_speech_urls, scrape_speech) ``` --- ```r covid_speeches %>% print(n = 15) ``` ``` ## # A tibble: 218 Ć 6 ## title date location abstract text url ## <chr> <date> <chr> <chr> <chr> <chr> ## 1 Coronavirus (COVID-1ā¦ 2021-04-20 St Andrā¦ Statemeā¦ "Gooā¦ httpā¦ ## 2 Coronavirus (COVID-1ā¦ 2021-04-13 St Andrā¦ Statemeā¦ "Thaā¦ httpā¦ ## 3 Coronavirus (COVID-1ā¦ 2021-04-06 St Andrā¦ Statemeā¦ "Gooā¦ httpā¦ ## 4 Coronavirus (COVID-1ā¦ 2021-03-30 St Andrā¦ Statemeā¦ "Thaā¦ httpā¦ ## 5 Coronavirus (COVID-1ā¦ 2021-03-24 Scottisā¦ Statemeā¦ "Thaā¦ httpā¦ ## 6 Coronavirus (Covid-1ā¦ 2021-03-23 The Scoā¦ Statemeā¦ "Preā¦ httpā¦ ## 7 Coronavirus (COVID-1ā¦ 2021-03-18 Scottisā¦ Statemeā¦ "Thaā¦ httpā¦ ## 8 Coronavirus (COVID-1ā¦ 2021-03-17 St Andrā¦ Statemeā¦ "\nGā¦ httpā¦ ## 9 Coronavirus (COVID-1ā¦ 2021-03-16 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## 10 Coronavirus (COVID-1ā¦ 2021-03-15 St Andrā¦ Statemeā¦ "\nGā¦ httpā¦ ## 11 Coronavirus (COVID-1ā¦ 2021-03-11 Scottisā¦ Statemeā¦ "I cā¦ httpā¦ ## 12 Coronavirus (COVID-1ā¦ 2021-03-09 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## 13 Coronavirus (COVID-1ā¦ 2021-03-05 Scottisā¦ Parliamā¦ "Helā¦ httpā¦ ## 14 Coronavirus (COVID-1ā¦ 2021-03-04 Scottisā¦ Parliamā¦ "I wā¦ httpā¦ ## 15 Coronavirus (COVID-1ā¦ 2021-03-02 Scottisā¦ Statemeā¦ "Preā¦ httpā¦ ## # ā¦ with 203 more rows ``` --- ## What could go wrong? ```r covid_speeches <- map_dfr(covid_speech_urls, scrape_speech) ``` - This will take a while to run - If you get `HTTP Error 429 (Too many requests)` you might want to slow down your hits by modifying your function to slow it down by adding a random wait (sleep) time between hitting each link ```r scrape_speech <- function(url){ # Sleep for randomly generated number of seconds # Generated from a uniform distribution between 0 and 1 * Sys.sleep(runif(1)) # Rest of your function code goes here... } ```