I am working on a project and am getting stuck on the following bit : I have a boolean array and I would like to determine if a specific pattern is present in this array.
For example, let's imagine I have this boolean array : [True, False, False, True, False, False, True, False, True, True, True] and I would like to know if it contains the following pattern [False, False, True, False].
My first idea was to loop through all values of the boolean array, define a variable that counts the number of consecutive "False" then loops again to find if "True" is following that sequence, etc... But it seems a bit tedious and I imagine there is a better way to do this...
Any advice would be very welcomed !
Thank you
One way would be to convert the array to a string of f and t characters, then use string search to find the desired pattern. For example:
def bool_list_to_str(bool_list):
return ''.join('ft'[i] for i in bool_list)
You can call this function to convert both your array and your search pattern to strings. Then you can use s.find(p) to search for pattern p in string s.
Note that this uses one character per bool value, so the string will have the same length as the bool list, and the indices will match. It also uses a minimal amount of string memory.
You can join them as string and check for substring:
super_list = [True, False, False, True, False, False, True, False, True, True, True]
sub_list = [False, False, True, False]
super_list_as_str = ''.join(map(str, super_list))
sub_list_as_str = ''.join(map(str, sub_list))
if sub_list_as_str in super_list_as_str:
print('Sub string')
I am starting with programming then my code is not optimize. But it's very easy to understand:
1.- Create a list with all the combinations of the array with lenght n
2.- Make a comparision of the pattern with the list you have create.
# a is the array you want to check with
a = [True, False, False, True, False, False, True, False, True, True, True]
# b is the array you want to check
b = [False, True, False, False]
# patterns is the list with all the patterns in the array
patterns = []
for i in range(len(a)-(len(b)-1)):
patterns.append((a[i:(len(b)+i)]))
# Check if b is in the list you have create.
b in patterns
Related
Given a random list length, how can I efficiently get all possible sequences of boolean values, except strings that are all True or all False?
For instance, given the number 3 it should return something like the following.
[True, False, False],
[True, True, False],
[True, False, True],
[False, True, False],
[False, True, True],
[False, False, True],
Is there already a known function that does this?
The order that it returns the sequences in is not important. I mainly just need a number of how many sequences are possible for a given list length.
This is mostly a maths question, unless you need the sequences themselves. If you do, there is a neat python solution:
from itertools import product
[seq for seq in product((True, False), repeat=3)][1:-1]
The list comprehension will contain all possible sequences, but we don't want (True, True, True) and (False, False, False). Conveniently, these will be the first and last element respectively, so we can simply discard them, using slicing from 1 to -1.
For sequences with different lengths, just change the "repeat" optional argument of the itertools.product function.
You don't need a function to determine this. Simple math will do the trick.
2**n - 2
2 because there are only two options (True/False)
n is your list length
-2 because you want to exclude the all True and all False results
This is more of a maths question, but here goes:
The number of total options is equal to the multiplication of the number of options per position, so, if you receive 3 as input:
index[0] could be true or false - 2
index[1] could be true or false - 2
index[2] could be true or false - 2
index has a total of 6 options.
For example:
boolarr = [True, True, False, False, True, True]
atleastonetrue = bool(sum(boolarr))
Is there a more elegant or conventional way of doing this?
Following the comments, any(arr) works.
How do I compute the inverse of what is described here: Getting indices of True values in a boolean list ?
That above link always comes up when I try searching for "how to obtain the true values in a boolean list from integer indices," but it gives me the indices from the true values in a boolean list, which is the inverse of what I want...
For example, from:
t = [4, 5, 7]
count = 16
I want to obtain:
[False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]
The values are all 0 indexed, as expected with Python.
I'm guessing that my question is a duplicate, but it's so annoying to not be able to find what I'm looking for every time I try to remember how to do this operation, I decided to ask a new question so my Google search will hopefully bring up this post next time.
You can use a list comprehension. I recommend you turn t into a set for O(1) lookup:
t_set = set(t)
res = [i in t_set for i in range(count)]
Use a list comprehension with conditions:
print([True if i in t else False for i in range(count)])
Shorter:
print([i in t else False for i in range(count)])
How about this:
In [6]: holderplace =[False for i in range(count)]
In [7]: for i in t:
...: holderplace[i-1]=True
...:
In [8]: holderplace
Out[8]:
[False,
False,
False,
True,
True,
False,
True,
False,
False,
False,
False,
False,
False,
False,
False,
False]
In [9]:
You could also try using map():
list(map(lambda x: x in t, range(count)))
# [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]
It might also be worth converting t to a set, since lookup is O(1) instead of O(N).
You could also use __contains__():
list(map(t.__contains__, range(count)))
Given a numpy array:
x = np.array([False, True, True, False, False, False, False, False, True, False])
How do I find the number of times the values transitions from False to True?
For the above example, the answer would be 2. I don't want to include transitions from True to False in the count.
From the answers to How do I identify sequences of values in a boolean array?, the following produces the indices at which the values are about to change, which is not what I want as this includes True-False transitions.
np.argwhere(np.diff(x)).squeeze()
# [0 2 7 8]
I know that this can be done by looping through the array, however I was wondering if there was a faster way to do this?
Get one-off slices - x[:-1] (starting from the first elem and ending in second last elem) and x[1:] (starting from the second elem and going on until the end), then look for the first slice being lesser than the second one, i.e. catch the pattern of [False, True] and finally get the count with ndarray.sum() or np.count_nonzero() -
(x[:-1] < x[1:]).sum()
np.count_nonzero(x[:-1] < x[1:])
Another way would be to look for the first slice being False and the second one as True, the idea again being to catch that pattern of [False, True] -
(~x[:-1] & x[1:]).sum()
np.count_nonzero(~x[:-1] & x[1:])
I kind of like to use numpy method "roll" for this kind of problems...
"roll" rotates the array to left some step length : (-1,-2,...) or to right (1,2,...)
import numpy as np
np.roll(x,-1)
...this will give x but shifted one step to the left:
array([ True, True, False, False, False, False, False, True, False, False],
dtype=bool)
A False followed by a True can then be expressed as:
~x & np.roll(x,-1)
array([ True, False, False, False, False, False, False, True, False, False],
dtype=bool)
I have a python list of ints, a. I also have another list, b, which is a tuple of 2 values (c, d). I need to see if any elements of a have values that are between any of the tuple elements of b.
I think there is a way to do this using map(), but I can't figure out how to pass in the values of my tuple list.
For example, my data structure looks like:
a = [1, 2, 3, 4, 5, 6, 7]
b = [(12,14), (54, 78), (2,3), (9,11)]
I am trying to find out if any of the elements in a have values between any of the tuple elements of b. In the above case, 2 and 3 (from a) are inside (inclusive) of the tuple (2,3) in b. So my final answer would be True.
Does anyone have any idea how to do this in a performat way? Right now, I am looping through each element of a and then looping through each element of b. This is ok for small amounts of data, but my arrays are quite large and this step takes way to long.
dId you want this?
[c in range(k[0], k[1]+1) for c in a for k in b]
returns:
[False, False, False, False, # 1 is in any ofthe tuple range in b?
False, False, True, False, # 2 is in any of the tuple range in b?
False, False, True, False, # etc. etc....
False, False, False, False,
False, False, False, False,
False, False, False, False,
False, False, False, False] # searches for each element in a within the range specified by the tuple elements in b
If you wanted to do something else like check every element in a using each element of b first then swap the order of the fors:
[c in range(k[0], k[1]+1) for k in b for c in a]
returns:
[False, False, False, False, False, False, False, // is b[0] range covers 1-7?
False, False, False, False, False, False, False, // etc. etc.
False, True, True, False, False, False, False,
False, False, False, False, False, False, False]
I assumed this is what you didn't want....but thought I would post it to you anyway
If the (c, d) values restricted to a certain range (say 0-100), you could calculate a boolean array of allowed values, and compare a against that with a simple index lookup.
If you the values are not restricted or the range would be too large, put the values of b into a sorted data structure. Then you can look up a against that quickly, without needing to go through the whole list every time. While building this lookup data structure, you would have to look out for overlapping ranges, and merge them.
If you sort first you can avoid checking every value in a against every tuple in b. The tuples that have already been checked against lower values of a can be discarded, which will make the check much faster.
def check_value_in_ranges(a, b):
a = sorted(set(a))
b = sorted(set(b), reverse=True)
lower, upper = b.pop()
for value in a:
while value >= lower:
if value <= upper:
return True
elif not b:
return False # no tuples left to check against
lower, upper = b.pop()
return False # no values of a left to check
I think this works whether the tuples are overlapping or not - check here.