Adding a vector to components of a list - r

I have the following list:
A <- c(11)
B <- c(7, 13)
C <- c(1, 10, 11, 12)
my_list <- list(A, B, C)
> my_list
[[1]]
[1] 11
[[2]]
[1] 7 13
[[3]]
[1] 1 10 11 12
I would like to add -2, -1, 0, 1, and 2 to each number in this list, and retain all of the unique values within each list element, to obtain the following resulting list:
> my_new_list
[[1]]
[1] 9 10 11 12 13
[[2]]
[1] 5 6 7 8 9 11 12 13 14 15
[[3]]
[1] -1 0 1 2 3 8 9 10 11 12 13 14
I tried the following code, but I did not get the result I was hoping for:
my_new_list <- lapply(res, `+`, -2:2)
> my_new_list
$`1`
[1] 9 10 11 12 13
$`2`
[1] 5 12 7 14 9
$`3`
[1] -1 9 11 13 3
Why is this happening, and how can I obtain the result I'd like? Thanks!

Assuming that we need the unique values
lapply(my_list, function(x) sort(unique(unlist(lapply(x, `+`, -2:2)))))
Or with outer
lapply(my_list, function(x) sort(unique(c(outer(x, -2:2, `+`)))))
Or with rep and recyling
lapply(my_list, function(x) sort(unique(rep(-2:2, each = length(x)) + x)))
#[[1]]
# [1] 9 10 11 12 13
#[[2]]
# [1] 5 6 7 8 9 11 12 13 14 15
#[[3]]
# [1] -1 0 1 2 3 8 9 10 11 12 13 14

How about this:
my_new_list <- lapply(my_list, function(x) unique(union(x,sapply(x, function(y) y +c(-2:2)) )))
my_new_list <- lapply(my_new_list, sort)
my_new_list
[[1]]
[1] 9 10 11 12 13
[[2]]
[1] 5 6 7 8 9 11 12 13 14 15
[[3]]
[1] -1 0 1 2 3 8 9 10 11 12 13 14

Related

Adding lists together with a loop

I'm trying to add lists together using a loop. Here is some example data.
df <- data.frame(var1 = c(1,1,2,2,2,2,3,3,3,3,3), var2= 1:11)
> df
var1 var2
1 1 1
2 1 2
3 2 3
4 2 4
5 2 5
6 2 6
7 3 7
8 3 8
9 3 9
10 3 10
11 3 11
I've run this loop code, and would like the items to be stored in a file that contains 3 lists
list_container <- list()
for (i in unique(df$var1) ) {
templist <- df[ df$var1==i , "var2"]
list_container <- list(list_container, templist)
}
it doesn't work, and ends up looking like this
> list_container
[[1]]
[[1]][[1]]
[[1]][[1]][[1]]
list()
[[1]][[1]][[2]]
[1] 1 2
[[1]][[2]]
[1] 3 4 5 6
[[2]]
[1] 7 8 9 10 11
I want the 3 sets of list to sit separately, it should end up like this
list_result <- list(1:2, 3:6, 7:11)
> list_result
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
Is there anyway I can modify my code to get the desired result? Any help greatly appreciated. Thanks
split would be more direct and faster
with(df, unname(split(var2, var1)))
-output
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
If we want to use the == with unique elements, initialize with a NULL list of length same as the length of unique elements of 'var1' column. Loop over the sequence of unique elements, and assign the subset of 'var2' to the ith element of 'list_container'
un1 <- unique(df$var1)
list_container <- vector('list', length(un1))
for(i in seq_along(un1))
list_container[[i]] <- df$var2[df$var1 == un1[i]]
-output
list_container
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
Another base R option using tapply
> with(df, tapply(var2, var1, c))
$`1`
[1] 1 2
$`2`
[1] 3 4 5 6
$`3`
[1] 7 8 9 10 11
or aggregate
> aggregate(var2 ~ ., df, c)$var2
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
You could also use unstack:
unstack(df, var2~var1)
$`1`
[1] 1 2
$`2`
[1] 3 4 5 6
$`3`
[1] 7 8 9 10 11
if you do not want the names, you can get rid of them:
unname(unstack(df, var2~var1))
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11

How to do a simple pattern coding

