Load packages

Load purrr and repurrrsive, which contains recursive list examples.

library(purrr)
library(repurrrsive)

Inspect and explore

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:

  • What is the length of the list?
  • Are the components homogeneous, i.e. do they have the same overall structure, albeit containing different data?
  • Note the length, names, and types of the constituent objects.

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.

Indexing, review

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.

listviewer and RStudio’s Object Explorer

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)

Wes Anderson color palettes

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" ...

Explore wesanderson

You can get a similar experience in RStudio via View(wesanderson).

Game of Thrones POV characters

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]

Explore got_chars

You can get a similar experience in RStudio via View(got_chars).

GitHub users and repositories

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

Explore gh_users

You can get a similar experience in RStudio via View(gh_users).

Explore gh_repos

You can get a similar experience in RStudio via View(gh_repos).

Exercises

  1. Read the documentation on 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?
  2. What does the 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?
  3. Call str() on got_chars, specifying both max.level and list.len.
  4. Call 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.