R: using assign() for list elements - r

Suppose I have an empty list grid. Is it possible to use assign() to assign several data frames to list elements?
grid <- list()
assign(grid[[1]],data.frame(x=1:4,y=5:8))
assign(grid[[2]],data.frame(x=2:4,y=6:8))
So that you would get an output like:
> grid
[[1]]
x y
1 5
2 6
3 7
4 8
[[2]]
x y
2 6
3 7
4 8
Code:
grid <- list()
type = "a"
for(k in 1:3){
assign(paste0("grid[[",k,"]]"),ifelse(type=="a",
data.frame(x=1:4,y=5:8),
data.frame(x=2:4,y=6:8)))
}
Desired output: (since type will always be "a")
> grid
[[1]]
x y
1 5
2 6
3 7
4 8
[[2]]
x y
1 5
2 6
3 7
4 8
[[3]]
x y
1 5
2 6
3 7
4 8

I may be missing something, but I don't see why you need to use assign() at all? Doesn't this do what you wanted?
grid <- list()
type = "a"
for(k in 1:3){
if(type=="a") grid[[k]] <- data.frame(x=1:4,y=5:8)
}
EDIT:
for(k in 1:3){
if(type=="a") grid[[k]] <- data.frame(x=1:4,y=5:8)
if(type=="b") grid[[k]] <- data.frame(x=2:4,y=6:8)
}

Related

Remove the last column of dataframe in R in a function

I need to remove the last column of 10 dataframes, so I decided to put it in lapply(). I wrote a function to remove the col, like below,
remove_col <- function(mydata){
mydata = subset(mydata, select=-c(24))
}
and create a mylist <- (data1, data2.... data10), then I passed lapply as
lapply(mylist, FUN = remove_col)
It did give me a list of the removed dataframe, however, when I checked the original dataframe, the last column is still there.
How should I change the code to change the original dataset?
You need to assign the result of the function call to the input list on the LHS:
mylist <- lapply(mylist, FUN = remove_col)
Had you defined your function with an explicit return value, this might have been more obvious:
remove_col <- function(mydata) {
mydata <- subset(mydata, select=-c(24))
return(mydata) # return the modified list/data frame
}
Instead of hardcoding the column number to remove you can use ncol to remove the last column from each dataframe.
remove_col <- function(mydata){
mydata[, -ncol(mydata)]
}
mylist <- lapply(mylist, remove_col)
To see the changes in the original dataframe you can assign names to list of dataframe and use list2env.
names(mylist) <- paste0('data', seq_along(mylist))
list2env(mylist, .GlobalEnv)
Using base R and lapply, Note, you can remove ", drop = F" from your script if there are more than 2 columns in all dataframes in the list.
> d1
c1 c2
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
> d2
c1 c2
1 5 10
2 4 9
3 3 8
4 2 7
5 1 6
> mylist <- list(d1, d2)
> mylist
[[1]]
c1 c2
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
[[2]]
c1 c2
1 5 10
2 4 9
3 3 8
4 2 7
5 1 6
> lapply(mylist, function(x) x[,1:(ncol(x)-1), drop = F] )
[[1]]
c1
1 1
2 2
3 3
4 4
5 5
[[2]]
c1
1 5
2 4
3 3
4 2
5 1
>

Get object and indices from subsetting call

Basically, I would like to create an R function separate_call that gets an argument like x[ind] and returns x and ind (so, from the parent environment):
x <- 1:10
ind <- 2:3
separate_call(x[ind]) ## should return `list(1:10, 2:3)`
I know that I need to use non-standard evaluation and some tree parsing but I'm not familiar enough with these two concepts. Any help would be appreciated.
Looking at the code of pryr:::tree and experimenting a little bit, I found a solution:
separate_call <- function(call) {
call.sep <- as.list(substitute(call))
parent_env <- parent.frame()
x <- eval(call.sep[[2]], parent_env)
ind <- eval(call.sep[[3]], parent_env)
list(x, ind)
}
Verification:
> x <- 1:10
> ind <- 2:3
> separate_call(x[ind])
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 2 3
> separate_call(x[1:2])
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 1 2
I am adding this solution, not very elegant, see if this fits your need.
separate_call <- function(m){
dprse <- deparse(substitute(m)) ##deparsing the data: to make it as string
lyst <- strsplit(dprse,"\\[|\\]")[[1]] ##removing the square brackets to sparate contents of m
lapply(lyst, function(y)eval(parse(text = y), envir = parent.frame()))
}
I hope this works, I tried it by calling it in three different ways
separate_call(1:10[ind])
separate_call(x[2:3])
separate_call(1:10[2:3])
They all gave me same response
> separate_call(1:10[ind])
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 2 3
> separate_call(x[2:3])
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 2 3
> separate_call(1:10[2:3])
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 2 3

Recursive looping in r

