14
$\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$
6
  • 5
    $\begingroup$ Not pretty ... MapThread[MapThread[f, {##}] &, {Range@Range[3], Range@Range[3]}] $\endgroup$
    – Szabolcs
    Feb 19, 2018 at 14:13
  • $\begingroup$ @Szabolcs Nice workaround. Would you agree it is bug-like. Thinking of reporting to WRI. $\endgroup$
    – Edmund
    Feb 19, 2018 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, 2018 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, 2018 at 14:26
  • 2
    $\begingroup$ Also not pretty: MapThread[Thread@*f, {Range@Range@3, Range@Range@3}, 1] $\endgroup$
    – user1066
    Feb 19, 2018 at 14:32

3 Answers 3

8
$\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$
2
  • $\begingroup$ I like this one :) $\endgroup$ Feb 20, 2018 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, 2018 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$

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