find the number of separated pairs in array - python

Good Morning,
I have a numpy array like:
[0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0]
and I would like to find the number of separated pairs of 1.
Three (or more) consecutive 1s count also as pair, i.e.: in this example the returned number should be 3.
What is the best technique to achieve this?
Thanks a lot!

using itertools.groupby,
k hold the unique key 0/1 based list lst below, g hold the correspond group iterator for the unique key k
import itertools
target = 1
lst = [0,1,1,0,1,1,1,0,0,1,0,1,1,0]
pair_count = 0
for k,g in itertools.groupby(lst):
if k==target and len(list(g))>1: # match target and more than 1 count as pair
pair_count += 1
# pair_count = 3

You can easily implement it yourself, see my code below:
l = [0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0]
# Create flag to skip all elements that have >2 1 in sequence
added = False
pairs_counter = 0
for i in range(1, len(l)):
rem = l[i - 1]
if l[i] == 1 and rem == 1 and not added:
added = True
pairs_counter +=1
if l[i] == 0:
added = False
print (pairs_counter)
Complexity of this method is O(n)

I would go for something like this:
a = [0 1 1 0 1 1 1 0 0 1 0 1 1 0]
sum([
a[i-2] == a[i-1] == 1 and a[i] == 0
for i in xrange(2,len(a))
]) + (len(a) > 2 and a[-1] == a[-2] == 1)
It just keeps adding Trues and Falses together. I guess some people will find it ugly, I think it is OK.
It should be noted though, that if the list is really large, this is not a good option, since it creates the list of bools in memory. It would be easy to avoid that.

Use itertools.groupby and sum
sum(1 for target, group_count in itertools.groupby(lst)
if target == 1 and len(list(group_count)) >= 2)

Related

Counting elements inside an array/matrix

I am struggling with what is hopefully a simple problem. Haven't been able to find a clear cut answer online.
The program given, asks for a user input (n) and then produces an n-sized square matrix. The matrix will only be made of 0s and 1s. I am attempting to count the arrays (I have called this x) that contain a number, or those that do not only contain only 0s.
Example output:
n = 3
[0, 0, 0] [1, 0, 0] [0, 1, 0]
In this case, x should = 2.
n = 4
[0, 0, 0, 0] [1, 0, 0, 0] [0, 1, 0, 0] [0, 0, 0, 0]
In this case, x should also be 2.
def xArrayCount(MX):
x = 0
count = 0
for i in MX:
if i in MX[0 + count] == 0:
x += 0
count += 1
else:
x += 1
count += 1
return(x)
Trying to count the number of 0s/1s in each index of the matrix but I am going wrong somewhere, could someone explain how this should work?
(Use of extra python modules is disallowed)
Thank you
You need to count all the lists that contain at least once the number one. To do that you can't use any other module.
def count_none_zero_items(matrix):
count = 0
for row in matrix:
if 1 in row:
count += 1
return count
x = [[0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]]
count_none_zero_items(x)
Also notice that i changed the function name to lower case as this is the convention in python. Read more about it here:
Python3 Conventions
Also it's worth mentioning that in python we call the variable list instead of array.
Those look like tuples, not arrays. I tried this myself and if I change the tuples into nested arrays (adding outer brackets and commas between the single arrays), this function works:
def xArrayCount(MX):
x = 0
count = 0
for i in matrix:
if MX[count][count] == 0:
count += 1
else:
x += 1
count += 1
return x

How to reduce higher values to 1s and sort the ones from right to left

I want to make a python program that quickly reduces a number greater than 1 in an array/list and places it in an empty spot before it. Say we have:
li = [4,1,0,0,0,1,3,0]
we'd get:
rtr = [1,1,0,1,1,1,1,0]
Note how the 4 turns into a 1 because it's already to the left and then the 3 gets divided into 2 positions before the 1 that has already been taken. Can anyone help me with this problem?
You can iterate the list from end to start and keep track of the sum you collect from the values. When you have a non zero sum, take 1 from it to populate the result list, and otherwise put a 0 in the result list.
Here is how that could work:
def spread(lst):
carry = 0
res = []
for i in reversed(lst):
carry += i
res.append(int(carry > 0))
if carry:
carry -= 1
return list(reversed(res))
lst = [4, 1, 0, 0, 0, 1, 3, 0]
print(spread(lst)) # [1, 1, 0, 1, 1, 1, 1, 0]
Using numpy
def fun(l):
s = np.array(l[::-1])
for i in range(len(s)):
if s[i] != 1 and s[i] != 0:
x = s[i+1:]
x[(x == 0).nonzero()[0][:s[i]-1]] = 1
s[i] = 1
return s[::-1].tolist()
print (fun([4,1,0,0,0,1,3,0]))
print (fun([0, 10]))
Output:
[1, 1, 0, 1, 1, 1, 1, 0]
[1, 1]