If I want to have the numbers coded like this, could you give me some suggestion about the codes?
1
2 3
6 5 4
7 8 9 10
15 14 13 12 11
...........
Thanks!!
Another base R option
v <- choose((1:5) + 1, 2)
Map(function(x, y) {
ifelse(x %% 2, rev, I)((y - x + 1):y)
}, seq_along(v), v)
gives
[[1]]
[1] 1
[[2]]
[1] 2 3
[[3]]
[1] 6 5 4
[[4]]
[1] 7 8 9 10
[[5]]
[1] 15 14 13 12 11
Here is one option with split
lst1 <- split(1:15, rep(1:5, 1:5))
lst1[c(TRUE, FALSE)] <- lapply(lst1[c(TRUE, FALSE)], rev)
-output
lst1
#$`1`
#[1] 1
#$`2`
#[1] 2 3
#$`3`
#[1] 6 5 4
#$`4`
#[1] 7 8 9 10
#$`5`
#[1] 15 14 13 12 11
Or another option is
m1 <- matrix(NA, 5, 5)
m1[upper.tri(m1, diag = TRUE)] <- 1:15
m1 <- t(m1)
m1[c(TRUE, FALSE),] <- t(apply(m1[c(TRUE, FALSE),], 1,
function(x) c(rev(x[!is.na(x)]), x[is.na(x)])))

Filter different values in each list using vectorized functions

I have a list:
> list
[[1]]
[1] 5 3 7 9 3 8 3 4 5 7
[[2]]
[1] 2 8 7 8 7 9 6 3 1 4
[[3]]
[1] 7 2 1 7 9 8 9 8 8 2
[[4]]
[1] 5 2 2 1 8 8 2 1 10 7
And now I have a list of elements that I want to filter.
> filtering
[[1]]
[1] 11 10 12
[[2]]
[1] 7 3 9
[[3]]
[1] 3 7 8
[[4]]
[1] 2 6 9
I want to filter, without using any looping, list[[1]] with elements in filtering[[1]], then elements from list[[2]] with elements in filtering[[2]], etc...
Something like this (but mapply is still a loop):
# example data
mylist <- list(1:5, 11:15)
myfilter <- list(c(2,4), c(12, 13))
mapply(FUN = function(x, y){ x[ x %in% y] }, mylist, myfilter, SIMPLIFY = FALSE)
# [[1]]
# [1] 2 4
#
# [[2]]
# [1] 12 13
Or as suggested in the comments by #akrun, using purrr package:
library(purrr)
map2(mylist, myfilter, ~ .x[.x %in% .y])

R: create a vector based on a list

I have the following list called m1:
> m1
[[1]]
[1] 36 37 38
[[2]]
[1] 34 35
[[3]]
[1] 30 31 32 33
[[4]]
[1] 24 25 26 27 28 29
[[5]]
[1] 20 21 22 23
[[6]]
[1] 14 15 16 17 18 19
[[7]]
[1] 11 12 13
[[8]]
[1] 7 8 9 10
[[9]]
[1] 5 6
[[10]]
[1] 1 2 3 4
[[11]]
integer(0)
I would like to create a vector based on this list, which has the value 1 at positions 36, 37, and 38; the value 2 at positions 34 and 35, etc. The final output should be:
vector_1 <- c(10, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 1, 1, 1)
How can I accomplish this in R?
EDIT:
Thanks to a comment below:
> rep(length(m1):1, sapply(m1, length))
[1] 11 11 11 10 10 9 9 9 9 8 8 8 8 8 8 7 7 7 7 6 6 6 6 6 6 5 5 5 4
[30] 4 4 4 3 3 2 2 2 2
That doesn't quite give me what I want, but it's definitely on the right track!
This should handle cases with empty entries and non-sequential entries....
m1 <- list(c(7,4,5), c(2,10,9), c(1,3,6,8), integer())
# [[1]]
# [1] 7 4 5
#
# [[2]]
# [1] 2 10 9
#
# [[3]]
# [1] 1 3 6 8
#
# [[4]]
# integer(0)
rep(seq_along(m1), sapply(m1, length))[order(unlist(m1))]
#[1] 3 2 3 1 1 3 1 3 2 2
This solution should work for more general cases too even if the elements inside m1 are not in a specific order
#DATA
m1 = list(36:38, 34:35, 30:33, 24:29, 20:23,
14:19, 11:13, 7:10, 5:6, 1:4, integer(0))
#Extract the maximum element in m1
mymax = max(unlist(m1))
#Go through m1 using index and replace respective indices in the position
#defined by the elements of m1, otherwise make the elements zero
Reduce("+", lapply(1:length(m1), function(i)
replace(rep(0, mymax), m1[[i]], i)))
# [1] 10 10 10 10 9 9 8 8 8 8 7 7 7 6 6 6 6 6 6 5 5 5 5
#[24] 4 4 4 4 4 4 3 3 3 3 2 2 1 1 1
Here is a straightforward base-R solution:
# data
m1 <- list(36:38, 34:35, 30:33, 24:29, 20:23, 14:19, 11:13, 7:10, 5:6, 1:4, integer(0))
# Count length, and repeat each number in 1:11 accordingly
rev(rep(1:11, sapply(m1, length)))
[1] 10 10 10 10 9 9 8 8 8 8 7 7 7 6 6 6 6 6 6 5 5 5 5 4 4 4 4 4 4 3 3 3
[33] 3 2 2 1 1 1
Edit:
A more generalisable answer would be:
rev(rep(seq_along(m1), sapply(m1, length)))
Try this:
rev(unlist(sapply(1:length(m1), function(x) rep(x,length(m1[[x]])))))
#or even better, #snoram's edited version of this:
rev(rep(seq_along(m1), sapply(m1, length)))
Output:
[1] 10 10 10 10 9 9 8 8 8 8 7 7 7 6 6 6 6 6 6 5 5 5 5 4
[25] 4 4 4 4 4 3 3 3 3 2 2 1 1 1
Sample data:
m1 <- list(36:38,34:35,30:33,24:29,20:23,
14:19,11:13,7:10,5:6,1:4)
names(m1) <- 1:10

