Hi guys i have this problem:
I have a list with number
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
Every time i found 0 i need to count it and know his position, but when the 0 is consecutive it count as one, for example:
Position start 5, Position end 8, Count 1
Position start 12, Position end 12, Count 2
Position start 14, Position end 14, Count 3
Position start 26, Position end 31, Count 4
This can be solved like this
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
last_was_0=False
zero_clusters=[]
for i in range(len(a)):
b=a[i]
if b==0:
if last_was_0:
zero_clusters[-1][-1]+=1
else:
zero_clusters.append([i, i])
last_was_0 = b==0
for i in range(len(zero_clusters)):
print(f'Position start {zero_clusters[i][0]}, Position end {zero_clusters[i][1]}, Count {i+1}')
but if i want to try it on two list? So doing the same thing that i do with one list but having two, and checking if the current index(the same for the two list) if are both 0
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
b= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
How can I achive the result?
I have already tried like this but it gives me as result only the first iteraction
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
b= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
last_was_0=False
zero_clusters=[]
for i in range(len(a)):
b=a[i]
c=b[i]
if b==0 and c==0:
if last_was_0:
zero_clusters[-1][-1]+=1
else:
zero_clusters.append([i, i])
last_was_0 = b==0
for i in range(len(zero_clusters)):
print(f'Position start {zero_clusters[i][0]}, Position end {zero_clusters[i][1]}, Count {i+1}')
Can someone help me achieve the same result of one list but with two??
You can iterate over the lists together uzing zip, and then use itertools groupby to group consecutive values.
Looping over that and storing the indexes where the key of the group is (0,0) will get you the result you want.
from itertools import groupby
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
b= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
zero_cluster = []
i = 0
for g, d in groupby(zip(a,b)):
data = list(d)
if g!=(0,0):
i+=len(data)
else:
zero_cluster.append((i, i+len(data)-1))
i+=len(data)
for i, cluster in enumerate(zero_cluster):
print(f'Position start {cluster[0]}, Position end {cluster[1]}, Count {i+1}')
Output
Position start 5, Position end 8, Count 1
Position start 12, Position end 12, Count 2
Position start 14, Position end 14, Count 3
Position start 16, Position end 16, Count 4
Position start 28, Position end 33, Count 5
Position start 36, Position end 36, Count 6
Position start 38, Position end 38, Count 7
Position start 40, Position end 40, Count 8
Also it might be helpful to understand what itertools groupby is doing by looking at this:
for g, d in groupby(zip(a,b)):
print(g,list(d))
Prints
(1, 1) [(1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]
(0, 0) [(0, 0), (0, 0), (0, 0), (0, 0)]
(1, 1) [(1, 1), (1, 1), (1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]
(0, 0) [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]
(1, 1) [(1, 1), (1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1)]
(0, 0) [(0, 0)]
(1, 1) [(1, 1)]
a numpy version
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
import numpy as np
def get_clusters(lst):
arr=np.array(lst)
diff=arr[:-1]-arr[1:]
starts = np.where(diff==1)
ends = np.where(diff==-1)
indices = np.vstack((starts[0]+1, ends[0])).T
return indices, diff[diff==1].size
print(get_clusters(a))
output
(array([[ 5, 8],
[12, 12],
[14, 14],
[16, 16],
[28, 33],
[36, 36],
[38, 38],
[40, 40]]), 8)
zip() is your friend when you need to compare elements of a list with it's predecessor or successor.
So you can use zip to get the starting index of each cluster of 1s and 0s. Then use zip again to combine these indexes into ranges of positions, selecting only the ranges that contain zeros.
a= [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
breaks = [i for i,(n,m) in enumerate(zip(a,a[1:]),1) if n!=m ]
positions = [ (s,e-1) for s,e in zip([0]+breaks,breaks+[len(a)]) if not a[s] ]
count = sum(e-s+1 for s,e in positions)
print(positions)
[(5, 8), (12, 12), (14, 14), (16, 16), (28, 33), (36, 36), (38, 38), (40, 40)]
print(count)
16
For two lists, you can combine them (using zip again) and apply the same solution:
x= [1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
y= [1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
a = [ paired != (0,0) for paired in zip(x,y) ]
breaks = [i for i,(n,m) in enumerate(zip(a,a[1:]),1) if n!=m ]
positions = [ (s,e-1) for s,e in zip([0]+breaks,breaks+[len(a)]) if not a[s] ]
count = sum(e-s+1 for s,e in positions)
print(positions)
[(5, 8), (12, 12), (14, 14), (16, 16), (28, 33), (36, 36), (38, 38), (40, 40)]
print(count)
16
If you're okay with using a library, this can be somewhat shortened using groupby from itertools:
from itertools import groupby
positions = [ (lg[0],lg[-1])
for bit,g in groupby(range(len(a)),lambda i:a[i]) if not bit
for lg in [list(g)]]
count = sum(e-s+1 for s,e in positions)
Related
I need to find a solution for the below problem in Python3. I tried itertools.combinations but not clear on how to do it.
Prepare a 7-digit number that sums to 5. Each digit can be between 0-4 only. Also, there can be repetitions. Valid example numbers are -
[ [2,1,1,0,0,1,0], [3,0,1,0,0,1,0], [0,0,0,4,0,0,1], [1,0,0,3,0,1,0], [1,1,1,1,0,1,0], ...... ]
As you can see, numbers may appear more than once in this list.
How can I create a list of all combinations meeting the criteria above?
You can get all that sum to 5 with:
list(p for p in itertools.product(range(5),repeat = 7) if sum(p) == 5)
This yields 455 solutions.
This function will find every combination, with repeated combinations, that sum to N:
from itertools import product
from typing import List, Tuple
def perm_n_digit_total(n_digits, total, choices) -> List[Tuple]:
return list(filter(
lambda x: sum(x) == total,
product(choices, repeat=n_digits)
))
Example:
perm_n_digit_total(3, 1, range(4))
Out[43]: [(0, 0, 1), (0, 1, 0), (1, 0, 0)]
perm_n_digit_total(7, 5, range(4))[::50]
Out[49]:
[(0, 0, 0, 0, 0, 0, 5),
(0, 0, 0, 3, 1, 1, 0),
(0, 0, 2, 0, 3, 0, 0),
(0, 1, 0, 1, 3, 0, 0),
(0, 2, 0, 0, 1, 0, 2),
(0, 4, 1, 0, 0, 0, 0),
(1, 0, 1, 1, 1, 0, 1),
(1, 1, 1, 1, 1, 0, 0),
(2, 0, 1, 0, 0, 2, 0),
(3, 1, 0, 0, 0, 1, 0)]
Here's an itertools'less recursive solution.
def find_solutions(target, numbers, depth, potential_solution=[]):
if depth == 0:
if sum(potential_solution) == target:
print(potential_solution)
return
current_sum = sum(potential_solution)
for n in numbers:
new_sum = current_sum + n
if new_sum > target:
continue
find_solutions(target, numbers, depth - 1, potential_solution + [n])
find_solutions(target=5, numbers=[0,1,2,3,4], depth=7)
Output
[0, 0, 0, 0, 0, 1, 4]
[0, 0, 0, 0, 0, 2, 3]
[0, 0, 0, 0, 0, 3, 2]
[0, 0, 0, 0, 0, 4, 1]
[0, 0, 0, 0, 1, 0, 4]
[0, 0, 0, 0, 1, 1, 3]
...
[3, 1, 1, 0, 0, 0, 0]
[3, 2, 0, 0, 0, 0, 0]
[4, 0, 0, 0, 0, 0, 1]
[4, 0, 0, 0, 0, 1, 0]
[4, 0, 0, 0, 1, 0, 0]
[4, 0, 0, 1, 0, 0, 0]
[4, 0, 1, 0, 0, 0, 0]
[4, 1, 0, 0, 0, 0, 0]
If I got it, you need something like this:
import itertools
value = [0, 1, 2, 3, 4]
p = itertools.product(value, repeat=7)
for j in list(p):
print(j)
As each digit can only take 5 unique values - you would require itertools.combinations_with_replacement -
from itertools import combinations_with_replacement
zero_four = list(range(5))
for c in combinations_with_replacement(zero_four, 7):
if sum(c) == 5:
print(c)
This will give you all possible combinations that sum to 5 but not all the permutations -
Output
(0, 0, 0, 0, 0, 1, 4)
(0, 0, 0, 0, 0, 2, 3)
(0, 0, 0, 0, 1, 1, 3)
(0, 0, 0, 0, 1, 2, 2)
(0, 0, 0, 1, 1, 1, 2)
(0, 0, 1, 1, 1, 1, 1)
To get all permutations - you can use the itertools.permutations but since your output can have repeated elements, you will need to use a set to retain only unique permutations -
for c in combinations_with_replacement(zero_four, 7):
if sum(c) == 5:
print(set(permutations(c)))
So the question revolve around character segmentation. My problem is the following:
I want to segment characters, based on y-axis pixel numbers, following this ( in python) : source
What i already done to get here:
read image io.imread
swap axis np.swapaxes
sum the numbers of each column (now row) - > got y array
I got to the point where i have two arrays (both of them are exactly what I use);
x = [94, 72, 2, 2, 1, 66, 1, 13, 1, 16, 1, 8, 1, 5, 1, 47, 1, 1, 1, 3, 1, 17, 14, 87, 100]
y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
y is the thresholded binary array of the y-axis, (0 if pixel count < 1275, 1 otherwise)
x is the itertools groupby version of the y array.
I have the avarege distance of the letters too, so i know which are the wrongly segmented parts. (according to the x, the avg is 28.)
And this is the image i would like to segment, it has 4 letters, "a","l","m","a":
picture which i would like to segment
So in theory, if i could somehow merge the parts where the number of the ones are lower than the avg, and turn the "separating" zeros to ones, i should get a list which is as long as the width of the image, and has zeros only where it should have.
If i use cv.line on the y array, it indeed does segment the characters, drawing a red line where the array is 0, but it oversegments it.
oversegmented image
What i would like to do is "modify" or just re-do the y array, based on the x.
I tried a lot of methods, but i just cant find the algorithm to go over the x, find the wrong values, delete the zeros inbetween, and modify a list according to that.
My best shot is this easy, nothing-like-my-original-idea piece:
num = 0
betterarray = []
for i in range(len(y)):
if( num == 1 and y[i] == 0 and y[i+1] == 1):
betterarray.append(1)
else :
betterarray.append(y[i])
num = y[i]
It does deletes the (most of the time) one column only bad segmentations, but as I guessed, it does delete some good segmentations aswell.
You should identify the wrongly segmented letters by comparing your segments to the peak segment average and modifying the x array by combining any peak segments that are smaller than the average.
def locate_oversegmentation(array, mask, avg):
length = len(array)
for i in range(length):
// less than average peak
if (mask[i]==1 and array[i]<=avg):
if (i-2>=0):
// previous peak is less than avg
if (array[i-2]<=avg):
mask[i-1] = 1
if (i+2<=length):
// next peak is less than avg
if (array[i+2]<=avg):
mask[i+1] = 1
return mask
This function takes in array x and a compact version of array y by grouping consecutive 0's and 1's. compact_y = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] It will return a new array changing 0's between below avg peaks to 1's. The output array is a guide to combining peaks in array x.
Example:
x = [94, 72, 2, 2, 1, 66, 1, 13, 1, 16, 1, 8, 1, 5, 1, 47, 1, 1, 1, 3, 1, 17, 14, 87, 100]
compact_y = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
avg = 28
guide = locate_oversegmentation(x, compact_y, avg)
>> guide = [0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,0]
Apply the guide on array x by adding consecutive 1's together in array x.
I want to place three numbers [0,1,-1] at 6 positions. It should give 3^6 = 729 combinations. The code should return something like:
(0,0,0,0,0,0)
(0,0,0,0,0,1)
(0,0,0,0,0,-1)
(0,0,0,0,1,1)
(0,0,0,0,1,-1)
.
.
(-1,-1,-1,-1,-1,0)
(-1,-1,-1,-1,-1,-1)
I tried using "itertools.permutations", but am confused about how to incorporate 0,1,-1 into it.
Check out itertools.product(*iterables, repeat=1) here.
Use like:
> product([-1, 0, 1], repeat=6)
[...]
(1, 1, 1, 0, -1, -1),
(1, 1, 1, 0, -1, 0),
(1, 1, 1, 0, -1, 1),
(1, 1, 1, 0, 0, -1),
(1, 1, 1, 0, 0, 0),
(1, 1, 1, 0, 0, 1),
(1, 1, 1, 0, 1, -1),
(1, 1, 1, 0, 1, 0),
(1, 1, 1, 0, 1, 1),
(1, 1, 1, 1, -1, -1),
(1, 1, 1, 1, -1, 0),
(1, 1, 1, 1, -1, 1),
(1, 1, 1, 1, 0, -1),
(1, 1, 1, 1, 0, 0),
(1, 1, 1, 1, 0, 1),
(1, 1, 1, 1, 1, -1),
(1, 1, 1, 1, 1, 0),
(1, 1, 1, 1, 1, 1)]
Gets you len(list(product([-1, 0, 1], repeat=6))) = 729
Use itertools.product
import iertools
assert(sum(1 for _ in itertools.product([-1, 0, 1], repeat=6)) == 3**6)
If you know itertools.permutations I think no more explanation is required.
I am trying to loop over a 3d matrix and access the values and get some count values for finding probability. I am successful in it but one thing is i am assuming b=0 initially which i dont want to since it gives a small error in my probability which i want to reduce. the matrix is
array([[[ 1, 1, 1, 1, -99, -99],
[ 1, 1, 1, 1, -99, -99],
[ 1, 1, 1, 1, -99, -99],
[ 1, 1, 1, 1, 1, -99]],
[[-99, -99, -99, -99, -99, -99],
[-99, -99, -99, -99, -99, -99],
[-99, -99, -99, -99, -99, 1],
[-99, -99, -99, -99, -99, 1]],
[[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1]],
[[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1, 1]]], dtype=int16)
and the code i ran is:
for i in range(4):
... for j in range(6):
... b=NR=NN=RR=RN=0
... for k in range(4):
... a = array[k][i][j]
... if(a==0 and b==0):
... NN+=1
... b=a
... elif(a==0 and b==1):
... RN+=1
... b=a
... elif(a==1 and b==0):
... NR+=1
... b=a
... elif(a==1 and b==1):
... RR+=1
... b=a
... else:
... exit
... print(NN,NR,RN,RR)
for this i get extra 24 'NR' values which i dont want to occur by not initializing b. When i did not initialize b i got the following output:
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 2)
(0, 0, 0, 2)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 2)
(0, 0, 0, 2)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 2)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
(0, 0, 0, 3)
which is not correct because the actual output is
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 1)
(0, 1, 0, 1)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 1)
(0, 1, 0, 1)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 1)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
(0, 1, 0, 2)
without the value '1' in the second column. Any suggestions on how i can solve this?
To skip a loop iteration use continue not exit (which is not a valid command). Since on the first iteration there is no valid b value, initialize it to -99. Skip summation and the b=a assignment if a=-99 so that b retains the last valid a value.
for i in range(4):
for j in range(6):
NR=NN=RR=RN=0
b = -99
for k in range(4):
a = array[k][i][j]
if a == -99 : continue
if(a == 0 and b == 0): NN+=1
elif(a == 0 and b == 1): RN+=1
elif(a == 1 and b == 0): NR+=1
elif(a == 1 and b == 1): RR+=1
b=a
print(NN,NR,RN,RR)
data = (1,1,1,1,1)
dict_letters = {(1,1,1,1,1) : 'A',
(0,1,1,0,1) : 'B',
(1,1,1,1,1) : 'C',
(1,0,1,0,1) : 'D'}
def search():
for key in dict_letters:
if data == key:
print(dict_letters[key])
search()
#when running, this would result in only 'C' being printed; 'A' was never printed
Previously in my code, I obtained a unique 5 item tuple that was made up of 0's and 1's (the data tuple). The 5 items were essential for differentiating most letters such as 'B' and 'D' from the rest in my dictionary. However, I was faced with a problem when there were two keys that had the same 5 items, so I added 10 more items (other identifying data) to the tuple to help further differentiate the keys; this is an excerpt from my current dictionary:
data = (0,1,1,0,1,1,1,1,1,1,1,1,1,1,1)
data = (0,1,1,0,1,0,0,0,0,0,0,0,0,0,0)
data = (0,1,1,0,1,0,0,1,1,1,0,0,1,0,1)
#x = 0 or 1
dict_letters = {(1,1,1,1,1,x,x,1,x,x,x,x,x,x,x) : 'A',
(0,1,1,0,1,x,x,x,x,x,x,x,x,x,x) : 'B',
(1,1,1,1,1,x,x,0,x,x,x,x,x,x,x) : 'C',
(1,1,0,0,1,x,x,x,x,x,x,x,x,x,x) : 'D'}
def search():
for key in dict_letters:
if data == key:
print(dict_letters[key])
search()
#I need to find a way for all of the data tuples to print 'B' after running the program
In this excerpt, I have only created conditions for 1 of the additional items to differentiate 'A' from 'C'. I was wondering if it was possible to disregard if the other 9 additional items were 0 or 1 since they are not useful in the differentiation of these two keys (the items I wish to disregard are marked with x). I would also like to disregard the 10 additional items for 'B' and 'D' because the first 5 items are sufficient for identification. For example, I want a way for (0,1,1,0,1,0,0,0,0,0,0,0,0,0,0),(0,1,1,0,1,1,1,1,1,1,1,1,1,1,1),(0,1,1,0,1,0,0,1,1,1,0,0,1,0,1), and etc to be all read as 'B' without coding 2047 extra keys for 'B'.
I tried setting x = 0 or 1, and x = 0 and 1 before the dictionary but these does not work since I found that the key is then set to (0,1,1,0,1,1,1,1,1,1,1,1,1,1,1) for some reason after running the program.
Note: I am looking at using all the additional items at least once in the future, so removing any of the 10 additional items is not an option.
I am also relatively new to Python so I would appreciate it if you could make your answers as simple as possible. Thank you in advance!
We can use a plain dict for this task, we just have to build all of the possible keys for each letter. Your key tuples contain 15 items, with each item having 2 different values so there's a maximum of only 2**15 = 32768 different patterns, that's quite small on a modern machine.
We can use itertools.product to generate all the patterns efficiently. product effectively creates nested for loops from the args you pass it. Here's a short illustration of the technique. The following code generates all the patterns corresponding to 10XX01.
from itertools import product
for t in product(*[(1,), (0,), (0, 1), (0, 1), (0,), (1,)]):
print(t)
output
(1, 0, 0, 0, 0, 1)
(1, 0, 0, 1, 0, 1)
(1, 0, 1, 0, 0, 1)
(1, 0, 1, 1, 0, 1)
Here's some code that uses the data given in the question to build a dict you can use for your searches. We use the dict.get method so that if you look up a pattern that isn't in the dict the code returns None.
from __future__ import print_function
from itertools import product
#x = 0 or 1
X = 'x'
letter_patterns = {
(1, 1, 1, 1, 1, X, X, 1, X, X, X, X, X, X, X): 'A',
(0, 1, 1, 0, 1, X, X, X, X, X, X, X, X, X, X): 'B',
(1, 1, 1, 1, 1, X, X, 0, X, X, X, X, X, X, X): 'C',
(1, 1, 0, 0, 1, X, X, X, X, X, X, X, X, X, X): 'D',
}
def make_dict(letter_patterns):
''' Build a dict of all the bit patterns for each letter '''
xlate = {0: (0,), 1: (1,), X: (0, 1)}
letter_dict = {}
# Generate all of the (0, 1) combinations for each X in each pattern
for pattern, letter in letter_patterns.items():
for key in product(*[xlate[u] for u in pattern]):
letter_dict[key] = letter
return letter_dict
# test
letter_dict = make_dict(letter_patterns)
test_items = [
((1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
((1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1), 'A'),
((1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
((0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'B'),
((0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'B'),
((0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1), 'B'),
((1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
((1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), 'C'),
((1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'D'),
((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), 'D'),
((1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'D'),
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'Z'),
]
# Check that each test key gets the correct letter, or returns
# None if the key isn't in letter_dict
for key, true_letter in test_items:
letter = letter_dict.get(key)
print(key, true_letter, letter, letter == true_letter)
output
(1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) A A True
(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) B B True
(0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) B B True
(0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1) B B True
(1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1) C C True
(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) D D True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1) D D True
(1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) D D True
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) Z None False
This code runs correctly on both Python 2 and 3 (tested on 2.6.6 and 3.6.0). You can make it slightly more efficient on Python 2 by changing
for pattern, letter in letter_patterns.items():
to
for pattern, letter in letter_patterns.iteritems():
The tuples in letter_patterns are not very convenient, especially if you want to put a lot of symbols into letter_patterns. To reduce the typing, rather than using those tuples we can use strings. Here's a variation of the above code that does that. The resulting letter_dict still uses tuple keys, since I assume that's what you get from your Leap Motion Device hardware.
letter_patterns = {
'A': '11111xx1xxxxxxx',
'B': '01101xxxxxxxxxx',
'C': '11111xx0xxxxxxx',
'D': '11001xxxxxxxxxx',
}
def make_dict(letter_patterns):
''' Build a dict of all the bit patterns for each letter '''
xlate = {'0': (0,), '1': (1,), 'x': (0, 1)}
letter_dict = {}
# Generate all of the (0, 1) combinations for each X in each pattern
for letter, pattern in letter_patterns.items():
for key in product(*[xlate[u] for u in pattern]):
letter_dict[key] = letter
return letter_dict
My understanding is that given some tuple as key, you want some entries in this tuple to be ignored if they do not match an existing key exactly.
You can do that by implementing your own dictionary-like class with the help of collections.UserDict and a custom __getitem__ method.
The following implementation assumes that entries in the tuple are either 1 or 0. Without that assumption, it would have to traverse all keys.
from UserDict import UserDict
# for Python 3 use this import instead:
# from collections import UserDict
from itertools import product
class WildcardDict(UserDict):
def __getitem__(self, args):
item, *wildcards = args
try:
return self.data[item]
except KeyError:
for xs in product((0, 1), repeat=len(wildcards)):
xs = iter(xs)
item = tuple(next(xs) if i in wildcards else x for i, x in enumerate(item))
if item in self.data:
return self.data[item]
raise KeyError(args)
d = WildcardDict()
d[0, 1, 1, 0, 1] = 'B'
print(d[(0, 1, 1, 0, 1), ]) # 'B'
print(d[(0, 1, 0, 0, 0), 2, 4]) # 'B'
Note that dict item lookup is usually O(1), although this makes it O(2k) where k is the number of wildcards. In particular, this means that if the number of wildcards was ever to grow, you would be better using a list where lookup would be O(n).