I have a task to do:
a = [0,1,0,1,0,0,1,1,1,1,0]
I have the list - a - randomly generated each time the program runs.
Task 1: find the longest 1-row (here it is [1,1,1,1]) and output its starting index number.
Task 2: find 1,1 in a; how many times it occurs? 1,1,1 doesn't count, only exact matches are taken into account.
a = [1,0,0,1,1,0,1,1,1,1]
counter = 1
for i in range(len(a)):
if a[i] == 1:
a[i] = counter
counter += 1
print(a)
b = []
one_rows = []
for i in a:
if i > 0:
one_rows.append(i)
if i == 0:
b.append([one_rows])
one_rows.clear()
print(b)
If I've understood your question right, you can use can use itertools.groupby to group the list and count the number of 1s:
a = [0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0]
max_len, double_ones, max_idx = float("-inf"), 0, 0
for v, g in groupby(enumerate(a), lambda k: k[1]):
if v == 1:
idxs = [i for i, _ in g]
double_ones += len(idxs) == 2
if len(idxs) > max_len:
max_len = len(idxs)
max_idx = idxs[0]
print("Longest 1-row:", max_len, "Index:", max_idx)
print("How many 1,1:", double_ones)
Prints:
Longest 1-row: 4 Index: 6
How many 1,1: 0
Related
I need to find the longest path of 0's in a 2d matrix recursively and can't figure out how to do it.( from a given (i , j) it can only move up, down, right or left)
For example this matrix:
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
print(question3_b(mat))
This should return 6 as there are communities of sizes 1,3,6.
My attempt: I created a few wrapper functions to find the maximum in a list, and a function to find the route at a given (i,j) element and add it to a list, and doing this on every point(i,j) in the matrix.
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
for i in range(rows):
for j in range(cols):
visited = zeros(rows, cols) # create a 2d list of 0's with size rows,cols
a = findcommunity(mat, (i, j), visited)
print("a=", a)
community_lengths.append(a)
print(community_lengths)
return findmax(community_lengths)
def findcommunity(mat: list, indices: tuple, visited: list): # find length of community
#global rec_counter
#rec_counter += 1
i, j = indices
rows = len(mat)
cols = len(mat[0])
if mat[i][j] == 0:
visited[i][j] = 1
if i < rows - 1:
if visited[i + 1][j] == 0:
return 1 + findcommunity(mat, (i + 1, j), visited)
if j < cols - 1:
if visited[i][j + 1] == 0:
return 1 + findcommunity(mat, (i, j + 1), visited)
if i > 0:
if visited[i - 1][j] == 0:
return 1 + findcommunity(mat, (i - 1, j), visited)
if j > 0:
if visited[i][j - 1] == 0:
return 1 + findcommunity(mat, (i, j - 1), visited)
else: # mat[i][j]!=0
return 0
def zeros(rows:int,cols:int)->list: #create a 2d matrix of zeros with size (rows,cols)
if rows==1 and cols==1:return [[0]]
if rows==1 and cols>1:return [[0]*cols]
if rows>1 and cols>1:return zeros(rows-1,cols)+zeros(1,cols)
def findmax(arr:list)->int: # find max in an array using recursion
if len(arr)==2:
if arr[0]>arr[1]:return arr[0]
else:return arr[1]
else:
if arr[0]>arr[1]:
arr[1]=arr[0]
return findmax(arr[1:])
else:
return findmax(arr[1:])
Where did I go wrong? for a matrix of 4X4 zeros, I expect it to run 16*16 times[16 times for each i,j, and there are 16 elements in the matrix]. but it only runs once.
zeros is a recursive function I made that functions like np.zeros, it creates a 2d matrix full of 0's with specified size.
It got really messy but I tried to just change your code instead of writing a new solution. You should have a look at collections deque. Saw this several times where people keep track of visited a lot easier.
I changed visited to outside of the loop and defined it with np.zeros (didn't have your function ;) ). I'm not sure if your recursive function calls at return were wrong but your if-statements were, or at least the logic behind it (or I didn't understand, also possible :) )
I changed that block completly. The first time you come across a 0 in mat the recursive part will dig into the mat as long as it finds another 0 left,right,bottom or top of it (that's the functionality behind dc and dr). That's where the community_counter is increasing. If the function is returning the last time and you jump out to the outerloop in question_3b the counter gets resetted and searchs for the next 0 (next start of another community).
import numpy as np
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
visited = np.zeros((rows, cols)) # create a 2d list of 0's with size rows,cols
community_count = 0
for row in range(rows):
for col in range(cols):
if visited[row][col]==0:
community_count,visited = findcommunity(mat, (row, col), visited, community_count)
if community_count!=0:
community_lengths.append(community_count)
community_count=0
return community_lengths
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
dr = [-1, 0, 1, 0]
dc = [0,-1, 0, 1]
for k in range(4):
rr = i + dr[k]
cc = j + dc[k]
if 0<=rr<rows and 0<=cc<cols:
if visited[rr][cc]==0 and mat[rr][cc]==0:
community_count, visited = findcommunity(mat, (rr,cc), visited, community_count)
return community_count,visited
else:
return community_count,visited
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
all_communities = question3_b(mat)
print(all_communities)
# [6, 3, 1]
print(max(all_communities))
# 6
EDIT
Here is the findcommunity function in your way. Tested it and it works aswell.
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
if i < rows - 1:
if visited[i + 1][j] == 0:
community_count, visited = findcommunity(mat, (i + 1, j), visited, community_count)
if j < cols - 1:
if visited[i][j + 1] == 0:
community_count, visited = findcommunity(mat, (i, j + 1), visited, community_count)
if i > 0:
if visited[i - 1][j] == 0:
community_count, visited = findcommunity(mat, (i - 1, j), visited, community_count)
if j > 0:
if visited[i][j - 1] == 0:
community_count, visited = findcommunity(mat, (i, j - 1), visited, community_count)
return community_count,visited
else:
return community_count,visited
Here a completely different approach, in case someone is interested.
import numpy as np
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
mat = np.array(mat)
# some padding of -1 to prevent index error
mask = np.full(np.array(mat.shape) + 2, -1)
mask[1:-1, 1:-1 ] = mat
# visiteds are set to -2
def trace(f, pt):
mask[tuple(pt)], pts = -2, [pt - 1]
pts += [trace(f, pt + d) for d in
([0, 1], [1, 0], [0, -1], [-1, 0]) if
mask[tuple(pt + d)] == f]
return pts
clusters = lambda f: {tuple(pt-1): trace(f, pt) for pt in np.argwhere(mask==f) if mask[tuple(pt)]==f}
# now call with value your looking for
print(clusters(0))
Consider an array Y of 0s and 1s. For example: Y = (0,1,1,0). I want to count the number of uninterrupted intervals of the 0s and 1s. In our example n0 = 2 and n1 = 1. I have a script which does the needed. It is not very elegant though. Does someone know a smoother or more pythonic version?
import pandas as pd
import numpy as np
# storage
counter = {}
# number of random draws
n = 10
# dataframe of random draw between 0 and 1
Y = pd.DataFrame(np.random.choice(2, n))
# where are the 0s and 1s
idx_0 = Y[Y[0] == 0].index
idx_1 = Y[Y[0] == 1].index
# count intervals of uninterrupted 0s
j = 0
for i in idx_0:
if i+1 < n:
if Y.loc[i+1, 0] == 1:
j += 1
else:
continue
if Y.loc[n-1, 0] == 0:
j += 1
counter['n_0'] = j
# count intervals of uninterrupted 1s
j = 0
for i in idx_1:
if i+1 < n:
if Y.loc[i+1, 0] == 0:
j += 1
else:
continue
if Y.loc[n-1, 0] == 1:
j += 1
counter['n_1'] = j
A more succinct solution taking advantage of pandas methods:
counter = Y[0][Y[0].diff() != 0].value_counts()
Y[0].diff() counts the difference between consecutive elements
diff != 0 marks the indices where the value changes
Y[idx].value_counts() counts the frequency of each value
Example result for 10 random elements, [0, 1, 1, 0, 1, 1, 1, 1, 1, 1]:
1 2
0 2
Name: 0, dtype: int64
If you insist having the keys as 'n_0' and 'n_1' instead, you can rename them with
counter = counter.rename(index={i: f'n_{i}' for i in range(2)})
You can also convert that to a dict with dict(counter), even though the pandas object has the same functionality with counter[key] giving you the respective value.
numbers = [0, 1, 1, 0]
def runs(x, numbers):
number_string = ''.join([str(n) for n in numbers])
return len([r for r in number_string.split('1' if x == 0 else '0') if r])
print(runs(0, numbers))
print(runs(1, numbers))
Update using dataframe:
import pandas as pd
import numpy as np
# storage
counter = {}
# number of random draws
n = 10
# dataframe of random draw between 0 and 1
Y = pd.DataFrame(np.random.choice(2, n))
print([v[0] for v in Y.values.tolist()])
def runs(x, numbers):
number_string = ''.join([str(n) for n in numbers])
return len([len(r) for r in number_string.split('1' if x == 0 else '0') if r])
values = [v[0] for v in Y.values.tolist()]
print(values)
print('Runs of 0: {}'.format(runs(0, values)))
print('Runs of 1: {}'.format(runs(1, values))
here is the input:
a = [1,1,2,3,4,1,1]
and I want to get the output like:
out = [1,2,3,4,1]
count = [2,1,1,1,2]
This is different from numpy.unique function.
here is my code, any better solutions?
def unique_count(input):
tmp = None
count = 0
count_list = []
value_list = []
for i in input:
if i == tmp:
count += 1
else:
if tmp != None:
count_list.append(count)
value_list.append(tmp)
count = 1
tmp = i
count_list.append(count)
value_list.append(tmp)
return((value_list,count_list))
What you want is itertools.groupby:
from itertools import groupby
a = [1,1,2,3,4,1,1]
group_counts = [(k, len(list(g))) for k, g in groupby(a)]
out, count = map(list, zip(*group_counts))
print(out)
print(count)
Or all in one line:
out, count = map(list, zip(*((k, len(list(g))) for k, g in groupby(a))))
Output:
[1, 2, 3, 4, 1]
[2, 1, 1, 1, 2]
If you want to know what's going on inside, you can take a look. Then you can try any library or other shorted/smarter solution. This is also a linear solution by the way.
arr = [1,1,2,3,4,1,1]
def customDupCounter(a):
result = [a[0]]
counter = [1]
curr_index = 0
for i in range(1,len(a)):
if a[i] == a[i-1]:
counter[curr_index] += 1
else:
curr_index += 1
result.append(a[i])
counter.append(1)
return result, counter
result, counter = customDupCounter(arr)
print(result)
print(counter)
a = [1, 1, 2, 3, 4, 1, 1]
uniques = []
count = []
curCount = 0
for i, num in enumerate(a):
if i == 0 or a[i - 1] != num:
uniques.append(num)
if a[i - 1] == num:
curCount += 1
else:
count.append(curCount)
curCount = 1
count.append(curCount)
print(uniques)
print(count)
Here, we go through each number in the code and add it to the uniques list if the previous number in the list was different. We also have a variable to keep track of the count which resets to 1 if the previous number was different.
I am practicing and trying to write O(n^2) program that tests whether there are two 1s lying on the same row or the same column in A. Where A = n x n matrix of 0s and 1s.
Given A as:
I should get answer return of 2 matches.
One is on the 1st row, and another on the 3rd column.
My 2nd Attempt:
def testLines():
count = 0
for x in range( 0, len(A)-1 ):
if( (A[x] == 1) & (A[x+1] == 1) ):
count+=1
for y in range( 0, len(A)-1):
if( (A[y] == 1 & A[y+1]) == 1 ):
count+=1
print( count, '1s has been matched in the array A')
testLines()
You want to nest the two loops and change the indexes so that both x and y are parsed. Currently your code moves through (all x, y = 0) and (x = 0, all y).
A = [[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 1, 0]]
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
if A[x][y] == 1:
if x+1 < N and A[x+1][y] == 1:
count += 1
if y+1 < N and A[x][y+1] == 1:
count += 1
print(count, '1s has been matched in the array A')
testLines()
Alternatively, you can go the Schwarzenegger way and not check if (x+1, y) or (x, y+1) even exist. That will raise IndexErrors that you can choose to ignore.
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
try:
if A[x][y] == 1 and A[x+1][y] == 1 or A[x][y+1] == 1:
count += 1
except IndexError:
continue
print(count, '1s has been matched in the array A')
You can run one nested loop (n²) to get summation of rows. If summation is 2 then that row has two 1s.
Now interchange rows and columns(consider rows as columns & vice versa).
Again run nested loop (n²) to check summation of columns.
n²+n²= O(n²)
I have a two dimensional list that I have to sort and I have to only use while loops. So far my code does not work for all lists.
def sort(list):
i = 0
j = 0
while i < len(list):
while j < len(list[i]) - 1:
if list[i][j] > list[i][j + 1]:
temp = list[i][j]
list[i][j] = list[i][j + 1]
list[i][j + 1] = temp
j += 1
j = 0
i += 1
return list
sort([[3,5,2,8,6,9],[9,1,2,5]])
This code still has numbers out of order. Is there a better way to sort?
Your inner loop takes only one pass through the list. This guarantees that the largest element is at the end, but doesn't necessarily do anythign else. You need to add a loop to continue while you still have unfinished business.
I left in the tracing statements I used to highlight the problem, as well as the simple reverse-order case.
def sort(list):
i = 0
j = 0
while i < len(list):
done = False
while not done:
done = True
while j < len(list[i]) - 1:
print i, j, list[i][j], list[i][j + 1]
if list[i][j] > list[i][j + 1]:
temp = list[i][j]
list[i][j] = list[i][j + 1]
list[i][j + 1] = temp
done = False
print "SWAP", list[i]
j += 1
j = 0
i += 1
return list
print sort([[6, 5, 4, 3, 2, 1, 0]])
print sort([[3,5,2,8,6,9],[9,1,2,5]])