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 pandas:
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