# MapThread over sublists of different length

I have a list which looks like this: l={{1,0,3,4},{0,2},{0,0,1,3},{1,2,0}}. Now I would like to count how many 0s the sublists contain in the first, second,... slot. The result for this example should be: {2,2,1,0}. Since the sublists do not have the same length MapThread does not work.

I would be grateful for a solution.

-
A related question. – J. M. Aug 2 '12 at 7:23

You can use Flatten to transpose a ragged array:

list = {{1, 0, 3, 4}, {0, 2}, {0, 0, 1, 3}, {1, 2, 0}}

Count[#, 0] & /@ Flatten[list, {{2}, {1}}]
(* {2, 2, 1, 0} *)


Edit

Step one is to transpose your list but in this case the list is ragged so Tranpose doesn't work:

Transpose[list]


However Flatten can transpose a ragged list (type Flatten in the documentation center and then go to "Applications"):

Flatten[list, {{2}, {1}}]
(* {{1, 0, 0, 1}, {0, 2, 0, 2}, {3, 1, 0}, {4, 3}} *)


Now that the list is transposed you can count the number of zeros, this is done by mapping the transposed list onto Count

Map[Count[#, 0] &, Flatten[list, {{2}, {1}}]]

-
+1 Neat indeed ;-) – Vitaliy Kaurov Aug 2 '12 at 6:36
it is one of those cool tricks "hidden" in the documentation, i.e. I doubt you'd find it by searching "transpose ragged array." – Mike Honeychurch Aug 2 '12 at 6:40
that's a great solution and it works perfectly. Unfortunately I, by now, dont understand completly how it actually works – RMMA Aug 2 '12 at 7:20
@rainer I just added an edit that hopefully explains how this works. – Mike Honeychurch Aug 2 '12 at 7:33
thank you! this does explain a lot – RMMA Aug 2 '12 at 7:56

Say you have

l = {{1, 0, 3, 4}, {0, 2}, {0, 0, 1, 3}, {1, 2, 0}, {1, 1, 1, 1, 0}};


There is a bit different approach to your function:

Sort@Tally@Position[l, 0][[All, 2]]


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

which is compressed from of your information. It gives you slot index and number of 0s there. If slot has no zeros it is not mentioned. If you have a lot of zero-less slots such format is much shorter.

Grid[{{"slot", "zeros"}}~Join~%, Frame -> All]


And here is clunky exercise in padding arrays (with help of Mike's comment) :

zcount[l_List] := With[{m = Max[Length /@ l]}, (Count[#, 0] & /@
Transpose[PadRight[#, m, None] & /@ l])]


The usage:

zcount[l]


{2, 2, 1, 0, 1}

-
I'd do Count[#, 0] & instead of Length /@ (Cases[#, 0] & but there are probably timing differences depending on how big his real list is. – Mike Honeychurch Aug 2 '12 at 6:31
@MikeHoneychurch forgot about Count[#, 0] ;-) – Vitaliy Kaurov Aug 2 '12 at 6:36

Since

and

the following variations also work:

list = {{1, 0, 3, 4}, {0, 2}, {0, 0, 1, 3}, {1, 2, 0}, {1, 1, 1, 1, 0}};
Count[#, 0] & /@ Transpose@PadRight[list, Automatic, "x"]
(* or *)
Tr /@ Transpose@PadRight[Map[Boole[# == 0] &, list, {-1}]]
(* or *)
Plus @@ PadRight[Boole[# == 0] & /@ # & /@ list]
(* => {2,2,1,0,1} *)


EDIT: Few more ways:

Rest@Total@BinCounts@Position[list, 0]
Count[Position[list, 0], {_, #}] & /@ Range@Length@list
Length@Position[Position[list, 0], {_, #}] & /@ Range@Length@list

-

I propose this:

Total @ PadRight[1 - Unitize[list]]

-
+1 great one... Don't pay attention to the ellipsis. The comment just needed 3 extra characters to not be too short... (I was referring to the first ellipsis, not this last one) – Rojo Aug 2 '12 at 14:05
Not general, but slick. +1 – Mr.Wizard Aug 2 '12 at 14:07
+1 ! How did you find it ?? :) – Dr. belisarius Aug 2 '12 at 14:22
@Rojo, how appropriate that you had to PadRight your comment :-) – Simon Woods Aug 3 '12 at 19:46

Since MapThread accepts a level specification, I think our ragged MapThread function should too.

raggedMapThread[f_, expr_, level_Integer: 1] :=
Apply[f, Flatten[expr, List /@ Range[2, level + 1]], {level}]


To solve the specific case posed in the question:

raggedMapThread[
Count[{##}, 0] &,
{{1, 0, 3, 4}, {0, 2}, {0, 0, 1, 3}, {1, 2, 0}}
]


{2, 2, 1, 0}

Extended to level 2 with an example function test:

a = {{{67, 47}, {5, 99}, {70, 44}, {9}},
{{75, 70}, {61}, {16, 23}, {50, 80}},
{{87, 11}, {10}, {29, 16}}};

PadRight[a, Automatic, ""] // MatrixForm  (* for illustration *)


@s0rce since there is no "ParallelApply" (at least not in v7) you would need to use something like ParallelMap: raggedMapThread[f_, expr_, level_Integer: 1] := ParallelMap[Function[, f @@ #, HoldAll], Flatten[expr, List /@ Range[2, level + 1]], {level}] the HoldAll function might be replaced with f @@ # & if you are certain of no surprise evaluations. – Mr.Wizard Nov 30 '12 at 1:48