how to make a vector of x from 1 to max value

In a dataset like this one
what code should I use if I want to make a vector of
x <- 1: max (day)/ID
? So x will be
1:7 for B1
1:11 for B2
1:22 for B3
I tried
max_day <- summaryBy(day ~ ID , df ,FUN=max) # to extract the maximum day per ID
df<- merge (df, max_day) ## to create another column with the maximum day
max_day<- unique(df[,c("ID", " day.max")]) ## to have one value (max) per ID
##& Finlay the vector
x <- 1: (max_day$day.max)
I got this message
Warning message:
In 1:(max_day$day.max) :
numerical expression has 11134 elements: only the first used
Any suggestions?
tapply(df$day, df$ID, function(x) 1:max(x))
I don't know how should look your output, but you can try this:
my_data <- data.frame(ID = c(rep("B1", 3), rep("B2", 4), rep("B3", 3)),
day = sample(1:20, 10, replace = TRUE))
tmp <- aggregate(test$day, by = list(test$ID), FUN = max)
sapply(1:nrow(tmp), function(y) return(1:tmp$x[y]))
# [[1]]
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# [[2]]
# [1] 1 2 3 4 5 6 7 8 9 10 11
# [[3]]
# [1] 1 2 3 4 5 6 7 8 9 10 11
We can use sapply to loop over unique element of ID and generate a sequence from 1 to the max for that ID in the day column
sapply(unique(df$ID), function(x) seq(1, max(df[df$ID == x, "day"])))
#[[1]]
#[1] 1 2 3 4 5 6 7
#[[2]]
#[1] 1 2 3 4 5 6 7 8 9 10 11
#[[3]]
#[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
If we want all as one vector , we can try unlist
unlist(sapply(unique(df$ID), function(x) seq(1, max(df[df$ID == x, "day"]))))
#[1] 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10
# 11 12 13 14 15 16 17 18 19 20 21 22
Yet another option, using Hadley Wickham's purrr package, as part of the tidyverse.
d <- data.frame(id = rep(c("B1", "B2", "B3"), c(3, 4, 5)),
v = c(1:3, 1:4, 1:5),
day = c(1, 3, 7, 1, 5, 9, 11, 3, 5, 11, 20, 22),
number = c(15, 20, 30, 25, 26, 28, 35, 10, 12, 14, 16, 18))
library(purrr)
d %>%
split(.$id) %>%
map(~1:max(.$day))
# $B1
# [1] 1 2 3 4 5 6 7
# $B2
# [1] 1 2 3 4 5 6 7 8 9 10 11
# $B3
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
df <-
data.frame(ID = c(rep("B1",3),rep("B2",4),rep("B3",5)),
V = c(1,2,3,1,2,3,4,1,2,3,4,5),
day = c(1,3,7,1,5,9,11,3,5,11,20,22),
number = c(15,20,30,25,26,28,35,10,12,14,16,18))
x <- list()
n <- 1
for(i in unique(df$ID)){
max_day <- max(df$day[df$ID==i])
x[[n]] <- 1:max_day
n <- n+1
}
x
[[1]]
[1] 1 2 3 4 5 6 7
[[2]]
[1] 1 2 3 4 5 6 7 8 9 10 11
[[3]]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Resources