How can I add vector elements to corresponding vectors in lists? - r

I have a vectors of variable length in lists and a vector, somewhat like this:
set.seed(0)
x <- lapply(as.list(sample(1:10, 10, repl=TRUE)),
function(x) sample(1:10, x, repl=TRUE))
y <- sample(1:10, 10, repl=TRUE)
I need to add each element of y to a corresponding vector in x. Currently I accomplish this as so:
newList <- list()
for (i in seq_along(y)) {
newList <- c(newList, list(y[i] + x[[i]]))
}
> x[1:2]
[[1]]
[1] 1 3 2 7 4 8 5 8 10
[[2]]
[1] 4 8 10
> y[1:2]
[1] 4 8
> newList
[[1]]
[1] 5 7 6 11 8 12 9 12 14
[[2]]
[1] 12 16 18
[[3]]
[1] 13 17 12 13
...
Is there a better way, perhaps using a lapply-like function?

This is very similar to previous questions, which use Map or mapply to operate on two lists/vectors of the same length in tandem:
How do I apply an index vector over a list of vectors?
Add respective dataframes in list together in R
For this specific case, try:
Map("+",x,y)
#[[1]]
#[1] 5 7 6 11 8 12 9 12 14
#
#[[2]]
#[1] 12 16 18
#
#[[3]]
#[1] 13 17 12 13

Related

Add a number to a vector inside a list in R

I have a list like this
x <- list(a=1:10, b="Good morning", c="Hi")
If I type x[1] or x[c("a")] I get the numbers 1 to 10 of the vector
I want to add 5 to each element of the vector a inside the list x.
I tried
x[1] + 5 #didn't work
x[c("a")] + 5 #didn't work
I tried a few other things I found on google and they didn't work
Any help will be appreciated.
A few solutions:
> x <- list(a=1:10, b="Good morning", c="Hi")
> x[["a"]] + 5
[1] 6 7 8 9 10 11 12 13 14 15
> x[[1]] + 5
[1] 6 7 8 9 10 11 12 13 14 15
> x$a + 5
[1] 6 7 8 9 10 11 12 13 14 15
The problem with your approach is that [] is the syntax to index vectors, but x is a list and not a vector. To get the an element of a list, you need to use [[]]. By using x[1] you get a sublist whose single element is a vector instead of getting a vector.
Addition in response to comment asking how to update the original list:
In R you can use the same syntax to retrieve values from and object and to assign values to that object.
> x$a <- x$a + 5
> x
$a
[1] 6 7 8 9 10 11 12 13 14 15
$b
[1] "Good morning"
$c
[1] "Hi"
Any combination of the different possible syntaxes would have worked, too:
x[["a"]] <- x[[1]] + 5

How to implement extract/separate functions (from dplyr and tidyr) to separate a column into multiple columns. based on arbitrary values?

I have a column:
Y = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
I would like to split into multiple columns, based on the positions of the column values. For instance, I would like:
Y1=c(1,2,3,4,5)
Y2=c(6,7,8,9,10)
Y3=c(11,12,13,14,15)
Y4=c(16,17,18,19,20)
Since I am working with a big data time series set, the divisions will be arbitrary depending on the length of one time period.
You can use the base split to split this vector into vectors that are each 5 items long. You could also use a variable to store this interval length.
Using rep with each = 5, and creating a sequence programmatically, gets you a sequence of the numbers 1, 2, ... up to the length divided by 5 (in this case, 4), each 5 times consecutively. Then split returns a list of vectors.
It's worth noting that a variety of SO posts will recommend you store similar data in lists such as this, rather than creating multiple variables, so I'm leaving it in list form here.
Y <- 1:20
breaks <- rep(1:(length(Y) / 5), each = 5)
split(Y, breaks)
#> $`1`
#> [1] 1 2 3 4 5
#>
#> $`2`
#> [1] 6 7 8 9 10
#>
#> $`3`
#> [1] 11 12 13 14 15
#>
#> $`4`
#> [1] 16 17 18 19 20
Created on 2019-02-12 by the reprex package (v0.2.1)
Not a dplyr solution, but I believe the easiest way would involve using matrices.
foo = function(data, sep.in=5) {
data.matrix = matrix(data,ncol=5)
data.df = as.data.frame(data.matrix)
return(data.df)
}
I have not tested it but this function should create a data.frame who can be merge to a existing one using cbind()
We can make use of split (writing the commented code as solution) to split the vector into a list of vectors.
lst <- split(Y, as.integer(gl(length(Y), 5, length(Y))))
lst
#$`1`
#[1] 1 2 3 4 5
#$`2`
#[1] 6 7 8 9 10
#$`3`
#[1] 11 12 13 14 15
#$`4`
#[1] 16 17 18 19 20
Here, the gl create a grouping index by specifying the n, k and length parameters where n - an integer giving the number of levels, k - an integer giving the number of replications, and length -an integer giving the length of the result.
In our case, we want to have 'k' as 5.
as.integer(gl(length(Y), 5, length(Y)))
#[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4
If we want to have multiple objects in the global environment, use list2env
list2env(setNames(lst, paste0("Y", seq_along(lst))), envir = .GlobalEnv)
Y1
#[1] 1 2 3 4 5
Y2
#[1] 6 7 8 9 10
Y3
#[1] 11 12 13 14 15
Y4
#[1] 16 17 18 19 20
Or as the OP mentioned dplyr/tidyr in the question, we can use those packages as well
library(tidyverse)
tibble(Y) %>%
group_by(grp = (row_number()-1) %/% 5 + 1) %>%
summarise(Y = list(Y)) %>%
pull(Y)
#[[1]]
#[1] 1 2 3 4 5
#[[2]]
#[1] 6 7 8 9 10
#[[3]]
#[1] 11 12 13 14 15
#[[4]]
#[1] 16 17 18 19 20
data
Y <- 1:20

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)

