25
$\begingroup$

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.

$\endgroup$
2

6 Answers 6

31
$\begingroup$

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}}]]
$\endgroup$
6
  • 5
    $\begingroup$ it is one of those cool tricks "hidden" in the documentation, i.e. I doubt you'd find it by searching "transpose ragged array." $\endgroup$ Commented Aug 2, 2012 at 6:40
  • $\begingroup$ that's a great solution and it works perfectly. Unfortunately I, by now, dont understand completly how it actually works $\endgroup$
    – RMMA
    Commented Aug 2, 2012 at 7:20
  • $\begingroup$ @rainer I just added an edit that hopefully explains how this works. $\endgroup$ Commented Aug 2, 2012 at 7:33
  • $\begingroup$ thank you! this does explain a lot $\endgroup$
    – RMMA
    Commented Aug 2, 2012 at 7:56
  • $\begingroup$ is it possible to transform this one in order to calculate the mean instead of counting zeros? $\endgroup$
    – RMMA
    Commented Aug 2, 2012 at 12:56
16
$\begingroup$

I propose this:

Total @ PadRight[1 - Unitize[list]]
$\endgroup$
3
  • $\begingroup$ +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) $\endgroup$
    – Rojo
    Commented Aug 2, 2012 at 14:05
  • $\begingroup$ Not general, but slick. +1 $\endgroup$
    – Mr.Wizard
    Commented Aug 2, 2012 at 14:07
  • 8
    $\begingroup$ @Rojo, how appropriate that you had to PadRight your comment :-) $\endgroup$ Commented Aug 3, 2012 at 19:46
11
$\begingroup$

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]

enter image description here

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}

$\endgroup$
2
  • $\begingroup$ I'd do Count[#, 0] & instead of Length /@ (Cases[#, 0] & but there are probably timing differences depending on how big his real list is. $\endgroup$ Commented Aug 2, 2012 at 6:31
  • $\begingroup$ @MikeHoneychurch forgot about Count[#, 0] ;-) $\endgroup$ Commented Aug 2, 2012 at 6:36
8
$\begingroup$

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 *)

raggedMapThread[test, a, 2] // MatrixForm

Mathematica graphics

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ how would you parallelize raggedMapThread? $\endgroup$
    – s0rce
    Commented Nov 30, 2012 at 1:26
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Commented Nov 30, 2012 at 1:48
5
$\begingroup$

Since

enter image description here

and

enter image description here

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
$\endgroup$
1
$\begingroup$

Here's a solution for when you have one list and one scalar. Perhaps it can be adapted to your needs -- maybe I'll come back and edit it tailored to your use-case a little more later, but I think you'll get the point.

EDIT: Just saw that this post was from 2012; maybe I won't rush on tailoring it to your needs, but this post came up near the top in my Google search, so maybe somebody else will get some use out of these...

Method 1

Inner[MyFunction, {d}, {{a, b, c}}]
Inner[MyFunction, {{a, b, c}}\[Transpose], {d}, Reverse]

Output (they're equivalent) [leaving it to the reader to do it in the opposite order]:

{MyFunction[d, a], MyFunction[d, b], MyFunction[d, c]}

Method 2 - might be more adaptable for you:

Outer[MyFunction, {a, b, c}, {d}]

Output:

{{MyFunction[a, d]}, {MyFunction[b, d]}, {MyFunction[c, d]}}

Method 3

Distribute[MyFunction[{a, b, c}, d], List]

Output:

{{MyFunction[a, d]}, {MyFunction[b, d]}, {MyFunction[c, d]}}

Update -- GENERAL-CASE SOLUTIONS:

  • These were also written to work with any combination of lists and scalars...
DistributeOp[distributeOver_] := Function[expr, Distribute[expr, distributeOver]]
EnsureList[expr_] := Flatten[{expr}, 1]

MapThreadRagged1[func_, a_, b_] := Inner[func, {EnsureList[a]}\[Transpose], {EnsureList[b]}]
MapThreadRagged2[func_, a_, b_] := Outer[func, EnsureList[a], EnsureList[b]]
MapThreadRagged3[func_, a_, b_] := DistributeOp[List]@func[EnsureList[a], EnsureList[b]]

Output:

MapThreadRagged1[f, {a, b}, {c, d}]
MapThreadRagged1[f, a, b]
MapThreadRagged2[f, {a, b}, {c, d}]
MapThreadRagged2[f, a, b]
MapThreadRagged3[f, {a, b}, {c, d}]
MapThreadRagged3[f, a, b]

{{f[a, c], f[a, d]}, {f[b, c], f[b, d]}}
{{f[a, b]}}

{{f[a, c], f[a, d]}, {f[b, c], f[b, d]}}
{{f[a, b]}}

{f[a, c], f[a, d], f[b, c], f[b, d]}
{f[a, b]}
$\endgroup$

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.