Compute the length of consecutive true values in a list

Essentially this problem can be split into two parts. I have a set of binary values that indicate whether a given signal is present or not. Given that the each value also corresponds to a unit of time (in this case minutes) I am trying to determine how long the signal exists on average given its occurrence within the overall list of values throughout the period I'm analyzing. For example, if I have the following list:
[0,0,0,1,1,1,0,0,1,0,0,0,1,1,1,1,0]
I can see that the signal occurs 3 separate times for variable lengths of time (i.e. in the first case for 3 minutes). If I want to calculate the average length of time for each occurrence however I need an indication of how many independent instances of the signal exist (i.e. 3). I have tried various index based strategies such as:
arb_ops.index(1)
to find the next occurrence of true values and correspondingly finding the next occurrence of 0 to find the length but am having trouble contextualizing this into a recursive function for the entire array.
You could use itertools.groupby() to group consecutive equal elements. To calculate a group's length convert the iterator to a list and apply len() to it:
>>> from itertools import groupby
>>> lst = [0 ,0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0 ,1, 1, 1, 1, 0]
>>> for k, g in groupby(lst):
... g = list(g)
... print(k, g, len(g))
...
0 [0, 0, 0] 3
1 [1, 1, 1] 3
0 [0, 0] 2
1 [1] 1
0 [0, 0, 0] 3
1 [1, 1, 1, 1] 4
0 [0] 1
Another option may be MaskedArray.count, which counts non-masked elements of an array along a given axis:
import numpy.ma as ma
a = ma.arange(6).reshape((2, 3))
a[1, :] = ma.masked
a
masked_array(data =
[[0 1 2]
[-- -- --]],
mask =
[[False False False]
[ True True True]],
fill_value = 999999)
a.count()
3
You can extend Masked Arrays quite far...
#eugene-yarmash solution with the groupby is decent. However, if you wanted to go with a solution that requires no import, and where you do the grouping yourself --for learning purposes-- you could try this::
>>> l = [0,0,0,1,1,1,0,0,1,0,0,0,1,1,1,1,0]
>>> def size(xs):
... sz = 0
... for x in xs:
... if x == 0 and sz > 0:
... yield sz
... sz = 0
... if x == 1:
... sz += 1
... if sz > 0:
... yield sz
...
>>> list(size(l))
[3, 1, 4]
I think this problem is actually pretty simple--you know you have a new signal if you see a value is 1, and the previous value is 0.
The code I provided is kind of long, but super simple, and done without imports.
signal = [0,0,0,1,1,1,0,0,1,0,0,0,1,1,1,1,0]
def find_number_of_signals(signal):
index = 0
signal_counter = 0
signal_duration = 0
for i in range(len(signal) - 1):
if signal[index] == 1:
signal_duration += 1.0
if signal[index- 1] == 0:
signal_counter += 1.0
index += 1
print signal_counter
print signal_duration
print float(signal_duration / signal_counter)
find_number_of_signals(signal)

Efficient way of re-numbering elements in an array

