What's the underlying logic or philosophical foundation to understand the difference between mylist[2]
and mylist[[2]]
in the following?
What's a simple logical way to understand single square brackets vs. double square brackets?
> mylist <- list(1, list("a","b","c"))
> mylist[2]
# [[1]]
# [[1]][[1]]
# [1] "a"
# [[1]][[2]]
# [1] "b"
# [[1]][[3]]
# [1] "c"
> mylist[[2]]
# [[1]]
# [1] "a"
# [[2]]
# [1] "b"
# [[3]]
# [1] "c"
A simple analogy is to think of a list as a train. Each car in the train is carrying stuff. If you remove two cars, you have a train with two fewer cars. If you remove all but one car, it is still a train with a single car.
[]
(subsetting) function.[[]]
(though $
may also be used with a named list). I refer to this as the extraction function, though I'm not sure if this is a widely used term.In your example, mylist[2] is a sublist of mylist containing one element. You can verify this with length(mylist[2])
. Provided that the arguments are valid, the [
function will provide a list with as many elements as are in the numeric or character vector provided as an argument to [
. Most often, we are interested in examining the contents of a list item. This is achieved with the [[
function. For example, mylist[[2]]
is the contents of mylist[2]
, which itself is a list containing multiple elements. To see this, try length(mylist[[2]])
Because [
can be thought of as a list subsetting function and [[
as a list element extraction function, mylist[1:2]
and mylist[c(1,2)]
return a sublist (which is equivalent to mylist in this case), whereas mylist[[1:2]]
and mylist[[c(1,2)]]
return a "subscript out of bounds" error. It is only possible to extract one list element at a time (ie, per function call).
@richard-scriven alerted me to a link on a Hadley Wickham twitter post providing an additional analogy of a nested list in the form of photographs.
With a fairly simple list structure, str
is great way to get an idea of the list contents. In this example, the output of str(mylist[2])
and str(mylist[[2]])
provide additional insight into their differing data structure.
In general, a list is agnostic to its contents, so that a single list may contain other lists, data.frames, matrices, and atomic vectors as separate elements. As @joran, joked in his comment, this where the train analogy gets stretched, maybe a little too much. However, once you are comfortable with the first level of a list, additional nested lists behave in the same way. (maybe the nested lists are boxes carried inside of the train car?)
Side Note:
One of my favorite functions for examining lists and data.frames (which are lists with atomic vectors of a common length), is the str
function. I regularly use it after reading in a .csv, .dta, or other file to examine the list structure. A common hurdle with users learning R (as well as experienced users) in debugging code is keeping in mind what data structure they are working with and what data structure is needed as an argument for or what data structure is the output of a function. str
together with typeof
and class
, are an excellent suite of tools in addressing this problem.
This answer benefits from comments from @42, @nicola, @joran, @jogo, and @richard-scriven.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With