multiply two lists of irregular length - r

I have lists like
a <- list(list(c(-2,1), 4:5, 2:3), list(c(0,2), c(-1,1)))
b <- list(7:9, c(5,-1))
> a
[[1]]
[[1]][[1]]
[1] -2 1
[[1]][[2]]
[1] 4 5
[[1]][[3]]
[1] 2 3
[[2]]
[[2]][[1]]
[1] 0 2
[[2]][[2]]
[1] -1 1
> b
[[1]]
[1] 7 8 9
[[2]]
[1] 5 -1
I want to multiply each of (-2, 1) from a[[1] with 7 from b[[1]] to get (-14, 7), each of (4, 5) with 8, each of (2, 3) with 9, and then each of (0, 2) with 5 and finally each of (-1, 1), with -1.
I can be sure that length(a[[i]])==length(b[[i]]) is TRUE for i=1,2 (in practice, i is way larger), so that there are the right number of entries for the desired multiplications.
However, it is not clear how many entries the a[[i]]) have (in the example, 3 for a[[1]] and 2 for a[[2]], or equivalently, how long the b[[i]] are), except that they'll have at least one entry. Hence, transforming a and b into matrices does not seem practical.
I am not sure that is relevant to the problem, but it will also be the case that we have as many entries in each of the a[[i]]) (i.e., 2) as we have a[[i]])s.
I was thinking of some combination of do.call and mapply, but could not get it to work.

