1
$\begingroup$

I want to make a list of many (say 1000) test instances for a geometry problem. The problem involves two line segments and a point. So I need

{
    (* first line *)     (*second line*)   (*point*)
  { {{x0,y0},{x1,y1}} , {{x2,y2},{x3,y3}} , {x4,y4} },
  ... (*1000 times*)
}

Now this is quite easy to achieve with

{#[[{1, 2}]], #[[{3, 4}]], #[[5]]} & /@ 
  RandomReal[{-100, 100}, {100, 5, 2}]

The Dimensions of that desired array are {1000,3,2}, which ignores the fact that the lines have extra depth while the point does not.

Is there a way to make this reshaping happen with ArrayReshape, or is it for strictly rectangular reshaping? Is there a clever way to use other functions like Thread, Transpose etc with their powerful options to do this?

I'll do some benchmarking for curiosity's sake.


I recently asked a similar question Thread Matrices, like image channels. With my wording, the most direct answer (restricted to Thread), isn't the best -- it's good to see plenty of votes for all the answers.

$\endgroup$
5
  • 1
    $\begingroup$ ArrayReshape >>Details: always gives a rectangular array of the specified dimensions $\endgroup$
    – kglr
    Commented Mar 22, 2022 at 0:26
  • $\begingroup$ What would be the correct output of Dimensions instead of {1000,3,2}? $\endgroup$
    – bmf
    Commented Mar 22, 2022 at 0:35
  • $\begingroup$ That's the correct output of Dimensions; I'm not looking for a generalized Dimensions function. I suppose I meant other functions in conjunction with ArrayReshape, smarter than simple mapping hacks (i.e. producing 1000 elements of the form {{{x0,y0},{x1,y1}},{{x2,y2},{x3,y3}},{{x4,y4}}} and then Mapping to turn the {{x4,y4}}'s into {x4,y4}'s). $\endgroup$
    – Adam
    Commented Mar 22, 2022 at 1:09
  • 2
    $\begingroup$ you might want to try {{#, #2}, {#3, #4}, #5} & @@@ list and Transpose[{#[[All, {1, 2}]], #[[All, {3, 4}]], #[[All, 5]]}] &@list where list is your input list with dimensions {100,5,2} $\endgroup$
    – kglr
    Commented Mar 22, 2022 at 3:41
  • $\begingroup$ Related: mathematica.stackexchange.com/questions/7511/… $\endgroup$
    – Michael E2
    Commented Mar 22, 2022 at 21:28

2 Answers 2

2
$\begingroup$

I might do something like this:

With[
  {n = 1000},
  Transpose[{RandomReal[{-100, 100}, {n, 2, 2}], 
    RandomReal[{-100, 100}, {n, 2, 2}], 
    RandomReal[{-100, 100}, {n, 2}]}]]

I don't think there's much savings in overhead by requiring that RandomReal only be called once.

Another approach that overgenerates the original data (but avoids a row-by-row re-partitioning):

MapAt[First, RandomReal[{-100, 100}, {1000, 3, 2, 2}], {All, 3}]
$\endgroup$
2
$\begingroup$

Alright I don't know how to make nice looking plots yet

plot

but kglr's approach

Transpose[{#[[All, {1, 2}]], #[[All, {3, 4}]], #[[All, 5]]}] &@list

and lecicr's first approach that calls RandomReal three times are both fastest, presumably by a constant factor by the looks of it.


The big takeaway for me is that calling RandomReal three times instead of once is a tiny performance hit. The following code could be fleshed out into a super informative test, but here it is for now

dat = Table[With[{sampleparts = Differences@Round[Range[0, m] n/m]},
SeedRandom@102;
First@AbsoluteTiming[RandomReal[{-100, 100}, #] & /@ sampleparts]
], {n, 1, 500}, {m, 1, n}];

It divides n into m approximately equal sized chunks (e.g. 47=4+3+4+3+4+4+3+4+4+3+4+3+4) and then calls RandomReal to generate the total amount of numbers in all the different sizes of chunks (all in 1 chunk, 2 chunks, 3 chunks... up to n chunks of size 1). Then the code

ListPlot3D[PadRight[#, 500] & /@ dat, 
AxesLabel -> {"m roughly uniform chunks", "n total random numbers", 
"total time"}]

yields randomtimingplot

You can see that around m=100 chunks there's a huge jump. Seeing this sort of plot for large numbers on a logloglog scale would be cool.

$\endgroup$
6
  • $\begingroup$ Since the performance of random functions isn't exactly on topic, lmk if I should ask a new question and move some stuff, or if there's a trickier way to migrate info somehow. $\endgroup$
    – Adam
    Commented Mar 22, 2022 at 20:43
  • 2
    $\begingroup$ If you call RandomReal once, you get a packed array that has to be unpacked since your desired data structure is not rectangular. So any savings in the single call takes hit in restructuring the data. Both the savings and the hits are probably small on data this size. As for the jump, I believe SystemOptions["CompileOptions" -> "MapCompileLength"] explains it. It has nothing to do with RandomReal, I believe. (SystemOptions["CompileOptions"] shows other such boundaries as well.) $\endgroup$
    – Michael E2
    Commented Mar 22, 2022 at 21:58
  • 1
    $\begingroup$ See for instance (54994), (55242) $\endgroup$
    – Michael E2
    Commented Mar 22, 2022 at 22:06
  • $\begingroup$ So then kglr's approach with [[All...]] also avoids unpacking/restructuring? $\endgroup$
    – Adam
    Commented Mar 22, 2022 at 22:18
  • 1
    $\begingroup$ Yes, list[[All, {1,2}]] avoids unpacking, but Transpose in that comment will unpack the arrays because the result is not rectangular. If you think about it, your ultimate output is a list of geometric primitives, and at some point the data has to be copied out of a packed array. I also wonder if it's worth worrying about performance too much for only 1000 instances. (And thanks for the compliment!) $\endgroup$
    – Michael E2
    Commented Mar 22, 2022 at 23:28

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.