I am reasonably new to python and am trying to implement a genetic algorithm, but need some assistance with the code for one of the operations.
I have formulated the problem this way:
each individual I is represented by a string of M integers
each element e in I takes a value from 0 to N
every number from 0 - N must appear in I at least once
the value of e is not important, so long as each uniquely valued element takes the same unique value (think of them as class labels)
e is less than or equal to N
N can be different for each I
after applying the crossover operation i can potentially generate children which violate one or more of these constraints, so i need to find a way to re-number the elements so that they retain their properties, but fit with the constraints.
for example:
parent_1 (N=5): [1 3 5 4 2 1|0 0 5 2]
parent_2 (N=3): [2 0 1 3 0 1|0 2 1 3]
*** crossover applied at "|" ***
child_1: [1 3 5 4 2 1 0 2 1 3]
child_2: [2 0 1 3 0 1 0 0 5 2]
child_1 obviously still satisfies all of the constraints, as N = 5 and all values 0-5 appear at least once in the array.
The problem lies with child 2 - if we use the max(child_2) way of calculating N we get a value of 5, but if we count the number of unique values then N = 4, which is what the value for N should be. What I am asking (in a very long winded way, granted) is what is a good, pythonic way of doing this:
child_2: [2 0 1 3 0 1 0 0 5 2]
*** some python magic ***
child_2': [2 0 1 3 0 1 0 0 4 2]
*or*
child_2'': [0 1 2 3 1 2 1 1 4 0]
child_2'' is there to illustrate that the values themselves dont matter, so long as each element of a unique value maps to the same value, the constraints are satisfied.
here is what i have tried so far:
value_map = []
for el in child:
if el not in value_map:
value_map.append(el)
for ii in range(0,len(child)):
child[ii] = value_map.index(child[ii])
this approach works and returns a result similar to child_2'', but i can't imagine that it is very efficient in the way it iterates over the string twice, so i was wondering if anyone has any suggestions of how to make it better.
thanks, and sorry for such a long post for such a simple question!
You will need to iterates the list more than once, I don't think there's any way around this. After all, you first have to determine the number of different elements (first pass) before you can start changing elements (second pass). Note, however, that depending on the number of different elements you might have up to O(n^2) due to the repetitive calls to index and not in, which have O(n) on a list.
Alternatively, you could use a dict instead of a list for your value_map. A dictionary has much faster lookup than a list, so this way, the complexity should indeed be on the order of O(n). You can do this using (1) a dictionary comprehension to determine the mapping of old to new values, and (2) a list comprehension for creating the updated child.
value_map = {el: i for i, el in enumerate(set(child))}
child2 = [value_map[el] for el in child]
Or change the child in-place using a for loop.
for i, el in enumerate(child):
child[i] = value_map[el]
You can do it with a single loop like this:
value_map = []
result = []
for el in child:
if el not in value_map:
value_map.append(el)
result.append(value_map.index(el))
One solution I can think of is:
Determine the value of N and determine unused integers. (this forces you to iterate over the array once)
Go through the array and each time you meet a number superior to N, map it to an unused integer.
This forces you to go through the arrays twice, but it should be faster than your example (that forces you to go through the value_map at each element of the array at each iteration)
child = [2, 0, 1, 3, 0, 1, 0, 0, 5, 2]
used = set(child)
N = len(used) - 1
unused = set(xrange(N+1)) - used
value_map = dict()
for i, e in enumerate(child):
if e <= N:
continue
if e not in value_map:
value_map[e] = unused.pop()
child[i] = value_map[e]
print child # [2, 0, 1, 3, 0, 1, 0, 0, 4, 2]
I like #Selçuk Cihan answer. It can also be done in place.
>>> child = [2, 0, 1, 3, 0, 1, 0, 0, 5, 2]
>>>
>>> value_map = []
>>> for i in range(len(child)):
... el = child[i]
... if el not in value_map:
... value_map.append(el)
... child[i] = value_map.index(el)
...
>>> child
[0, 1, 2, 3, 1, 2, 1, 1, 4, 0]
I believe that this works, although I didn't test it for more than the single case that is given in the question.
The only thing that bothers me is that value_map appears three times in the code...
def renumber(individual):
"""
>>> renumber([2, 0, 1, 3, 0, 1, 0, 0, 4, 2])
[0, 1, 2, 3, 1, 2, 1, 1, 4, 0]
"""
value_map = {}
return [value_map.setdefault(e, len(value_map)) for e in individual]
Here is a fast solution, which iterates the list only once.
a = [2, 0, 1, 3, 0, 1, 0, 0, 5, 2]
b = [-1]*len(a)
j = 0
for i in range(len(a)):
if b[a[i]] == -1:
b[a[i]] = j
a[i] = j
j += 1
else:
a[i] = b[a[i]]
print(a) # [0, 1, 2, 3, 1, 2, 1, 1, 4, 0]

merge values in list

I have a list in which I have to merge values like this:
list: [1, 1, 2, 1, 1, 0]
result: [2, 2, 2, 0, 0, 0]
It merges from left to right, and only merges 2 of the same numbers per time.
the first two values are added up (1 + 1)
the 2 is shifted to the left
the other two values are added up as well (1 + 1) and shifted
the list is be padded from the right with zero's so the length of the list stays the same
(edit) any zero's in the list will be at the end, not in the middle
I'm quite new to Python, and don't have a clear idea on how to approach this. Any help is appreciated.
How about something simple and straight-forward:
def merge_pairs(a):
out = []
i = 0
while i < len(a):
if i < len(a) - 1 and a[i] == a[i + 1]:
out.append(a[i] + a[i + 1])
i += 2
else:
out.append(a[i])
i += 1
out += [0] * (len(a) - len(out))
return out
Not 100% sure this is what you require, but perhaps it can be a start.
The single test you provided seems to work:
>>> merge_pairs([1, 1, 2, 1, 1, 0])
[2, 2, 2, 0, 0, 0]

Categories