Reputation: 501
I have a list of lists similar to the toy example given here. I would like to loop through this list to return a new list which has had elements removed based on a variable.
dput(head(list)):
list(FEB_gems = list(GAME1 = structure(list(GAME1_Class = structure(c(2L,
1L, 5L, 4L, 3L), .Label = c("fighter", "paladin", "rouge", "sorcerer",
"wizard"), class = "factor"), GAME1_Race = structure(c(3L, 1L,
4L, 3L, 2L), .Label = c("elf", "gnome", "human", "orc"), class = "factor"),
GAME1_Alignment = structure(c(4L, 2L, 1L, 5L, 3L), .Label = c("CE",
"CG", "LG", "NE", "NN"), class = "factor"), GAME1_Level = c(6,
7, 6, 7, 7), GAME1_Alive = structure(c(1L, 1L, 1L, 1L, 1L
), .Label = "y", class = "factor")), class = "data.frame", row.names = c(NA,
-5L)), GAME2 = structure(list(GAME2_Class = structure(c(3L, 5L,
2L, 4L, 1L), .Label = c("bard", "cleric", "fighter", "monk",
"wizard"), class = "factor"), GAME2_Race = structure(c(2L, 3L,
2L, 4L, 1L), .Label = c("dwarf", "elf", "half-elf", "human"), class = "factor"),
GAME2_Alignment = structure(c(4L, 2L, 1L, 5L, 3L), .Label = c("CE",
"CG", "LG", "NE", "NN"), class = "factor"), GAME2_Level = c(5,
5, 5, 5, 5), GAME2_Alive = structure(c(1L, 2L, 2L, 2L, 2L
), .Label = c("n", "y"), class = "factor")), class = "data.frame", row.names = c(NA,
-5L))), MAR_gems = list(GAME3 = structure(list(GAME3_Class = structure(c(2L,
1L, 5L, 4L, 3L), .Label = c("barbarian", "cleric", "monk", "ranger",
"warlock"), class = "factor"), GAME3_Race = structure(c(2L, 3L,
2L, 4L, 1L), .Label = c("dwarf", "elf", "half-elf", "human"), class = "factor"),
GAME3_Alignment = structure(c(2L, 2L, 1L, 3L, 2L), .Label = c("CE",
"LG", "LN"), class = "factor"), GAME3_Level = c(1, 1, 1,
1, 1), GAME3_Alive = structure(c(2L, 2L, 2L, 1L, 2L), .Label = c("n",
"y"), class = "factor")), class = "data.frame", row.names = c(NA,
-5L)), GAME4 = structure(list(GAME4_Class = structure(c(2L, 1L,
5L, 4L, 3L), .Label = c("fighter", "paladin", "rouge", "sorcerer",
"wizard"), class = "factor"), GAME4_Race = structure(c(2L, 3L,
2L, 4L, 1L), .Label = c("dwarf", "elf", "half-elf", "human"), class = "factor"),
GAME4_Alignment = structure(c(1L, 2L, 1L, 4L, 3L), .Label = c("CE",
"CG", "LG", "LN"), class = "factor"), GAME4_Level = c(5,
5, 5, 5, 5), GAME4_Alive = structure(c(1L, 2L, 2L, 2L, 2L
), .Label = c("n", "y"), class = "factor")), class = "data.frame", row.names = c(NA,
-5L))))
I have made some attempt at manually sub-setting the list of lists. A function would be preferable is because I have multiple types of data to subset.
1) sub-setting Level columns based on interger
df1 <- Games.Split[[1]][[1]]
Level <- df1[which(df1[4] > 6),]
Games.Split[[1]][[1]] <- Level
df1:
GAME1_Class GAME1_Race GAME1_Alignment GAME1_Level GAME1_Alive
1 paladin human NE 6 y
2 fighter elf CG 7 y
3 wizard orc CE 6 y
4 sorcerer human NN 7 y
5 rouge gnome LG 7 y
Level:
GAME1_Class GAME1_Race GAME1_Alignment GAME1_Level GAME1_Alive
2 fighter elf CG 7 y
4 sorcerer human NN 7 y
5 rouge gnome LG 7 y
2) sub-setting Alive columns based on string
df2 <- Games.Split[[1]][[2]]
Alive <- df2[which(df2[5] == 'y'),]
Games.Split[[1]][[2]] <- Alive
df2:
GAME2_Class GAME2_Race GAME2_Alignment GAME2_Level GAME2_Alive
1 fighter elf NE 5 n
2 wizard half-elf CG 5 y
3 cleric elf CE 5 y
4 monk human NN 5 y
5 bard dwarf LG 5 y
Alive:
GAME2_Class GAME2_Race GAME2_Alignment GAME2_Level GAME2_Alive
2 wizard half-elf CG 5 y
3 cleric elf CE 5 y
4 monk human NN 5 y
5 bard dwarf LG 5 y
However I'm struggling to put this into action in a for loop to perform these sub-setting tasks on the entire list.
for (i in Games.Split){
for (j in i){
Alive = j[which(j[5] == 'y'),]
j <- Alive
}
}
Overall, a function/ method that can iterate through the whole list to provide a new list which no longer has the removed elements.
Upvotes: 1
Views: 117
Reputation: 346
Since you have two levels of lists to organizing the dataframes, this will require a nested list apply function (lapply
)-- same as a loop but a little neater. Here is an example that creates a function to subset the game list (gameList) based on maximum level (maxLevel):
listSubset <- function(x, maxLevel){
lapply(x, function(ls){
lapply(ls, function(df) df[df[[grep('Level', names(df), value = TRUE)]] < maxLevel, ])
})
}
listSubset(x = gameList, maxLevel = 6)
Output:
$`FEB_gems`
$`FEB_gems`$`GAME1`
[1] GAME1_Class GAME1_Race GAME1_Alignment GAME1_Level GAME1_Alive
<0 rows> (or 0-length row.names)
$`FEB_gems`$GAME2
GAME2_Class GAME2_Race GAME2_Alignment GAME2_Level GAME2_Alive
1 fighter elf NE 5 n
2 wizard half-elf CG 5 y
3 cleric elf CE 5 y
4 monk human NN 5 y
5 bard dwarf LG 5 y
$MAR_gems
$MAR_gems$`GAME3`
GAME3_Class GAME3_Race GAME3_Alignment GAME3_Level GAME3_Alive
1 cleric elf LG 1 y
2 barbarian half-elf LG 1 y
3 warlock elf CE 1 y
4 ranger human LN 1 n
5 monk dwarf LG 1 y
$MAR_gems$GAME4
GAME4_Class GAME4_Race GAME4_Alignment GAME4_Level GAME4_Alive
1 paladin elf CE 5 n
2 fighter half-elf CG 5 y
3 wizard elf CE 5 y
4 sorcerer human LN 5 y
5 rouge dwarf LG 5 y
All the functions are in base R, so no need to install and learn new packages.
Upvotes: 2
Reputation: 12074
I'd argue that life would be easier if you restructure your data, then use dplyr
's filter
to pull out what you want (or omit what you don't want). Assuming your original data is called foo
:
# Load libraries
library(dplyr)
library(purrr)
# Remove one list
bar <- unlist(foo, recursive = FALSE)
# Get names of campaigns and games
campaign_games <- data.frame(do.call(rbind, strsplit(names(bar), "\\.")))
# Add campaigns and games numbers to data frames
ls_games <- pmap(list(campaign_games[, 1], campaign_games[, 2], bar), cbind)
# Rename all columns
ls_games <- lapply(ls_games, function(x){names(x) <- c("Campaign", "Game_n", "Class", "Race", "Alignment", "Level", "Alive"); x})
# Convert to data frame
df <- bind_rows(ls_games)
# Look at result
head(df)
Now your data looks like this:
# Campaign Game_n Class Race Alignment Level Alive
# 1 FEB_gems GAME1 paladin human NE 6 y
# 2 FEB_gems GAME1 fighter elf CG 7 y
# 3 FEB_gems GAME1 wizard orc CE 6 y
# 4 FEB_gems GAME1 sorcerer human NN 7 y
# 5 FEB_gems GAME1 rouge gnome LG 7 y
# 6 FEB_gems GAME2 fighter elf NE 5 n
which is easy to handle. For example, pull those that are alive in game 1 of FEB gems and are level 7 or higher.
df %>% filter(Alive == "y", Campaign == "FEB_gems",
Level >= 7, Game_n == "GAME1")
# Campaign Game_n Class Race Alignment Level Alive
# 1 FEB_gems GAME1 fighter elf CG 7 y
# 2 FEB_gems GAME1 sorcerer human NN 7 y
# 3 FEB_gems GAME1 rouge gnome LG 7 y
Upvotes: 1
Reputation: 887391
If there are only two nested list
s, and want different filter
ing conditions, apply on it individually and assign the output back to the list
element. We loop through the master list
with map
and then apply the logical conditions
library(purrr)
library(dplyr)
lst2 <- map(lst1, ~ {
.x[[1]] <- .x[[1]] %>%
filter_at(4, all_vars(. > 6))
.x[[2]] <- .x[[2]] %>%
filter_at(5, all_vars(. == 'y'))
.x
})
Upvotes: 1