Load purrr and repurrrsive, which contains recursive list examples.
library(purrr)
library(repurrrsive)
List inspection is very important and also fairly miserable. Before you can apply a function to every element of a list, you’d better understand the list!
You need to develop a toolkit for list inspection. Be on the look out for:
I have no idea what’s in this list or what its structure is! Please send help.
Understand this is situation normal, especially when your list comes from querying a poorly documented API. This is often true even when your list has been created completely within R. How many of us perfectly understand the structure of a fitted linear model object? You just have to embark on a voyage of discovery and figure out what’s in there. Happy trails.
Remember, there are 3 ways to pull elements out of a list:
The $
operator. Extracts a single element by name. Name can be unquoted, if syntactic.
x <- list(a = "a", b = 2)
x$a
#> [1] "a"
x$b
#> [1] 2
[[
a.k.a. double square bracket. Extracts a single element by name or position. Name must be quoted, if provided directly. Name or position can also be stored in a variable.
x <- list(a = "a", b = 2)
x[["a"]]
#> [1] "a"
x[[2]]
#> [1] 2
nm <- "a"
x[[nm]]
#> [1] "a"
i <- 2
x[[i]]
#> [1] 2
[
a.k.a. single square bracket. Regular vector indexing. For a list input, this always returns a list!
x <- list(a = "a", b = 2)
x["a"]
#> $a
#> [1] "a"
x[c("a", "b")]
#> $a
#> [1] "a"
#>
#> $b
#> [1] 2
x[c(FALSE, TRUE)]
#> $b
#> [1] 2
str()
str()
can help with basic list inspection, although it’s still rather frustrating. Learn to love the max.level
and list.len
arguments. You can use them to keep the output of str()
down to a manageable volume.
Once you begin to suspect or trust that your list is homogeneous, i.e. consists of sub-lists with similar structure, it’s often a good idea to do an in-depth study of a single element. In general, remember you can combine list inspection via str(..., list.len = x, max.level = y)
with single [
and double [[
square bracket indexing.
The repurrrsive package provides examples of lists. We explore them below, to lay the groundwork for other lessons, and to demonstrate list inspection strategies.
The RStudio IDE (v1.1 and higher) offers an Object Explorer that provides interactive inspection and code generation tools for hierarchical objects, such as lists. You can invoke it via the GUI or in code as View(YOUR_UGLY_LIST)
.
However, that won’t help you expose list exploration in something like this website. I am using the listviewer package to do this below. It allows you to expose list exploration in a rendered .Rmd
document. To replicate this experience locally, call, e.g., listviewer::jsonedit(got_chars, mode = "view")
.
library(listviewer)
wesanderson
is a simple list containing color palettes from the wesanderson package. Each component is a palette, named after a movie, and contains a character vector of colors as hexadecimal triplets.
str(wesanderson)
#> List of 15
#> $ GrandBudapest : chr [1:4] "#F1BB7B" "#FD6467" "#5B1A18" "#D67236"
#> $ Moonrise1 : chr [1:4] "#F3DF6C" "#CEAB07" "#D5D5D3" "#24281A"
#> $ Royal1 : chr [1:4] "#899DA4" "#C93312" "#FAEFD1" "#DC863B"
#> $ Moonrise2 : chr [1:4] "#798E87" "#C27D38" "#CCC591" "#29211F"
#> $ Cavalcanti : chr [1:5] "#D8B70A" "#02401B" "#A2A475" "#81A88D" ...
#> $ Royal2 : chr [1:5] "#9A8822" "#F5CDB4" "#F8AFA8" "#FDDDA0" ...
#> $ GrandBudapest2: chr [1:4] "#E6A0C4" "#C6CDF7" "#D8A499" "#7294D4"
#> $ Moonrise3 : chr [1:5] "#85D4E3" "#F4B5BD" "#9C964A" "#CDC08C" ...
#> $ Chevalier : chr [1:4] "#446455" "#FDD262" "#D3DDDC" "#C7B19C"
#> $ Zissou : chr [1:5] "#3B9AB2" "#78B7C5" "#EBCC2A" "#E1AF00" ...
#> $ FantasticFox : chr [1:5] "#DD8D29" "#E2D200" "#46ACC8" "#E58601" ...
#> $ Darjeeling : chr [1:5] "#FF0000" "#00A08A" "#F2AD00" "#F98400" ...
#> $ Rushmore : chr [1:5] "#E1BD6D" "#EABE94" "#0B775E" "#35274A" ...
#> $ BottleRocket : chr [1:7] "#A42820" "#5F5647" "#9B110E" "#3F5151" ...
#> $ Darjeeling2 : chr [1:5] "#ECCBAE" "#046C9A" "#D69C4E" "#ABDDDE" ...
wesanderson
You can get a similar experience in RStudio via View(wesanderson)
.
got_chars
is a list with information on the 30 point-of-view characters from the first five books in the Song of Ice and Fire series by George R. R. Martin. Retrieved from An API Of Ice And Fire. Each component corresponds to one character and contains 18 components which are named atomic vectors of various lengths and types.
str(got_chars, list.len = 3)
#> List of 30
#> $ :List of 18
#> ..$ url : chr "https://www.anapioficeandfire.com/api/characters/1022"
#> ..$ id : int 1022
#> ..$ name : chr "Theon Greyjoy"
#> .. [list output truncated]
#> $ :List of 18
#> ..$ url : chr "https://www.anapioficeandfire.com/api/characters/1052"
#> ..$ id : int 1052
#> ..$ name : chr "Tyrion Lannister"
#> .. [list output truncated]
#> $ :List of 18
#> ..$ url : chr "https://www.anapioficeandfire.com/api/characters/1074"
#> ..$ id : int 1074
#> ..$ name : chr "Victarion Greyjoy"
#> .. [list output truncated]
#> [list output truncated]
str(got_chars[[1]], list.len = 8)
#> List of 18
#> $ url : chr "https://www.anapioficeandfire.com/api/characters/1022"
#> $ id : int 1022
#> $ name : chr "Theon Greyjoy"
#> $ gender : chr "Male"
#> $ culture : chr "Ironborn"
#> $ born : chr "In 278 AC or 279 AC, at Pyke"
#> $ died : chr ""
#> $ alive : logi TRUE
#> [list output truncated]
got_chars
You can get a similar experience in RStudio via View(got_chars)
.
gh_users
is a list with information on 6 GitHub users. gh_repos
is a nested list, also of length 6, where each component is another list with information on up to 30 of that user’s repositories. Retrieved from the GitHub API.
str(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
gh_users
You can get a similar experience in RStudio via View(gh_users)
.
gh_repos
You can get a similar experience in RStudio via View(gh_repos)
.
str()
. What does max.level
control? Apply str()
to wesanderson
and/or got_chars
and experiment with max.level = 0
, max.level = 1
, and max.level = 2
. Which will you use in practice with deeply nested lists?list.len
argument of str()
control? What is its default value? Call str()
on got_chars
and then on a single component of got_chars
with list.len
set to a value much smaller than the default. What range of values do you think you’ll use in real life?str()
on got_chars
, specifying both max.level
and list.len
.str()
on the first element of got_chars
, i.e. the first Game of Thrones character. Use what you’ve learned to pick an appropriate combination of max.level
and list.len
.