4
$\begingroup$

If I have a list of lists $a$ and a list of occurrences of each sublist $n$

a={ {1,1,1}, {2,2,2}, {3,3,3} }
n={1,3,2}

what is the most efficient way to get the following list?

l={{1,1,1},{2,2,2},{2,2,2},{2,2,2},{3,3,3},{3,3,3}}

My current implementation is

l=Flatten[Table[Table[a[[i]],{x,n[[i]]}],{i,Range[Length[n]]}],1]

EDIT

Thank you for the answers!

For the problem as posted, the fastest solution is Catenate[...] from march (inspired by J.M.) at 1.7 10^-5 seconds (AbsoluteTiming), with all other solutions being above 2 10^-5.

If I drastically increase the number of samples I want (for example multiplying n*100), then Catenate[MapThread[Table, {a, List /@ n}]]; takes 1.5 10^-3 seconds and the fastest solution by far is a[[Join @@ MapIndexed[ConstantArray[#2[[1]], #1] &, n]]]; from ubpdqn, at 7.7 10^-5.

In both cases, solutions from garej had intermediate timings.

So I guess the ideal solution depends on the exact problem (size of the array, number of samples, ...) and may or may not have a great impact on the overall performance.

$\endgroup$
2
  • 6
    $\begingroup$ Flatten[MapThread[ConstantArray, {a, n}], 1]? $\endgroup$ Commented Apr 4, 2016 at 15:01
  • $\begingroup$ @Delphine If you're using Mma 10, I think Catenate will be about 20% faster than Flatten[..., 1]. $\endgroup$ Commented Apr 4, 2016 at 15:18

4 Answers 4

7
$\begingroup$
a[[Join @@ MapIndexed[ConstantArray[#2[[1]], #1] &, n]]]
$\endgroup$
4
  • $\begingroup$ Your solution is actually the fastest. Can you explain a bit what it does? $\endgroup$
    – Delphine
    Commented Apr 5, 2016 at 9:54
  • $\begingroup$ @Delphine MapIndexed uses the index as position in list.ConstantArray just produces the desired number of the position. Join just makes a flat list and then you just use Part ([[...]]) to select the desired outcome. @MartinButtner notes Catenate is faster. This could also be used. $\endgroup$
    – ubpdqn
    Commented Apr 5, 2016 at 10:26
  • $\begingroup$ @ubpdqn, If possible please tell why did not you use of Flatten instead of Join? is there any special reason? $\endgroup$ Commented Jun 2, 2016 at 19:28
  • $\begingroup$ @Irreversible No special reason. Could use Join. $\endgroup$
    – ubpdqn
    Commented Jun 2, 2016 at 22:35
5
$\begingroup$
Table @@@ Transpose[{a, n}] // Catenate

{{1, 1, 1}, {2, 2, 2}, {2, 2, 2}, {2, 2, 2}, {3, 3, 3}, {3, 3, 3}}

Edit Also

Join @@ Table @@@ Thread[{a, n}]
$\endgroup$
1
  • $\begingroup$ These are good solutions too, very elegant, but not the fastest. $\endgroup$
    – Delphine
    Commented Apr 5, 2016 at 10:10
3
$\begingroup$

From a comment by J.M., what I consider to be the most natural solution:

Flatten[MapThread[ConstantArray, {a, n}], 1];

Via Martin, alternatively do

MapThread[ConstantArray, {a, n}] // Catenate;

You can also do something similar using pure functions and Apply:

ConstantArray[#1, #2] & @@@ Thread[{a, n}] // Catenate;

This is just about as fast as the previous version. You can also get around having to Catenate at the end by using Sequence all along the way, i.e.

Sequence @@ ConstantArray[#1, #2] & @@@ Thread[{a, n}];

but it turns out that this is a little slower (maybe 25% slower).

I tried to come up with a ReplaceAll version, but they were all ten times slower.

The fastest so far seems to be again by J.M. Pre 10.2 version is

Catenate[MapThread[Table, {a, List /@ n}]];

and 10.2 and later is

Catenate[MapThread[Table, {a, n}]];
$\endgroup$
4
  • 2
    $\begingroup$ Apparently Table[] also works: Catenate[MapThread[Table, {a, n}]] $\endgroup$ Commented Apr 4, 2016 at 17:37
  • 1
    $\begingroup$ @J.M. Perhaps in version newer than V10.0. I had to do Catenate[MapThread[Table, {a, List /@ n}]] to make that work, but it turned out to be 4 times faster than the others! $\endgroup$
    – march
    Commented Apr 4, 2016 at 17:44
  • $\begingroup$ yes, that's the classical syntax for Table[]: Table[1, {5}]. Thus my surprise when I forgot to put in the list for the iterator and it worked anyway. $\endgroup$ Commented Apr 4, 2016 at 17:46
  • $\begingroup$ I varied the size of the array a and the number of replicates n. It appears this last solution is the fastest, but only if the arrays or the number of replicates are small. $\endgroup$
    – Delphine
    Commented Apr 5, 2016 at 10:08
0
$\begingroup$
list = {{1, 1, 1}, {2, 2, 2}, {3, 3, 3}};

n = {1, 3, 2};

Using MapApply (new in 13.1) and Splice (new in 12.1)

MapApply[Splice @* Table] @ Transpose[{list, n}]

{{1, 1, 1}, {2, 2, 2}, {2, 2, 2}, {2, 2, 2}, {3, 3, 3}, {3, 3, 3}}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.