There's another way to assign to a list, using my_list[[name or number]] <-. If you really want to do that in a loop, just looping over things with names like iter1, iter2, ...
A <- list()
n_iter <- 2
for (i in 1:n_iter){
iname <- paste("iter",i,sep="")
A[[iname]] <- get(iname)
}
As @mnel pointed out, dynamically growing a list is inefficient. The alternative is, I think, to use lapply:
n_iter <- 2
inames <- paste("iter",1:n_iter,sep="")
names(inames) <- inames
A <- lapply(inames,get)
This can also be done with a data frame, which would be a better format if your sublists always have two elements, each having a consistent class (item1 being numeric and item 2 being character).
n_iter <- 2
DF <- data.frame(item1=rep(0,n_iter),item2=rep("",n_iter),stringsAsFactors=FALSE)
for (i in 1:n_iter){
iname <- paste("iter",i,sep="")
DF[i,] <- get(iname)
rownames(DF)[i] <- iname
}
# item1 item2
# iter1 1 a
# iter2 1 b
However, that's a pretty ugly way of doing things; things get messy pretty quickly when using get. With your data structure, maybe you want to create iter1 and iter2 in a loop and immediately embed them into the parent list or data frame?
n_iter = 10
DF <- data.frame(item1 = rep(0,n_iter), item2 = rep("",n_iter))
for (i in 1:n_iter){
... do stuff to make anum and achar ...
DF[i,"item1"] <- anum
DF[i,"item2"] <- achar
}
Where anum and achar are the values of item1 and item2 you want to store from that iteration. Elsewhere on SO, they say that there is an alternative using the data.table package that is almost 10x as fast/efficient as this sort of data-frame assignment.
Oh, one last idea: if you want to put them in a list first, you can easily convert to a data frame later with
DF <- do.call(rbind.data.frame,A)
Answer from Frank on Stack OverflowThere's another way to assign to a list, using my_list[[name or number]] <-. If you really want to do that in a loop, just looping over things with names like iter1, iter2, ...
A <- list()
n_iter <- 2
for (i in 1:n_iter){
iname <- paste("iter",i,sep="")
A[[iname]] <- get(iname)
}
As @mnel pointed out, dynamically growing a list is inefficient. The alternative is, I think, to use lapply:
n_iter <- 2
inames <- paste("iter",1:n_iter,sep="")
names(inames) <- inames
A <- lapply(inames,get)
This can also be done with a data frame, which would be a better format if your sublists always have two elements, each having a consistent class (item1 being numeric and item 2 being character).
n_iter <- 2
DF <- data.frame(item1=rep(0,n_iter),item2=rep("",n_iter),stringsAsFactors=FALSE)
for (i in 1:n_iter){
iname <- paste("iter",i,sep="")
DF[i,] <- get(iname)
rownames(DF)[i] <- iname
}
# item1 item2
# iter1 1 a
# iter2 1 b
However, that's a pretty ugly way of doing things; things get messy pretty quickly when using get. With your data structure, maybe you want to create iter1 and iter2 in a loop and immediately embed them into the parent list or data frame?
n_iter = 10
DF <- data.frame(item1 = rep(0,n_iter), item2 = rep("",n_iter))
for (i in 1:n_iter){
... do stuff to make anum and achar ...
DF[i,"item1"] <- anum
DF[i,"item2"] <- achar
}
Where anum and achar are the values of item1 and item2 you want to store from that iteration. Elsewhere on SO, they say that there is an alternative using the data.table package that is almost 10x as fast/efficient as this sort of data-frame assignment.
Oh, one last idea: if you want to put them in a list first, you can easily convert to a data frame later with
DF <- do.call(rbind.data.frame,A)
This gets you the equivalent of your All
c(iter1=list(iter1), iter2=list(iter2))
> identical(c(iter1=list(iter1), iter2=list(iter2)), All)
[1] TRUE
Let's say you'd like to add a third list to All:
c(All, list(iter3=iter3))
If you don't care for the list names, it looks a little cleaner
c(list(iter1), list(iter2))
Working with nested lists
tidyr - accessing nested lists in R - Stack Overflow
How can I access data in a nested R list? - Stack Overflow
How to efficiently manipulate the contents of nested lists in R - Stack Overflow
Hi, I'm having a tough time trying to work with lists. I'm getting data from an API (nhlapi) and its a nested list of lists ...
I want to transform those lists into data frames, but even at the lowest level some items differ in length so functions like rbind/bind_row doesn't work. I tried using purrr, but I'm struggling.
Do you guys know any material that teaches how to work with lists? I tried looking on youtube but only found some very basic stuff. I would post the list structure but its too big to even fit in the console. Any help would be highly appreciated. Thanks
Edit: so I'm will be trying to use unnest function of the tidyr package and see what that will do. I'll post here too, the dump of str() function:
List of 1 $ :List of 3 ..$ copyright: chr "NHL and the NHL Shield are registered trademarks of the National Hockey League. NHL and NHL team marks are the "| __truncated__ ..$ teams :List of 2 .. ..$ away:List of 10 .. .. ..$ team :List of 3 .. .. .. ..$ id : int 10 .. .. .. ..$ name: chr "Toronto Maple Leafs" .. .. .. ..$ link: chr "/api/v1/teams/10" .. .. ..$ teamStats :List of 1 .. .. .. ..$ teamSkaterStats:List of 11 .. .. .. .. ..$ goals : int 4 .. .. .. .. ..$ pim : int 8 .. .. .. .. ..$ shots : int 28 .. .. .. .. .. [list output truncated] .. .. ..$ players :List of 37 .. .. .. ..$ ID8475718:List of 4 .. .. .. .. ..$ person :List of 22 .. .. .. .. .. ..$ id : int 8475718 .. .. .. .. .. ..$ fullName : chr "Justin Holl" .. .. .. .. .. ..$ link : chr "/api/v1/people/8475718" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "3" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "D" .. .. .. .. .. ..$ name : chr "Defenseman" .. .. .. .. .. ..$ type : chr "Defenseman" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. ..$ ID8477939:List of 4 .. .. .. .. ..$ person :List of 22 .. .. .. .. .. ..$ id : int 8477939 .. .. .. .. .. ..$ fullName : chr "William Nylander" .. .. .. .. .. ..$ link : chr "/api/v1/people/8477939" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "88" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "R" .. .. .. .. .. ..$ name : chr "Right Wing" .. .. .. .. .. ..$ type : chr "Forward" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. ..$ ID8478408:List of 4 .. .. .. .. ..$ person :List of 22 .. .. .. .. .. ..$ id : int 8478408 .. .. .. .. .. ..$ fullName : chr "Travis Dermott" .. .. .. .. .. ..$ link : chr "/api/v1/people/8478408" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "23" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "D" .. .. .. .. .. ..$ name : chr "Defenseman" .. .. .. .. .. ..$ type : chr "Defenseman" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. .. [list output truncated] .. .. .. [list output truncated] .. ..$ home:List of 10 .. .. ..$ team :List of 3 .. .. .. ..$ id : int 8 .. .. .. ..$ name: chr "Montréal Canadiens" .. .. .. ..$ link: chr "/api/v1/teams/8" .. .. ..$ teamStats :List of 1 .. .. .. ..$ teamSkaterStats:List of 11 .. .. .. .. ..$ goals : int 0 .. .. .. .. ..$ pim : int 4 .. .. .. .. ..$ shots : int 32 .. .. .. .. .. [list output truncated] .. .. ..$ players :List of 34 .. .. .. ..$ ID8480051:List of 4 .. .. .. .. ..$ person :List of 22 .. .. .. .. .. ..$ id : int 8480051 .. .. .. .. .. ..$ fullName : chr "Cayden Primeau" .. .. .. .. .. ..$ link : chr "/api/v1/people/8480051" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "30" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "N/A" .. .. .. .. .. ..$ name : chr "Unknown" .. .. .. .. .. ..$ type : chr "Unknown" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. ..$ ID8480829:List of 4 .. .. .. .. ..$ person :List of 21 .. .. .. .. .. ..$ id : int 8480829 .. .. .. .. .. ..$ fullName : chr "Jesperi Kotkaniemi" .. .. .. .. .. ..$ link : chr "/api/v1/people/8480829" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "15" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "C" .. .. .. .. .. ..$ name : chr "Center" .. .. .. .. .. ..$ type : chr "Forward" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. ..$ ID8476967:List of 4 .. .. .. .. ..$ person :List of 22 .. .. .. .. .. ..$ id : int 8476967 .. .. .. .. .. ..$ fullName : chr "Brett Kulak" .. .. .. .. .. ..$ link : chr "/api/v1/people/8476967" .. .. .. .. .. .. [list output truncated] .. .. .. .. ..$ jerseyNumber: chr "77" .. .. .. .. ..$ position :List of 4 .. .. .. .. .. ..$ code : chr "D" .. .. .. .. .. ..$ name : chr "Defenseman" .. .. .. .. .. ..$ type : chr "Defenseman" .. .. .. .. .. .. [list output truncated] .. .. .. .. .. [list output truncated] .. .. .. .. [list output truncated] .. .. .. [list output truncated] ..$ officials:'data.frame': 5 obs. of 4 variables: .. ..$ officialType : chr [1:5] "Referee" "Referee" "Referee" "Linesman" ... .. ..$ official.id : int [1:5] 4621 2275 4692 2329 2460 .. ..$ official.fullName: chr [1:5] "Graham Skilliter" "Marc Joannette" "Kendrick Nicholson" "Steve Barton" ... .. .. [list output truncated] ..- attr(*, "url")= chr "https://statsapi.web.nhl.com/api/v1/game/2020030174/boxscore"
As you can see even truncated to 3 level, this thing is quite big, I'm only interested in the players list, I would to create a dataframe where every item of those list below is a column and every ID would be a row.
This is a pipe-able (%>%) base R approach
select_unnest <- function(x, select_val){
x[[2]][x[[1]]==select_val][[1]]
}
nest_2 %>% select_unnest("Asia") %>% select_unnest("China")
Comparing the timings:
Unit: microseconds
min lq mean median uq max neval
aosmith1 3202.105 3354.0055 4045.9602 3612.126 4179.9610 17119.495 100
aosmith2 5797.744 6191.9380 7327.6619 6716.445 7662.6415 24245.779 100
Floo0 227.169 303.3280 414.3779 346.135 400.6735 4804.500 100
Ben Bolker 622.267 720.6015 852.9727 775.172 875.5985 1942.495 100
Code:
microbenchmark::microbenchmark(
{a<-nest_2[nest_2$continent=="Asia",]$by_continent
flatten_df(a) %>%
filter(country == "China") %>%
unnest},
{nest_2 %>%
filter(continent == "Asia") %>%
select(by_continent) %>%
unnest%>%
filter(country == "China") %>%
unnest},
{nest_2 %>% select_unnest("Asia") %>% select_unnest("China")},
{n1 <- nest_2$by_continent[nest_2$continent=="Asia"][[1]]
n2 <- n1 %>% filter(country=="China")
n2$by_country[[1]]}
)
Your a is still a list, which would need to be flattened before you could do more.
You could use flatten_df, dplyr::filter, and unnest:
library(dplyr)
flatten_df(a) %>%
filter(country == "China") %>%
unnest
# A tibble: 12 x 5
country year lifeExp pop gdpPercap
<fctr> <int> <dbl> <int> <dbl>
1 China 1952 44.00000 556263527 400.4486
2 China 1957 50.54896 637408000 575.9870
3 China 1962 44.50136 665770000 487.6740
4 China 1967 58.38112 754550000 612.7057
5 China 1972 63.11888 862030000 676.9001
6 China 1977 63.96736 943455000 741.2375
7 China 1982 65.52500 1000281000 962.4214
8 China 1987 67.27400 1084035000 1378.9040
9 China 1992 68.69000 1164970000 1655.7842
10 China 1997 70.42600 1230075000 2289.2341
11 China 2002 72.02800 1280400000 3119.2809
12 China 2007 72.96100 1318683096 4959.1149
An alternative way to pull out Asia and end up in a situation where you aren't working with a list. This would avoid the need to flatten later.
asia = nest_2 %>%
filter(continent == "Asia") %>%
select(by_continent) %>%
unnest
Let's look at some ways to do what you want:
data[[1]]
This returns the first element of the list, which is itself a list. You can use the $ subsetting shorthand, but the name of the first element is nonstandard. R prefers names that start with letters and include only alphanumeric characters, periods and underscores. You can escape this behavior with backticks:
data$`1`
If you want to access one of the elements of list 1 in your list of lists, you need to further subset. To get to doy, which is the third element of 1. You can do that four ways.
data[[1]][[3]]
data$`1`[[3]]
data[[1]]$doy
data$`1`$doy
One way (in addition to what Ben Norris has shown):
our_list[[c("1", "doy")]]
Reproducible example data (please provide next time)
our_list <- list(`1` = list(site = "x", doy = 3))
I've been banging my head against a wall on this and assign() doesn't appear to be the answer. If anyone has any suggestions, I have to save from within the function so that my user input doesn't become the next line of code because I am using readline().
Edit: I think others unfamiliar with functions may not understand the issue: list[[x]] <- update.object. That doesn't work. It is inside a function. It will not write any object to the global environment unless you tell it to. Assign() allows for this but not for nested lists. So I am not sure how to do so.
Edit: reproducible example available here: https://stackoverflow.com/questions/69934750/updating-a-nested-list-object-in-the-global-environment-from-within-a-function-i