Home > OS >  Count the maximum number of 0s between two 1s in list python
Count the maximum number of 0s between two 1s in list python

Time:11-10

I want to count the 0s between two 1s from a list. For example:

l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]

I want the output to be [4,2,1]. How can I do that in python?

CodePudding user response:

A slightly different way using itertools.groupby - using the fact that any entries beyond the first and last 1 is irrelevant to us

from itertools import groupby

first_one = l.index(1) # index of the first "1"
last_one = len(l) - l[::-1].index(1) - 1 # index of the last "1"
out = [len(list(g)) for k, g in groupby(l[first_one:last_one], key=lambda x: x == 0) if k]

Output

[4, 2, 1]

CodePudding user response:

One option using :

l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]

s = pd.Series(l)

out = (s[s.eq(0)&s.cummax()&s.loc[::-1].cummax()]
       .rsub(1).groupby(s.ne(0).cumsum()).sum()
       .tolist()
      )

With pure python and itertools.groupby:

from itertools import groupby

l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]

out = []
start = False
for k, g in groupby(l):
    if k == 1:
        if start:
            out.append(count)
        start = True
    else:
        count = len(list(g))

output: [4, 2, 1]

CodePudding user response:

An old-school answer.

def count(l: list[int]) -> list[int]:
    res = []
    counter = 0
    lastx = l[0]
    for x in l[1:]:
        rising = (x-lastx) > 0
        if rising and counter != 0:
            res.append(counter)
        counter = counter 1 if x==0 else 0
        lastx = x
    return res

count([0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0])  # [4, 2, 1]

CodePudding user response:

My one-liner

Just for fun (not that I encourage doing so in real project), here is a one liner (but a big line), using almost all iterators in itertools (well, not nearly, in reality. There are really lot of them)

(y for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)

It's an iterator, and nowhere in the process do I build any list. So it would work f l was itself an iterator giving billions of 1 and 0, without using any memory

Explanation

itertools.groupby(l)

is an iterator giving subiterators for each new value of l. So

for v,it in itertools.groupby(l):
    for x in it:
        print(x)

Just prints all elements of l. But with 9 iterations for x in it, one in which x is 1 time 0, then one in which x is 1 time 1, then one in which x is 4 times 0, then etc.

If y is an iterator, then sum(1 for _ in y) is the number of iterations.

So

((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))

iterates pairs (value0or1, numberOfSuchInGroup), with alternating value0or1

For example

list((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))

here is

[(0, 1), (1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2), (0, 1)]

If want to drop the first pair, at least if it is a group of 0, since leading 0 does not count. Plus, I want to play with another iterator, which is dropwhile. So

itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))

is the same iterator as before. But without the first pair if it is group of 0

list(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))))

is

[(1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2), (0, 1)]

I also want do drop the last pair (at least if it is a group of 0, but it doesn't hurt if I drop it also if it is a group of 1). And I haven't played with pairwise yet. Which iterates through pairs of subsequent elemnents

list(itertools.pairwise(range(5)))

is

((0,1),(1,2),(2,3),(3,4))

for example

Here, I use it for a very silly reason: just to drop the last item, since of course, there is one less item in pairwise iteration. In my last example, we have 4 items

list(x for x,y in itertools.pairwise(range(5)))

is

[0,1,2,3]

So, strange usage of pairwise, but it drops the last iteration used that way.

So in our case

((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))))

is the same iterator as before, but without the last pair

list((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))))

is

[(1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2)]

Now that we have only groups of 0 that are valid, we can filter out the 1s

list((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)

is

[(0, 4), (0, 2), (0, 1)]

Plus, we don't need the 0s because at this stage they are all 0 anyway. So, keep just y not (x,y)

list(y for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)

Is

[4,2,1]

CodePudding user response:

How about this, explanation is all in the code:

l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]
output = []

for i in range(len(l)): #this goes through every index in l (1,2,3,...15, 16)
  if l[i] == 1: #if in the list at the current index is a 1 it
    zeros = 0 #sets zeros to 0
    while l[i 1 zeros] == 0: #and starts a while loop as long as the current index 1 'the amount of zeros after the last number 1' is a zero. (so it stops when it reaches another 1)
      zeros  = 1 # because the while loop still runs it adds another 0
      if i 1 zeros == len(l): #the current index   1   'the amount of zeros' = the length of our list
        zeros = 0 # it sets zeros back to 0 so the program doesn't add them to the output (else the output would be [4, 2, 1, 1])
        break #breaks out of the loop
    if zeros > 0: #if the zeros counted between two 1s are more then 0:
      output.append(zeros) # it adds them to our final output
    
print(output) #prints [4, 2, 1] to the terminal
  • Related