How to make a sequences from a range

I am trying to make a sequences from a range from the output of range.
> range(wines$quality)
[1] 3 8
> seq(3, 8)
[1] 3 4 5 6 7 8
> seq(range(wines$quality))
[1] 1 2
but I am trying to get put the output of range 3, 8 into seq to get the list of 3, 4, 5, 6, 7 ,8 why is giving me a list with 1 2? How do a make it behave as I want?
Another option:
do.call(seq, as.list(range(wines$quality)))
# [1] 3 4 5 6 7 8
You problem right now is you are passing a two element vector as one argument, when seq expects two one element arguments in order for it to do what you want.
do.call calls seq with each of the items in as.list... as an argument.
I am sure there is a fancier way to do it but why not just:
x <- range(wine$quality)
seq(x[1], x[2])
Some possible solutions, though the eval parse is more fooling around:
set.seed(10)
x <- rpois(20, 10)
y <- range(x); y[1]:y[2]
seq(y[1], y[2])
eval(parse(text = paste(range(x), collapse=":")))
## > y <- range(x); y[1]:y[2]
## [1] 5 6 7 8 9 10 11 12 13 14 15
## > seq(y[1], y[2])
## [1] 5 6 7 8 9 10 11 12 13 14 15
## > eval(parse(text = paste(range(x), collapse=":")))
## [1] 5 6 7 8 9 10 11 12 13 14 15

Loop through a vector of vectors

When I loop through a vector of vectors, the result of each loop is several vectors. I would expect the result of each loop to be a vector. Please see the following example:
foo <- seq(from=1, to=5, by=1)
bar <- seq(from=6, to=10, by=1)
baz <- seq(from=11, to=15, by=1)
vects <- c(foo,bar,baz)
for(v in vects) {print(v)}
# [1] 1
# [1] 2
# [1] 3
# [1] 4
# [1] 5
# [1] 6
# [1] 7
# [1] 8
# [1] 9
# [1] 10
# [1] 11
# [1] 12
# [1] 13
# [1] 14
# [1] 15
This is odd as I would expect three vectors given it (should) iterate three times given the vector, c(foo,bar,baz). Something like:
# [1] 1 2 3 4 5
# [1] 6 7 8 9 10
# [1] 11 12 13 14 15
Can anyone explain why I am getting this result (15 vectors) and how to achieve the result I am looking for (3 vectors)?
Look at what vects is:
> vects
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
The c() joins (in this case) the three vectors, concatenating them into a single vector. In the for() loop, v takes on each values in vects in turn and prints it, hence the result you see.
Did you want a list of the three separate vectors? If so
> vects2 <- list(foo, bar, baz)
> for(v in vects2) {print(v)}
[1] 1 2 3 4 5
[1] 6 7 8 9 10
[1] 11 12 13 14 15
In other words, form a list of the vectors, not a combination of the vectors.
Substitute vects <- list(foo,bar,baz) for vects <- c(foo,bar,baz).
There is no such thing (really) as a vector of vectors.

Resources