Renaming nested lists with lapply - r

I have a nested list:
my_list <- list(A = 1, B = 2, C = 3)
my_nested_list <- list(D = my_list, E = my_list, F = my_list)
I want to change the names of the inner most elements to the following:
my_names <- c("X", "Y", "Z")
So, A, B, C should becoming X, Y, Z.
Here is my attempt:
name_changer <- lapply(my_nested_list, FUN = function(x){
lapply(x, FUN = function(y){
names(y) <- my_names
})
})
Why does this not work?

In your example you have two lapply loops when you only need one.
Your original second loop tries to apply the 3-element "my_names" vector to each individual element of "my_list", which won't work, since the lengths don't match. But you don't need the second loop at all:
my_list <- list(A = 1, B = 2, C = 3)
my_nested_list <- list(D = my_list, E = my_list, F = my_list)
my_names <- c("X", "Y", "Z")
name_changer <- lapply(my_nested_list, FUN = function(x){
names(x) <- my_names
return(x)
})
str(name_changer)
List of 3
$ D:List of 3
..$ X: num 1
..$ Y: num 2
..$ Z: num 3
$ E:List of 3
..$ X: num 1
..$ Y: num 2
..$ Z: num 3
$ F:List of 3
..$ X: num 1
..$ Y: num 2
..$ Z: num 3

You can use setNames as well -
lapply(my_nested_list, setNames, my_names)
#$D
#$D$X
#[1] 1
#$D$Y
#[1] 2
#$D$Z
#[1] 3
#$E
#$E$X
#[1] 1
#$E$Y
#[1] 2
#$E$Z
#[1] 3
#$F
#$F$X
#[1] 1
#$F$Y
#[1] 2
#$F$Z
#[1] 3

Related

Name columns with Dataframe name in a list of Dataframes