We may indeed use mapply (and Map, which is the same as mapply but with SIMPLIFY = FALSE). Depending on the format (matrix as in #RonakShah's answer or a list as in your question), you may use
Map(mapply, a, b, MoreArgs = list(FUN = `*`))
# [[1]]
# [,1] [,2] [,3]
# [1,] -14 32 18
# [2,] 7 40 27
#
# [[2]]
# [,1] [,2]
# [1,] 0 1
# [2,] 10 -1
or
Map(Map, a, b, MoreArgs = list(f = `*`))
# [[1]]
# [[1]][[1]]
# [1] -14 7
#
# [[1]][[2]]
# [1] 32 40
#
# [[1]][[3]]
# [1] 18 27
#
#
#[[2]]
# [[2]][[1]]
# [1] 0 10
#
# [[2]][[2]]
# [1] 1 -1
A tidyverse alternative to the latter is
map2(a, b, map2, `*`)

Since, you can ensure length(a[[i]])==length(b[[i]]) we can use mapply inside lapply
lapply(seq_along(a), function(x) mapply("*", a[[x]], b[[x]]))
#[[1]]
# [,1] [,2] [,3]
#[1,] -14 32 18
#[2,] 7 40 27
#[[2]]
# [,1] [,2]
#[1,] 0 1
#[2,] 10 -1

Related

Imitate `simplify=F` in `apply()` for versions of R < 4.1.0

Given a binary matrix, I want to generate a list of the positions of the 1s by row. For example:
> M <- matrix(c(1,1,0,0,1,1,1,0,1), 3, 3)
> M
[,1] [,2] [,3]
[1,] 1 0 1
[2,] 1 1 0
[3,] 0 1 1
> apply(M==1, 1, which, simplify = FALSE)
[[1]]
[1] 1 3
[[2]]
[1] 1 2
[[3]]
[1] 2 3
However, in versions of R before 4.1.0, the apply() function automatically simplifies, which means the result will sometimes not be a list, but gets simplified into a vector or matrix. For example:
> apply(M==1, 1, which)
[,1] [,2] [,3]
[1,] 1 1 2
[2,] 3 2 3
How can I ensure the output will always be a list, even in earlier versions of R when the simplify = F parameter isn't available for apply()? Thanks!
You can use asplit, which splits the matrix into a list of vectors by the selected margin. Then use lapply on the result to apply the which to each vector.
lapply(asplit(M == 1, 1), which)
#> [[1]]
#> [1] 1 3
#>
#> [[2]]
#> [1] 1 2
#>
#> [[3]]
#> [1] 2 3
We may use which with arr.ind = TRUE and then split by the 'row'
ind <- which(M == 1, arr.ind = TRUE)
split(ind[,2], ind[, 1])
$`1`
[1] 1 3
$`2`
[1] 1 2
$`3`
[1] 2 3

Finding minimum of each rows from a list and their corresponding column number

A reproducible example:
mat1 <- matrix(c(1,2,4,2,4,2,4,6,5,7,8,3), nrow = 3, ncol = 4, byrow = T)
mat2 <- matrix(c(2,1,7,8,2,6), nrow = 3, ncol = 2, byrow = T)
mat3 <- matrix(c(3,2,3,5,7,5,4,5,6,4,2,3,4,5,2), nrow = 3, ncol = 5, byrow = T)
list.mat <- list(mat1,mat2,mat3)
> list.mat
[[1]]
[,1] [,2] [,3] [,4]
[1,] 1 2 4 2
[2,] 4 2 4 6
[3,] 5 7 8 3
[[2]]
[,1] [,2]
[1,] 2 1
[2,] 7 8
[3,] 2 6
[[3]]
[,1] [,2] [,3] [,4] [,5]
[1,] 3 2 3 5 7
[2,] 5 4 5 6 4
[3,] 2 3 4 5 2
Objective 1: Find the minimum of each rows of each element. Expected output [[1]]-> 1,2,3 [[2]]-> 1,7,2 [[3]]-> 2,4,2
Objective 2: Find the corresponding column numbers. Expected output [[1]]-> 1,2,4 [[2]]-> 2,1,1 [[3]]-> 2,2,1
***NOTE that in [[3]][3,] there are two minimum numbers, one in column 1 and other in column 5. In such case, only report the column that comes first.
Objective 3: Find the sum of the output found in objective 1 separately for each list. Expected outcome [[1]]-> 6 [[2]]-> 10 [[3]]-> 8
I am looking for a general solution applicable to a much larger list than the example provided.
You can use lapply :
output1 <- lapply(list.mat, function(x) apply(x, 1, min))
output1
#[[1]]
#[1] 1 2 3
#[[2]]
#[1] 1 7 2
#[[3]]
#[1] 2 4 2
output2 <- lapply(list.mat, function(x) apply(x, 1, which.min))
output2
#[[1]]
#[1] 1 2 4
#[[2]]
#[1] 2 1 1
#[[3]]
#[1] 2 2 1
output3 <- lapply(output1, sum)
output3
#[[1]]
#[1] 6
#[[2]]
#[1] 10
#[[3]]
#[1] 8
You can put the code in a function if you want to apply this for multiple such lists.
We could use dapply from collapse which could be faster
library(collapse)
lapply(list.mat, dapply, fmin, MARGIN = 1)
#[[1]]
#[1] 1 2 3
#[[2]]
#[1] 1 7 2
#[[3]]
#[1] 2 4 2
Here is a base R option using pmin
> lapply(list.mat, function(x) do.call(pmin, data.frame(x)))
[[1]]
[1] 1 2 3
[[2]]
[1] 1 7 2
[[3]]
[1] 2 4 2

Use mapply or lapply to nested list

I want to apply a sample function to a nested list (I will call this list bb) and I also have a list of numbers (I will call this list k) to be supplied in the sample function. I would like each of the numbers in k to iterate through all the values of each list in bb. How to do this using mapply or lapply?
Here are the data:
k <- list(1,2,4,3) #this is the list of numbers to be supplied in the `sample.int` function
b1 <- list(c(1,2,3),c(2,3,4),c(3,4,5),c(4,5,6)) #The first list of bb
b2 <- list(c(1,2),c(2,3),c(3,4),c(4,5), c(5,6)) #The second list of bb
bb <- list(b1,b2) #This is list bb containing b1 and b2 whose values are to be iterated through
I created this mapply function but it didn't get the expected outcome:
mapply(function(x, y) {
x[sample.int(y,y, replace = TRUE)]
}, bb,k, SIMPLIFY = FALSE)
This only returns 10 output values but I would like each number of k to loop through all values of the two lists in bb and so there should be 10*2 outputs for the two lists in bb. I might be using mapply in the wrong way and so I would appreciate if anyone can point me to the right direction!
outer is your friend. It's normally used to calculate the outer matrix product. Consider:
outer(1:3, 2:4)
1:3 %o% 2:4 ## or
# [,1] [,2] [,3]
# [1,] 2 3 4
# [2,] 4 6 8
# [3,] 6 9 12
It also has a FUN= argument that defaults to "*". However it enables you to calculate any function over the combinations of x and y cross-wise, i.e. x[1] X y[1], x[1] X y[2], ... whereas *apply functions only calculate x[1] X y[1], x[2] X y[2], .... So let's do it:
FUN <- Vectorize(function(x, y) x[sample.int(y, y)])
set.seed(42)
res <- outer(bb, k, FUN)
res
# [,1] [,2] [,3] [,4]
# [1,] List,1 List,2 List,4 List,3
# [2,] List,1 List,2 List,4 List,3
This result looks a little weird, but we may easily unlist it.
res <- unlist(res, recursive=F)
Result
res
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [1] 1 2
#
# [[3]]
# [1] 1 2 3
#
# [[4]]
# [1] 2 3 4
#
# [[5]]
# [1] 2 3
#
# [[6]]
# [1] 1 2
#
# [[7]]
# [1] 2 3 4
#
# [[8]]
# [1] 4 5 6
#
# [[9]]
# [1] 1 2 3
#
# [[10]]
# [1] 3 4 5
#
# [[11]]
# [1] 3 4
#
# [[12]]
# [1] 4 5
#
# [[13]]
# [1] 2 3
#
# [[14]]
# [1] 1 2
#
# [[15]]
# [1] 1 2 3
#
# [[16]]
# [1] 2 3 4
#
# [[17]]
# [1] 3 4 5
#
# [[18]]
# [1] 2 3
#
# [[19]]
# [1] 3 4
#
# [[20]]
# [1] 1 2
VoilĂ , 20 results.

Getting all the combination of numbers from a list that would sum to a specific number

I have the following list of numbers (1,3,4,5,7,9,10,12,15) and I want to find out all the possible combinations of 3 numbers from this list that would sum to 20.
My research on stackoverflow has led me to this post:
Finding all possible combinations of numbers to reach a given sum
There is a solution provided by Mark which stand as follows:
subset_sum = function(numbers,target,partial=0){
if(any(is.na(partial))) return()
s = sum(partial)
if(s == target) print(sprintf("sum(%s)=%s",paste(partial[-1],collapse="+"),target))
if(s > target) return()
for( i in seq_along(numbers)){
n = numbers[i]
remaining = numbers[(i+1):length(numbers)]
subset_sum(remaining,target,c(partial,n))
}
}
However I am having a hard time trying to tweak this set of codes to match my problem. Or may be there is a simpler solution?
I want the output in R to show me the list of numbers.
Any help would be appreciated.
You can use combn function and filter to meet your criteria. I have performed below calculation in 2 steps but one can perform it in single step too.
v <- c(1,3,4,5,7,9,10,12,15)
AllComb <- combn(v, 3) #generates all combination taking 3 at a time.
PossibleComb <- AllComb[,colSums(AllComb) == 20] #filter those with sum == 20
#Result: 6 sets of 3 numbers (column-wise)
PossibleComb
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 3 3 4
# [2,] 4 7 9 5 7 7
# [3,] 15 12 10 12 10 9
#
# Result in list
split(PossibleComb, col(PossibleComb))
# $`1`
# [1] 1 4 15
#
# $`2`
# [1] 1 7 12
#
# $`3`
# [1] 1 9 10
#
# $`4`
# [1] 3 5 12
#
# $`5`
# [1] 3 7 10
#
# $`6`
# [1] 4 7 9
The combn also have a FUN parameter which we can describe to output as list and then Filter the list elements based on the condition
Filter(function(x) sum(x) == 20, combn(v, 3, FUN = list))
#[[1]]
#[1] 1 4 15
#[[2]]
#[1] 1 7 12
#[[3]]
#[1] 1 9 10
#[[4]]
#[1] 3 5 12
#[[5]]
#[1] 3 7 10
#[[6]]
#[1] 4 7 9
data
v <- c(1,3,4,5,7,9,10,12,15)

Getting all splits of numeric sequence in R

I'm trying to get all the possible splits of a sequence [1:n] in R. E.g.:
getSplits(0,3)
Should return all possible splits of the sequence 123, in other words (in a list of vectors):
[1] 1
[2] 1 2
[3] 1 2 3
[4] 1 3
[5] 2
[6] 2 3
[7] 3
Now I've created a function which does get to these vectors recursively, but having trouble combining them into one as above. My function is:
getSplits <- function(currentDigit, lastDigit, split) {
splits=list();
for (nextDigit in currentDigit: lastDigit)
{
currentSplit <- c(split, c(nextDigit));
print(currentSplit);
if(nextDigit < lastDigit) {
possibleSplits = c(list(currentSplit), getSplits(nextDigit+1, lastDigit, currentSplit));
}else{
possibleSplits = currentSplit;
}
splits <- c(splits, list(possibleSplits));
}
return(splits);
}
Where printing each currentSplit results in all the right vectors I need, but somehow the final returnt list (splits) nests them into deeper levels of lists, returning:
[1] 1
[[1]][[2]]
[[1]][[2]][[1]]
[1] 1 2
[[1]][[2]][[2]]
[1] 1 2 3
[[1]][[3]]
[1] 1 3
[[2]]
[[2]][[1]]
[1] 2
[[2]][[2]]
[1] 2 3
[[3]]
[1] 3
For the corresponding function call getSplits(1, 3, c()).
If anyone could help me out on getting this to work the way I described above, it'd be much appreciated!
character vector output
Try combn:
k <- 3
s <- unlist(lapply(1:k, combn, x = k, toString))
s
## [1] "1" "2" "3" "1, 2" "1, 3" "2, 3" "1, 2, 3"
data frame output
If you would prefer that the output be in the form of a data frame:
read.table(text = s, header = FALSE, sep = ",", fill = TRUE, col.names = 1:k)
giving:
X1 X2 X3
1 1 NA NA
2 2 NA NA
3 3 NA NA
4 1 2 NA
5 1 3 NA
6 2 3 NA
7 1 2 3
list output
or a list:
lapply(s, function(x) scan(textConnection(x), quiet = TRUE, sep = ","))
giving:
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 1 2
[[5]]
[1] 1 3
[[6]]
[1] 2 3
[[7]]
[1] 1 2 3
Update: Have incorporated improvement mentioned in comments as well as one further simplification and also added data frame and list output.
Here is another approach:
f <- function(nums) sapply(1:length(nums), function(x) t(combn(nums, m = x)))
f(1:3)
This yields
[[1]]
[,1]
[1,] 1
[2,] 2
[3,] 3
[[2]]
[,1] [,2]
[1,] 1 2
[2,] 1 3
[3,] 2 3
[[3]]
[,1] [,2] [,3]
[1,] 1 2 3
The OP is looking for the Power set of c(1,2,3). There are several packages that will quickly get you this in one line. Using the package rje, we have:
library(rje)
powerSet(c(1,2,3))
[[1]]
numeric(0)
[[2]]
[1] 1
[[3]]
[1] 2
[[4]]
[1] 1 2
[[5]]
[1] 3
[[6]]
[1] 1 3
[[7]]
[1] 2 3
[[8]]
[1] 1 2 3
... and with iterpc:
library(iterpc)
getall(iterpc(c(2,1,1,1), 3, labels = 0:3))
[,1] [,2] [,3]
[1,] 0 0 1
[2,] 0 0 2
[3,] 0 0 3
[4,] 0 1 2
[5,] 0 1 3
[6,] 0 2 3
[7,] 1 2 3
More generally,
n <- 3
getall(iterpc(c(n-1,rep(1, n)), n, labels = 0:n)) ## same as above

Resources