Multiplying Combinations of a List of Lists in R

Multiplying Combinations of a list of lists in R

Have no idea if this is fast or memory intensive just that it works, Joris Meys's answer is more eloquent:

x <- expand.grid(1:length(a), 1:length(b))
x <- x[order(x$Var1), ] #gives the order you asked for
FUN <- function(i) diag(outer(a[[x[i, 1]]], b[[x[i, 2]]], "*"))
sapply(1:nrow(x), FUN) #I like this out put
lapply(1:nrow(x), FUN) #This one matches what you asked for

EDIT: Now that Brian introduced benchmarking (which I love (LINK)) I have to respond. I actually have a faster answer using what I call expand.grid2 that's a lighter weight version of the original that I stole from HERE. I was going to throw it up before but when I saw how fast Joris's is I figured why bother, both short and sweet but also fast. But now that Diggs has dug I figured I'd throw up here the expand.grid2 for educational purposes.

expand.grid2 <-function(seq1,seq2) {
cbind(Var1 = rep.int(seq1, length(seq2)),
Var2 = rep.int(seq2, rep.int(length(seq1),length(seq2))))
}

x <- expand.grid2(1:length(a), 1:length(b))
x <- x[order(x[,'Var1']), ] #gives the order you asked for
FUN <- function(i) diag(outer(a[[x[i, 1]]], b[[x[i, 2]]], "*"))
lapply(1:nrow(x), FUN)

Here's the results (same labeling as Bryan's except TylerEG2 is using the expand.grid2):

Unit: microseconds
expr min lq median uq max
1 DiggsL(a, b) 5102.296 5307.816 5471.578 5887.516 70965.58
2 DiggsM(a, b) 384.912 428.769 443.466 461.428 36213.89
3 Joris(a, b) 91.446 105.210 123.172 130.171 16833.47
4 TylerEG2(a, b) 392.377 425.503 438.100 453.263 32208.94
5 TylerL(a, b) 1752.398 1808.852 1847.577 1975.880 49214.10
6 TylerM(a, b) 1827.515 1888.867 1925.959 2090.421 75766.01
7 Wojciech(a, b) 1719.740 1771.760 1807.686 1924.325 81666.12

And if I take the ordering step out I can squeak out even more but it still isn't close to Joris's answer.

Sample Image

Multiplying two different lists objects in R

You can use map as shown below:

Map('*',x,y)

Output of the above code is shown below:

> Map('*',x,y)
[[1]]
[,1] [,2]
[1,] 1 0
[2,] 0 4

[[2]]
[,1] [,2]
[1,] 0 0
[2,] 6 8

OR

You can use unlist to unlist list and multiple list together:

Listxy <- list(unlist(x)*unlist(y))
Listxy
[[1]]
[1] 1 0 0 4 0 6 0 8

How to multiply a list of vector by list of arrays (by columns) in R

We can try with Map

res <- Map(function(u, v) {
u[u!=0] <- u[u!=0]*rep(v, dim(u)[3])
u},
a, x)
a[[1]][2,1,1]*x[[1]][1]
#[1] 0.3840722
a[[1]][2,1,2]*x[[1]][1]
#[1] -0.8685313

res[[1]][2, 1, 1]
#[1] 0.3840722
res[[1]][2, 1, 2]
#[1] -0.8685313

Matrix calculation between list objects in R

We can use combn to create pairwise combination on the sequence of the list, extract the elements and do the multiplication

new_lst <- combn(seq_along(results), 2, \(i) matr[,i[1]] * matr[,i[2]] * 
results[[i[1]]] * results[[i[2]]], simplify = FALSE)
names(new_lst) <- combn(seq_along(results), 2, paste, collapse="_")

-output

> new_lst
$`1_2`
[,1] [,2] [,3]
[1,] 64.135122 2.6966755 12.4307531
[2,] 2.696676 0.1133865 0.5226732
[3,] 12.430753 0.5226732 2.4093448

$`1_3`
[,1] [,2] [,3]
[1,] 5.775451 -1.2624981 -5.095849
[2,] -1.262498 0.2759787 1.113939
[3,] -5.095849 1.1139391 4.496217

$`1_4`
[,1] [,2] [,3]
[1,] 18.46710 -2.275650 -18.610758
[2,] -2.27565 0.280422 2.293352
[3,] -18.61076 2.293352 18.755530

$`2_3`
[,1] [,2] [,3]
[1,] 43.621251 -7.589849 -9.242303
[2,] -7.589849 1.320590 1.608108
[3,] -9.242303 1.608108 1.958223

$`2_4`
[,1] [,2] [,3]
[1,] 139.47970 -13.680683 -33.754187
[2,] -13.68068 1.341852 3.310735
[3,] -33.75419 3.310735 8.168537

$`3_4`
[,1] [,2] [,3]
[1,] 12.560327 6.404863 13.837154
[2,] 6.404863 3.266019 7.055953
[3,] 13.837154 7.055953 15.243778

multiply two lists of irregular length

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, `*`)

Prolog - finding all combinations (The product) of List of Lists (of Lists)

I'm not so sure this is the most efficient approach, but it's fairly transparent. The idea here is to define the problem in recursive (or inductive) "layers":

% multiply_lists(ListOfLists, MultipliedListOfLists)
%
% The first two clauses handle the case where ListOfLists consists
% of just one list
% The third clause handles the general case
%
multiply_lists([[X]], [[X]]).
multiply_lists([[X|Xs]], [[X]|T]) :-
multiply_lists([Xs], T).
multiply_lists([E|Es], R) :-
multiply_lists(Es, R1),
multiply_list(E, R1, R).

% multiply_list relates the product of a list of lists and a single list
% of elements
%
multiply_list([], _, []).
multiply_list([E|Es], L, Ls) :-
multiply_list(Es, L, LL),
multiply_element(E, L, LL, Ls).

% multiply_element relates the product, prepended to a given list,
% of a single list of lists and a single element
%
multiply_element(_, [], A, A).
multiply_element(X, [Y|Ys], A, [[X|Y]|T]) :-
multiply_element(X, Ys, A, T).

multiply_element/4 actually combines two rules into one: it defines multiplication of a list by a single element, and prepends those results as individual elements to a given list.

A sample result:

| ?- multiply_lists([[1, 2], [1, 2, 3]], L).

L = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]] ? ;

no
| ?- multiply_lists([[a,b,c], [1,2], [x,y]], L).

L = [[a,1,x],[a,1,y],[a,2,x],[a,2,y],[b,1,x],[b,1,y],[b,2,x],[b,2,y],[c,1,x],[c,1,y],[c,2,x],[c,2,y]] ? ;

no

Some quirks with the above implementation:

  • It's not tail recursive (so will use more stack as the lists get longer)
  • It leaves a choice point

But it does illustrate how the problem can be solved without using append/3 or other list-based pre-defined predicates.



Related Topics



Leave a reply



Submit