Objective: Change colname of dataframes in a list of dataframes to the name of each dataframe.
I have some issues when dealing with list and dataframes regarding its name. I have prepared this example to clarify. Hope it is not a mess.
Data:
df1 <- data.frame(A = 1, B = 2, C = 3)
df2 <- data.frame(A = 3, B = 3, C = 2)
dfList <- list(df1,df2)
Output:
> str(dfList)
List of 2
$ :'data.frame': 1 obs. of 3 variables:
..$ A: num 1
..$ B: num 2
..$ C: num 3
$ :'data.frame': 1 obs. of 3 variables:
..$ A: num 3
..$ B: num 3
..$ C: num 2
> names(dfList)
NULL
> names(dfList$df1)
NULL
> names(dfList$df2)
NULL
Manually Input names:
names(dfList) <- c("df1", "df2")
dfList <- lapply(dfList, setNames, c("A", "B", "C"))
Which yields:
> str(dfList)
List of 2
$ df1:'data.frame': 1 obs. of 3 variables:
..$ A: num 1
..$ B: num 2
..$ C: num 3
$ df2:'data.frame': 1 obs. of 3 variables:
..$ A: num 3
..$ B: num 3
..$ C: num 2
> names(dfList)
[1] "df1" "df2"
> names(dfList$df1)
[1] "A" "B" "C"
> names(dfList$df2)
[1] "A" "B" "C"
Desired Solution:
WishedList <- dfList
WishedList[[1]] <- setNames(WishedList[[1]], c("A", "B", "df1"))
WishedList[[2]] <- setNames(WishedList[[2]], c("A", "B", "df2"))
Output solution:
> str(WishedList)
List of 2
$ df1:'data.frame': 1 obs. of 3 variables:
..$ A : num 1
..$ B : num 2
..$ df1: num 3
$ df2:'data.frame': 1 obs. of 3 variables:
..$ A : num 3
..$ B : num 3
..$ df2: num 2
> names(WishedList)
[1] "df1" "df2"
> names(WishedList$df1)
[1] "A" "B" "df1"
> names(WishedList$df2)
[1] "A" "B" "df2"
MyTry:
TryList1 <- lapply(dfList, function(x) setNames(x, c("A", "B", quote(x))))
str(TryList1)
List of 2
$ df1:'data.frame': 1 obs. of 3 variables:
..$ A: num 1
..$ B: num 2
..$ x: num 3
$ df2:'data.frame': 1 obs. of 3 variables:
..$ A: num 3
..$ B: num 3
..$ x: num 2
Doubts:
1) Why when creating the file the names both of the dataframes and of the cols of the dataframes are not included in the list?
2) quote(x) with a single dataframe works. Why not in the list?
> df1 <- data.frame(A = 1, B = 2, C = 3)
> df1 <- setNames(df1, c("A", "B", quote(df1)))
> names(df1)
[1] "A" "B" "df1"
Thank you very much!
Here's a slightly different approach:
df1 <- data.frame(A = 1, B = 2, C = 3)
df2 <- data.frame(A = 3, B = 3, C = 2)
dfList <- list(df1,df2)
names(dfList) <- c("df1", "df2")
Map(function(df, dfn) {names(df)[3] <- dfn; df}, dfList, names(dfList))
#$df1
# A B df1
#1 1 2 3
#
#$df2
# A B df2
#1 3 3 2
You could alternatively use setNames(df, c("A", "B", dfn)) inside the mapply function.
A note on OP's trial: The documentation for quote states:
quote simply returns its argument.
That's why when you use quote(x) inside lapply, it simply returns the character x.
We can lapply() over names(dfList) instead of dfList:
lapply(names(dfList), function(dfn) {
df <- dfList[[dfn]]
names(df)[3] <- dfn
df
})
# [[1]]
# A B df1
# 1 1 2 3
#
# [[2]]
# A B df2
# 1 3 3 2
There's a convenience function in purrr that maps over a list and its names simultaneously:
library(purrr)
imap(dfList, ~ {
names(.x)[3] <- .y
.x
})
# $df1
# A B df1
# 1 1 2 3
#
# $df2
# A B df2
# 1 3 3 2
Or if you're after a short one-liner and don't mind hard-coding "A" and "B":
imap(dfList, ~ setNames(.x, c("A", "B", .y)))
(NB: Essentially those are just variations around Docendo discimus' answer).
Also, not your expected output but maybe of interest for you:
dplyr::bind_rows(dfList, .id = "origin")
# origin A B C
# 1 df1 1 2 3
# 2 df2 3 3 2
Or:
bind_rows(map(dfList, select, -C), .id = "C")
# C A B
# 1 df1 1 2
# 2 df2 3 3

Using modifyList() to merge more than two lists

Ive been usingmodifyList() ` to combine two lists of similar structure, since other methods usually don't my data structure. However, I now need to apply this process over multiple lists.
lst1 <- list("name" = c("paul", "mary", "jane"), "height" = c(188,177,166))
lst2 <- list("color" = c("pink", "grey", "black"), "value" = c(22,33,44))
res <- modifyList(lst1, lst2)
gives the desired outcome for two lists
> str(res)
List of 4
$ name : chr [1:3] "paul" "mary" "jane"
$ height: num [1:3] 188 177 166
$ color : chr [1:3] "blue" "red" "green"
$ value : num [1:3] 12 13 14
but how do I apply this over > 2 lists dynamically, i.e.
lst1 <- list("name" = c("paul", "mary", "jane"), "height" = c(188,177,166))
lst2 <- list("color" = c("pink", "grey", "black"), "value" = c(22,33,44))
lst3 <- list("type" = c("good", "bad", "ugly"), "weight" = c(80,70,60))
The expected output in this case would be:
> str(res)
List of 6
$ name : chr [1:3] "paul" "mary" "jane"
$ height: num [1:3] 188 177 166
$ color : chr [1:3] "blue" "red" "green"
$ value : num [1:3] 12 13 14
$ type : chr [1:3] "good" "bad" "ugly"
$ weight: num [1:3] 80 70 60
In the OP's example, the list elements are all disjoint elements which can be joined simply by c(lst1, lst2, lst3). Using another reproducible example
Reduce(modifyList, mget(ls(pattern = "foo\\d+")))
#$a
#[1] 1
#$b
#$b$c
#[1] "d"
#$b$d
#[1] TRUE
#$e
#[1] 2
#$g
#[1] 4
data
foo1 <- list(a = 1, b = list(c = "a", d = FALSE))
foo2 <- list(e = 2, b = list(d = TRUE))
foo3 <- list(g = 4, b = list(c = "d"))

How do I replace empty dataframe/data.tables from a list with a placeholder data.table?

This post How do I remove empty data frames from a list? talks about removing empty dataframes. How do i remove empty dataframes(nrow =0) from a list and replace them with 1 row placeholder dataframes/data.tables?
M1 <- data.frame(matrix(1:4, nrow = 2, ncol = 2))
M2 <- data.frame(matrix(nrow = 0, ncol = 0))
M3 <- data.frame(matrix(9:12, nrow = 2, ncol = 2))
mlist <- list(M1, M2, M3)
placeholder = data.table(a=1,b=1)
I tried:
lapply(mlist, function(x) ifelse(nrow(fundslist[[x]]) == 0, placeholder, x))
One option would be using lengths
mlist[!lengths(mlist)] <- list(placeholder)
str(mlist)
#List of 3
# $ :'data.frame': 2 obs. of 2 variables:
# ..$ X1: int [1:2] 1 2
# ..$ X2: int [1:2] 3 4
# $ :Classes ‘data.table’ and 'data.frame': 1 obs. of 2 variables:
# ..$ a: num 1
# ..$ b: num 1
# ..- attr(*, ".internal.selfref")=<externalptr>
# $ :'data.frame': 2 obs. of 2 variables:
# ..$ X1: int [1:2] 9 10
# ..$ X2: int [1:2] 11 12
How about this? Since your placeholder is fairly small, it's not a problem to multiply it n times.
library(data.table)
M1 <- data.frame(matrix(1:4, nrow = 2, ncol = 2))
M2 <- data.frame(matrix(nrow = 0, ncol = 0))
M3 <- data.frame(matrix(9:12, nrow = 2, ncol = 2))
mlist <- list(M1, M2, M3)
placeholder = data.table(a=1,b=1)
num.rows <- unlist(lapply(mlist, nrow))
num.zeros <- length(num.rows[num.rows == 0])
replacement <- replicate(num.zeros, {placeholder}, simplify = FALSE)
mlist[num.rows == 0] <- replacement
str(mlist)
List of 3
$ :'data.frame': 2 obs. of 2 variables:
..$ X1: int [1:2] 1 2
..$ X2: int [1:2] 3 4
$ :Classes ‘data.table’ and 'data.frame': 1 obs. of 2 variables:
..$ a: num 1
..$ b: num 1
..- attr(*, ".internal.selfref")=<externalptr>
$ :'data.frame': 2 obs. of 2 variables:
..$ X1: int [1:2] 9 10
..$ X2: int [1:2] 11 12
Just to explain how you could complete it using your approach itself!
M1 <- data.frame(matrix(1:4, nrow = 2, ncol = 2))
M2 <- data.frame(matrix(nrow = 0, ncol = 0))
M3 <- data.frame(matrix(9:12, nrow = 2, ncol = 2))
mlist <- list(M1, M2, M3)
placeholder = data.frame(matrix(c(1,1), nrow=1))
abc <- function(x){
if(sum(dim(x))==0)
return(data.frame(placeholder))
else
return(x)
}
lapply(mlist, abc)

How do I insert 3 vectors in the same row of a dataframe?

I'm new in R and I have a problem. I created an "empty" data.frame, as in R I cannot do this, I created a vector to fulfil it that I would delete when finishing the script.
x <- c("aa", 0, 0, 0, "zz") #Vector to fulfill the dataframe (it will be deleted at the end of the script)
df <<- rbind(x) #Creating a matrix. See: class(df)
df <<- data.frame(df) #Converting the matrix into a data.frame
But now I need to fulfil that dataframe with three vectors:
a <- c("bb")
b <- c(2, 3, 4)
c <- c("yy")
The desired output is a dataframe like this:
X1 X2 X3 X4 X5
r1 aa 0 0 0 zz
r2 bb 2 3 4 yy
I have tried this: df <- rbind(df, a, b, c) but it does not work...
Any suggestion?
Given your desired output you may try this:
library(dplyr) # Gives %>%
dataF <- data.frame(X1 = "aa",
X2 = 0,
X3 = 0,
X4 = 0,
X5 = "zz",
stringsAsFactors = FALSE)
a <- c("bb")
b <- c(2, 3, 4)
c <- c("yy")
# setNames to simply use rbind()
newrow <- data.frame(a, t(b), c, stringsAsFactors = FALSE) %>% setNames(names(dataF))
rbind(dataF, newrow)
Which gives you:
'data.frame': 2 obs. of 5 variables:
$ X1: chr "aa" "bb"
$ X2: num 0 2
$ X3: num 0 3
$ X4: num 0 4
$ X5: chr "zz" "yy"
And guessing your even more desired output:
library(dplyr)
dataF <- data.frame(X1 = character(),
X2 = numeric(),
X3 = numeric(),
X4 = numeric(),
X5 = character(),
stringsAsFactors = FALSE)
a <- c("bb")
b <- c(2, 3, 4)
c <- c("yy")
newrow <- data.frame(a, t(b), c, stringsAsFactors = FALSE) %>% setNames(names(dataF))
rbind(dataF, newrow)
Which gives you:
'data.frame': 1 obs. of 5 variables:
$ X1: chr "bb"
$ X2: num 2
$ X3: num 3
$ X4: num 4
$ X5: chr "yy"
Maybe, you can just do
x <- c("aa", 0, 0, 0, "zz")
a <- c("bb")
b <- c(2, 3, 4)
c <- c("yy")
y <- c(a, b, c)
rbind(r1 = x,r2 = y)
# [,1] [,2] [,3] [,4] [,5]
#r1 "aa" "0" "0" "0" "zz"
#r2 "bb" "2" "3" "4" "yy"
This is a matrix, to get it as a data frame, you can
data.frame(rbind(r1 = x,r2 = y))
# X1 X2 X3 X4 X5
#r1 aa 0 0 0 zz
#r2 bb 2 3 4 yy

create list of data frames from two lists of vectors via mapply

I expect a list of two data.frame instead of list of 4 vectors. what did i do wrong?
list1 <- list(1:3, 5:6)
list2 <- list(7:9, 10:11)
result <- mapply((function(x, y) data.frame(id = x, value = y)),
list1, list2)
str(result)
# List of 4
# $ : int [1:3] 1 2 3
# $ : int [1:3] 7 8 9
# $ : int [1:2] 5 6
# $ : int [1:2] 10 11
# - attr(*, "dim")= int [1:2] 2 2
# - attr(*, "dimnames")=List of 2
# ..$ : chr [1:2] "id" "value"
# ..$ : NULL
You can use SIMPLIFY=FALSE in mapply to conserve the list structure
mapply(function(x, y) data.frame(id = x, value = y),
list1, list2, SIMPLIFY=FALSE)
Or use Map
Map(function(x,y) data.frame(id=x, value=y), list1, list2)

Resources