8
$\begingroup$

I have table like this:

t = {False, False, False, 4.5, 6.789, 1.23, 0.98, False, False}

and I want to know how many False values I have at the beginning and at the end of the table.

I made:

size = Length[t]
t = Cases[t,Except[False]];
size2 = Length[t]
diff = size - size2

But this counts number of all 'False' values inside the table. But I want to know how many is at the beginning and how many at the end (so i my example I have: 3 at the beginning and 2 at the end). How can I solve that?

$\endgroup$
1
  • $\begingroup$ If the list is all False, what you want, the length of the lsit or the numbers at the beginning and at the end separately? (It's an edge-case in which the numbers of False and from the beginning and from the end are each equal to the length of the list. Based on your example, I would assume in the case of all False you want the length and otherwise the sum of the runs. But please clarify.) $\endgroup$
    – Michael E2
    Commented Jan 11, 2014 at 20:17

8 Answers 8

13
$\begingroup$

Here is one way:

before= LengthWhile[t, Not]

(* 3  *)

after = LengthWhile[Reverse[t], Not]

(* 2  *)
$\endgroup$
5
  • $\begingroup$ Very nice solution, I didn't get the idea about reversing the table. Thanks! $\endgroup$
    – Ziva
    Commented Jan 11, 2014 at 11:55
  • $\begingroup$ @Ziva In fact, reversing might not be optimal for long lists, you might be better off explicitly looping from the end then. For longer lists, however, mixing Boolean values with numbers may not be a good practice anyway. $\endgroup$ Commented Jan 11, 2014 at 11:56
  • $\begingroup$ @Ziva Thanks for the accept, but I'd wait for some more answers before accepting one - this way you will encourage more solutions, including perhaps better ones. $\endgroup$ Commented Jan 11, 2014 at 11:57
  • $\begingroup$ +1 but IMHO it would be cleaner to eliminate Function: LengthWhile[t, Not]. $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2014 at 13:45
  • $\begingroup$ @Mr.Wizard Agreed. Edited. $\endgroup$ Commented Jan 11, 2014 at 14:57
10
$\begingroup$
z = Split[t];
Map[Length, {First[z], Last[z]}]

(* {3, 2} *)
$\endgroup$
3
  • 1
    $\begingroup$ I get about a 40% reduction in computation time with SplitBy relative to LengthWhile. $\endgroup$ Commented Jan 11, 2014 at 13:16
  • 2
    $\begingroup$ I don't understand what you intend with SplitBy[t, False]. I believe it is equivalent to Split[t] here. $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2014 at 13:28
  • 1
    $\begingroup$ Not SplitBy, Split. :-) I would write: Length /@ Split[t][[{1, -1}]], Also, note that this method will only work if one is certain that the list begins and ends with False; it cannot handle a "zero count" case. $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2014 at 13:34
4
$\begingroup$
list = {False, False, False, 4.5, 6.789, 1.23, 0.98, False, False};

Using SequenceCount (new in 10.1)

SequenceCount[list, #] & /@ {{False, _}, {_, False}}

{3, 2}

$\endgroup$
2
$\begingroup$

Using SequenceCases:

list = {False, False, False, 4.5, 6.789, 1.23, 0.98, False, False};

SequenceCases[list, s : {False ..} :> Length@s]

(*{3, 2}*)

Or using SequenceSplit and Select:

patt = {b : Longest[False ..]} :> {b};

Length /@ Select[SequenceSplit[list, patt], ! FreeQ[#, False] &]

(*{3, 2}*)
$\endgroup$
2
$\begingroup$

Since @eldo keeps up the wonderful work and @E. Chan-López is following along I am providing an answer the question.

t = {False, False, False, 4.5, 6.789, 1.23, 0.98, False, False};
foreldo[list_] := Module[{adjacency, result},
   adjacency = 
    SparseArray[
      list /. x_ /; x > 0 -> 0 /. x_ /; x < 0 -> 0 /. 
       x_ /; x == 0 -> 0]["AdjacencyLists"];
   result = Length /@ Split[adjacency, #2 == # + 1 &];
   Print[
    "The number of consecutive False elements are --- from start to \
end"];
   Return[result];
   ];

Then, we have

foreldo[t]

returning

for eldo

$\endgroup$
1
$\begingroup$

Gives you the leading and trailing as a list:

LengthWhile[#, ! # &] & /@ {t, t // Reverse}

If you need to assign to symbols, e.g.,

{front, back}=LengthWhile[#, ! # &] & /@ {t, t // Reverse}

Edit: Just saw L.S.'s answer - same idea. As he notes, for huge lists reverse may not be the best way. I find the idiom somelist[[-1;;1;;-1]] a faster way, and depending on what you know about the structure of the data, other ways may be faster.

$\endgroup$
1
$\begingroup$

Here's a rules-based solution as well so that we have one of those too:

t /. {f : False ..., Except[False] .., l : False ...} :> {Length[{f}], Length[{l}]}
$\endgroup$
1
  • 2
    $\begingroup$ I thought of this one too, but unfortunately it is extremely inefficient due to Mathematica's failure to optimize pattern matching. $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2014 at 14:59
1
$\begingroup$

I came up with this and then noticed it is a variation on Anon's approach:

 # /. {___, x:Longest[False ...], __, y:Longest[False ...]} :> {Length[{x}], Length[{y}]} &

The same idea, with a named function:

f[list_] := Length[list /. {___, x : Longest[False ...], __} :> {x}]

Example:

 {f[t], f[Reverse[t]]}

{3, 2}

$\endgroup$
0

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.