13
$\begingroup$

Update: WRI Support has considered this a missing feature of MapThread instead of a bug. A request to update MapThread to support ragged arrays has been logged.


MapThread is not mapping over ragged arrays. It is my expectation that it should map given Map's behavior on ragged arrays and no information to the contrary in MapThread's documentation.

Map works fine on ragged array.

Map[f, Range@Range@3, {2}]
{{f[1]}, {f[1], f[2]}, {f[1], f[2], f[3]}}

But MapThread has issues.

MapThread[f, 
 {
  Range@Range@3,
  Range@Range@3
  },
 2]

MapThread::mptd: Object {{1},{1,2},{1,2,3}} at position {2, 1} in MapThread[f,{{{1},{1,2},{1,2,3}},{{1},{1,2},{1,2,3}}},2] has only 1 of required 2 dimensions.

It is complaining about missing dimensions but the two lists are identical. My real problem has over 2 million rows and MapIndex is slower than MapThread.

I think this is a bug. Yes/No?

Win 10 Ent with Mma 11.2


A representative sample of data as requested.

SeedRandom[1122]
raggedDims = RandomInteger[{10, 22}, 150000];
assoc1 = TakeList[
   AssociationThread[{"z"}, #] & /@ 
    RandomReal[{0., 10.}, {Total@raggedDims, 1}], raggedDims];
assoc2 = TakeList[
   AssociationThread[{"a", "b", "c"}, #] & /@ 
    RandomReal[{0., 10.}, {Total@raggedDims, 3}], raggedDims];

and I was trying to do the below and assign it back to another association with Part. Where data is a list of associations.

data[[All, "key"]] = 
    MapThread[
      Association,
      {
       assoc1,
       assoc2
       },
      2];
$\endgroup$
  • 5
    $\begingroup$ Not pretty ... MapThread[MapThread[f, {##}] &, {Range@Range[3], Range@Range[3]}] $\endgroup$ – Szabolcs Feb 19 '18 at 14:13
  • $\begingroup$ @Szabolcs Nice workaround. Would you agree it is bug-like. Thinking of reporting to WRI. $\endgroup$ – Edmund Feb 19 '18 at 14:16
  • 1
    $\begingroup$ @Szabolcs Well, starts to be an issue with Range@Range@Range@3. I suppose Nest can be used to generalize the nested MapThreads. $\endgroup$ – Edmund Feb 19 '18 at 14:19
  • 1
    $\begingroup$ I wouldn't say it is a bug, rather a missing feature. Could you show a few benchmark datasets (with depths and lengths relevant to your actual problem, so that the method can be tailored to it)? $\endgroup$ – Szabolcs Feb 19 '18 at 14:26
  • 2
    $\begingroup$ Also not pretty: MapThread[Thread@*f, {Range@Range@3, Range@Range@3}, 1] $\endgroup$ – user1066 Feb 19 '18 at 14:32
7
$\begingroup$

A quick note about the problem:

While I was trying to write a generalization to my Flatten+Apply approach I faced a problem that I need to now the Depth[array]. Which makes it problematic for e.g. test = {#, Sin@#} &@Range@Range@ because symbolic Sin[1] adds to the depth even though you would not care.

So now you have uneven, with respect to depth, arrays. And ArrayDepth won't help you as it can't deal with ragged arrays.

Maybe that's it, I don't know. Anyway

Specific solution:

test = {#, Sin@N@#} &@Range@Range@3

Apply[
 foo,
 Flatten[test, {{2}, {3}, {1}}],
 {2}
]
{{foo[1, 1]}, {foo[1, 1], foo[2, 2]}, {foo[1, 1], foo[2, 2],   foo[3, 3]}}

General(?) solution

I'm not 100% sure but this looks like a generalization:

test = {#, Sin@N@#} &@Range@Range@Range@3;

MapThreadRagged[f_, arr_List, lvl_Integer
]:= MapThreadRagged[f, arr, lvl, Depth[arr]]

MapThreadRagged[f_, arr_List, lvl_Integer, depth_Integer] := Module[{spec}
  , spec = RotateRight[List /@ Range[depth - 1], lvl];
  Apply[f, Flatten[arr, spec], {lvl}]
  ]


MapThreadRagged[foo, test, 3] // MatrixForm
MapThreadRagged[foo, test, 2] // MatrixForm
MapThreadRagged[foo, test, 1] // MatrixForm

enter image description here

and to workaround a problem mentioned in the first paragraph:

test = {#, Sin@#} &@Range@Range@Range@3;

you can provide the depth by yourself:

MapThreadRagged[foo, test, 3, 5] // MatrixForm
MapThreadRagged[foo, test, 2, 5] // MatrixForm
MapThreadRagged[foo, test, 1, 5] // MatrixForm

enter image description here

$\endgroup$
  • $\begingroup$ I like this one :) $\endgroup$ – Mike Honeychurch Feb 20 '18 at 4:36
  • 1
    $\begingroup$ @MikeHoneychurch thanks, I like to force myself to do something with generalized Flatten from time to time to stay in shape :p $\endgroup$ – Kuba Feb 20 '18 at 7:09
6
$\begingroup$

Two more workarounds:

lists = {Range@Range@3, Range@Range@3};

MapThread[f, #] & /@ Transpose[lists]

Thread[g[f, Transpose[lists]]] /. g -> MapThread
$\endgroup$
5
$\begingroup$

Why not just pad the list and create an extra definition for f?

f[0, 0] = Nothing;
MapThread[f, {PadRight@Range@Range@3, PadRight@Range@Range@3}, 2]

(* {{f[1, 1]}, {f[1, 1], f[2, 2]}, {f[1, 1], f[2, 2], f[3, 3]}} *)

or in case f[0,0] is required to do something then this:

f[Missing[], Missing[]] = Nothing;
MapThread[f, {PadRight[Range@Range@3, {3, 3}, Missing[]], PadRight[Range@Range@3, {3, 3}, Missing[]]}, 2]

(* {{f[1, 1]}, {f[1, 1], f[2, 2]}, {f[1, 1], f[2, 2], f[3, 3]}} *)
$\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.