I am new in R but I want to loop through elements of a given list recursively, to be presice I have alist of vectors where the first vector is given by (1,2,3,4), then I now want to loop through this vector and append the second vector obtained to the original list, again loop thorugh second vector in the list and get the third vector which is also appended on the original list and so on. I have this code to start with`
occlist <- list()
occ_cell <- c(1,2,3,4)
for(i in occ_cell){
occ_cell <- seq(i,4*i, by = 1)
occlist[[i]] <- occ_cell
}
`
gives the following list
#[[1]]
#[1] 1 2 3 4
#[[2]]
#[1] 2 3 4 5 6 7 8
#[[3]]
# [1] 3 4 5 6 7 8 9 10 11 12
#[[4]]
# [1] 4 5 6 7 8 9 10 11 12 13 14 15 16
I think to be more clear, lets have the following figure
recOcc <- function(i) {
if (i == 0) return ( NULL )
append( recOcc(i-1), list(seq(i, 4*i)) )
}
And, call with (to reproduce your output)
recOcc(4)
# [[1]]
# [1] 1 2 3 4
#
# [[2]]
# [1] 2 3 4 5 6 7 8
#
# [[3]]
# [1] 3 4 5 6 7 8 9 10 11 12
#
# [[4]]
# [1] 4 5 6 7 8 9 10 11 12 13 14 15 16
You can also use Recall to name your recursive function in the recursive call, which allows for the function name to change.
Edit
For the tree structure, you could try this
## i is the number to start the sequence
## depth determines how deep to recurse
recOcc2 <- function(i, depth=3, cur.depth=0) {
if (depth==cur.depth) return(seq(i, 4*i))
acc <- as.list(seq(i, 4*i))
for (ii in seq_along(acc))
acc[[ii]] <- recOcc2(acc[[ii]], depth, cur.depth+1)
acc
}
## To recreate the simple list
res <- recOcc2(1, depth=1)
## For nested lists
res <- recOcc2(1, depth=2)

Removing outliers easily in R

I have data with discrete x-values, such as
x = c(3,8,13,8,13,3,3,8,13,8,3,8,8,13,8,13,8,3,3,8,13,8,13,3,3)
y = c(4,5,4,6,7,20,1,4,6,2,6,8,2,6,7,3,2,5,7,3,2,5,7,3,2);
How can I generate a new dataset of x and y values where I eliminate pairs of values where the y-value is 2 standard deviations above the mean for that bin. For example, in the x=3 bin, 20 is more than 2 SDs above the mean, so that data point should be removed.
for me you want something like :
by(dat,dat$x, function(z) z$y[z$y < 2*sd(z$y)])
dat$x: 3
[1] 4 1 6 5 7 3 2
---------------------------------------------------------------------------------------------------------------
dat$x: 8
[1] 4 2 2 2 3
---------------------------------------------------------------------------------------------------------------
dat$x: 13
[1] 3 2
EDIT after comment :
by(dat,dat$x,
function(z) z$y[abs(z$y-mean(z$y))< 2*sd(z$y)])
EDIT
I slightly change the by function to get x and y, then I call rbind using do.call
do.call(rbind,by(dat,dat$x,function(z) {
idx <- abs(z$y-mean(z$y))< 2*sd(z$y)
z[idx,]
}))
or using plyr in single call
ddply(dat,.(x),function(z) {
idx <- abs(z$y-mean(z$y))< 2*sd(z$y)
z[idx,]})
Something like this?
newdata <- cbind(x,y)[-which(y>2*sd(y)), ]
Or you mean something like this?
Data <- cbind(x,y)
Data[-which(sd(y)>rowMeans(Data)), ]
You can use tapply for this, but you will lose your original ordering.
tapply(y,x,function(z) z[abs(z-mean(z))<2*sd(z)])
$`3`
[1] 4 1 6 5 7 3 2
$`8`
[1] 5 6 4 2 8 2 7 2 3 5
$`13`
[1] 4 7 6 6 3 2 7

mapply and two lists

I'm trying to use mapply to combine two lists (A and B). Each element is a dataframe.
I'm trying to rbind the dataframes in A to the corresponding dataframes in B. The following returns what I would like in combo1:
num = 10
A<-list()
B<-list()
for (j in 1:num){
A[[j]] <- as.data.frame(matrix(seq(1:9),3,3))
B[[j]] <- as.data.frame(matrix(seq(10:18),3,3))
}
combo1<-list()
for (i in 1:num){
combo1[[i]] <-rbind(A[[i]], B[[i]])
}
I'm trying to use mapply to do the same, but I can't get it to work:
combo2<-list()
combo2<-mapply("rbind", A, B)
I was hoping someone could please help me
You were very close!
## Make this a more _minimal_ reproducible example
A <- A[1:2]
B <- B[1:2]
## Override default attempt to reduce results to a vector, matrix, or other array
mapply("rbind", A, B, SIMPLIFY=FALSE)
# [[1]]
# V1 V2 V3
# 1 1 4 7
# 2 2 5 8
# 3 3 6 9
# 4 1 4 7
# 5 2 5 8
# 6 3 6 9
#
# [[2]]
# V1 V2 V3
# 1 1 4 7
# 2 2 5 8
# 3 3 6 9
# 4 1 4 7
# 5 2 5 8
# 6 3 6 9

Resources