2
$\begingroup$

I'm running some analysis that requires the creation, row by row, of a large list of lists. The big list is called "Nodes" and it will ultimately contain hundreds of thousands of rows. The first few rows will look like this ...

Row 1: {{1,3,5}, {2,4,6}, {7}, {6,7,8,9}}

Row 2: {{7,3,8}, {2,5,4}, {2}, {6,2,8,4}}

Row 3: {{9,7,5}, {5,4,6}, {1}, {6,7,8,9}}

I've tried AppendTo and Join in this way ...

Nodes = AppendTo[Nodes,List[{8,3,2},{1,2,3},{8},{8,3,3,2}]];

Nodes = Join[Nodes,List[{8,3,2},{1,2,3},{8},{8,3,3,2}]];

and they both take quite a long time to run.

I also tried creating a huge Nodes array ahead of time and then replacing each row, something like this ...

Nodes = Range[200000];
For[ji = 1, ji < 200001, ji++, Nodes[[ji]] = List[{}, {}, {}, {}]];

followed by

Nodes[[nodesrownumber]] = List[{8,3,2},{1,2,3},{8},{8,3,3,2}]];

and this seems to take even longer.

Any suggestions for improving the speed of this? Thanks, very much, in advance.

$\endgroup$
  • $\begingroup$ Welcome to Mathematica.SE! I hope you will become a regular contributor. To get started, 1) take the introductory Tour now, 2) when you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge, 3) remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign, and 4) give help too, by answering questions in your areas of expertise. $\endgroup$ – bbgodfrey Jul 31 '15 at 3:57
  • 2
    $\begingroup$ Iteratively appending/joining will kill performance. Also, you don't need to set the variable when using AppendTo. In any case, where do the elements come from? Are you generating them randomly, from prior results in list, from some other list, from... you need to specify this question much more precisely. $\endgroup$ – ciao Jul 31 '15 at 4:11
  • 2
    $\begingroup$ AppendTo Is probably your problem because it generates a copy of its argument before appending. Look into using e.g. Reap and Sow instead (blog.wolfram.com/2011/12/07/… and mathematica.stackexchange.com/questions/70149/…). We will also need to know how you are generating your lists in order to help you. $\endgroup$ – MarcoB Jul 31 '15 at 4:15
  • $\begingroup$ Related, perhaps even duplicate?: (2930) $\endgroup$ – Mr.Wizard Jul 31 '15 at 4:33
  • 1
    $\begingroup$ @Marco, I forgot about that blog entry; thanks for linking to it. However, tip #3 seems to be becoming a bit less applicable with each new version, unfortunately. :( $\endgroup$ – J. M.'s discontentment Jul 31 '15 at 5:01
4
$\begingroup$

One approach is to construct the rows with a head other than list, say row, then accumulate the rows as a linked list, and finally transform the linked list into a simple list of sublists of the form wanted. Here is an example.

Row constructor.

 newRow[] := 
   With[{rs = RandomSample[Range[11]]},
     row[rs[[;; 3]], rs[[4 ;; 6]], {rs[[7]]}, rs[[8 ;;]]]]

On my system, a not very fast, aging iMac, I get the following timing for making a table of 100000 rows as I described.

timer[n_] :=
  Module[{data = {}},
   AbsoluteTiming[
     Do[data = {data, newRow[]}, {n}];
     List @@@ Flatten@data][[1]]]

timer[100000]
0.866738

That's less than a second for a 100000 rows, so maybe it will be fast enough for you.

And just to show that the process actually produces the structures described, consider

Module[{data = {}},
  Do[data = {data, newRow[]}, {3}];
  Column[{data, List @@@ Flatten@data}]]

results

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ slick. This could probably use a little more explanation. $\endgroup$ – george2079 Jul 31 '15 at 19:02
1
$\begingroup$

Maybe there's something I do not understand because this seems fast to me. I used Do instead of For, since it is faster:

(nodes = ConstantArray[0, {200000}];
  Do[
   nodes[[ji]] = List[{8, 3, 2}, {1, 2, 3}, {8}, {8, 3, 3, 2}],
   {ji, 200000}];) // AbsoluteTiming
(*  {0.13, Null}  *)

But it's not even twice as fast as For:

(nodes = ConstantArray[0, {200000}];
  Block[{ji},
   For[ji = 1, ji < 200001, ji++,
    nodes[[ji]] = List[{8, 3, 2}, {1, 2, 3}, {8}, {8, 3, 3, 2}]
    ]];) // AbsoluteTiming
(*  {0.246, Null}  *)

If the calculation of a row can be done independently, either as a function of some parameters or none, then Table will be faster still:

calcrow[i] := List[{8, 3, 2}, {1, 2, 3}, {8}, {8, 3, 3, 2}];
(nodes = Table[
     calcrow[i],
     {i, 200000}];) // RepeatedTiming
(*  {0.086, Null}  *)

nextrow[] := List[{8, 3, 2}, {1, 2, 3}, {8}, {8, 3, 3, 2}];
(nodes = Table[
     nextrow[],
     {i, 200000}];) // RepeatedTiming
(*  {0.018, Null}  *)

Some baseline comparisons for iteration and memory storage:

(* all 1's *)
(#[1, {200000}] // RepeatedTiming // First) & /@ {Do, Table, ConstantArray}
(*  {0.0031, 0.0034, 0.0000663}  *)

(* rows of 11 elements *)
(#[Evaluate@ Internal`PartitionRagged[Range@11, {3, 3, 1, 4}], {200000}] // 
     RepeatedTiming // First) & /@ {Table, ConstantArray}
(*  {0.0062, 0.0012}  *)

Notes: Do does not produce a table, so it gives some idea of a lower bound on iteration. ConstantArray is a rather specialized & optimized table-generator; timings with Table probably reflects the lower bound in generating a table.

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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