Value Error on computing matrix elements sum - python

I am trying to write a function that adds all elements in a matrix. The special condition is if an element in the matrix is 0, we count the element below this 0 also 0. For example:
matrix =
[[0, 1, 1, 2],
[0, 5, 0, 0],
[2, 0, 3, 3]]
Should return 9 because 1+1+2+5=9
Here is what I have for my code, I got this error, ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all(). Can someone please help?
import numpy as np
def matrixElementsSum(matrix):
a=np.array([matrix])
sumofcolumn=0
sum=0
for x in range(len(a)): # x in matrix row
for y in range(len(a[0])): #y in matrix col
if a[x][y]==0:
a[x+1][y]==0 #set next row same column=0
sumofcolumn+=a[x][y] #sumofcolumn is a column sum
for x in sumofcolumn:
sum+=x
return sum

You could rotate, flatten, and them use a simple comprehension:
import numpy as np
matrix = [[0, 1, 1, 2], [0, 5, 0, 0], [2, 0, 3, 3]]
matrix = np.rot90(matrix).flatten()
indices = set(np.where(matrix==0)[0]+1) # set of indices to the right of 0 fast lookup
final = sum(e for i,e in enumerate(matrix) if i not in indices)
print(final)
Output:
9
When you rotate and flatten, you are left with:
[2 0 3 1 0 3 1 5 0 0 0 2]
And if you notice, all the values that had 0 above them in your matrix, now have 0 to the left of them, and you can use the list comprehension to ignore these, and then find the sum of the result.
I'm sure there is a way to do this without the rotation, but I feel this way is much easier to visualize.

Find the woking code with inline comments where you got wrong.
import numpy as np
def matrixElementsSum(matrix):
a = np.array(matrix) # no need of appending in []
my_sum = 0 # sumofcolumn not required
for x in range(len(a)): # x in matrix row
for y in range(len(a[x])): # y in matrix col
if a[x][y] == 0 and x < len(a)-1: # handling last index
a[x+1][y] = 0 # set next row same column=0
my_sum += a[x][y] # adding value to sum..
# no for loop required, you are not appending values to a list.
# it's an integer and it's declared outside of loops.
return my_sum
matrix = [[0, 1, 1, 2], [0, 5, 0, 0], [2, 0, 3, 3]]
print(matrix)
print(matrixElementsSum(matrix))

You can do this as follows
total = 0
zeros = []
for row in matrix:
total += sum([val for ix, val in enumerate(row) if not ix in zeros])
zeros = [ix for ix, i in enumerate(row) if i == 0]
total

It can be done with rows-to-columns and flat list where index is increased by 2 if value is 0:
def matrix_elements_sum(matrix):
lst = sum(list(zip(*matrix)), ()) # rows-to-columns and to flat list
total = 0
i = 0
while i < len(lst):
if lst[i] != 0:
total += lst[i]
i += 1
else:
i += 2
return total

def matrixElementsSum(matrix):
totalSum = 0
for row in range(len(matrix)):
for col in range (len(matrix[0])):
if matrix[row][col] == 0 and row != len(matrix) -1:
matrix[row+1][col] = 0
else:
totalSum = totalSum + matrix[row][col]
return totalSum

I know this is vary late...
def matrixElementsSum(matrix):
sum = 0
for r in range(len(matrix)):
for c in range(len(matrix[r])):
if (matrix[r][c] != 0):
if (r==0):
sum += matrix[r][c]
else:
if(matrix[r-1][c] !=0):
sum += matrix[r][c]
else:
if(r == len(matrix)-1):
continue
else:
matrix[r+1][c] = 0
return sum

Related

How to generate a matrix with consecutive/continues integers like [ [1, 2, 3], [4, 5, 6], [7, 8, 9]... ]?

I have n rows and m columns I need a matrix which consists of consecutive m numbers like 1,2,3,4 this in each of the n rows that needs to be in ever increasing order.
example: 3 X 4 matrix\
**[\
[1, 2, 3, 4], \
[5, 6, 7, 8],\
[9, 10, 11, 12]\
]**
The intuition is very simple. What we need is our starting element in eaxh row should be the next element of the previous row's last element. That is the only tricky part in this problem.
For that we start our next row generation with arr[i-1][-1] to the arr[i-1][-1] + m. But for the first row generation we start from 1 since the matrix is empty.
Code
mat = []
n,m = map(int,input().split())
for row in range(n):
# if the row is starting row we start it with 1
# Else we assign k to the prev rows
if row == 0:
k = 1
else:
k = mat[row-1][-1] + 1
x = []
#the new row starts from previous rows last elemnt + 1
for j in range(k,k+m):
x.append(j)
mat.append(x)
print(mat)
First generate a continuous sequence of numbers and then adjust the format, with reference to either:(n and m represent the number of rows and columns respectively)
use the built-in functions, array to generate sequences, reshape to adjust the layout
import numpy as np
n, m = map(int, input().split())
res = np.arange(1, n*m+1).reshape(n, m)
print(res)
using list generative
items = list(range(1, m*n+1))
res = [items[i:i+m] for i in range(0, len(items), m)]
print(res)
here's a one liner to achieve that -
row, col = 3, 4
mat = [[col*i + j for j in range(1, col+1)] for i in range(row)]
print(mat)

How could you rewrite a list of lists so that "islands" of values are unique from one another?

Let's say I've got a list of lists (or more conceptually accurate a 2D array):
list = [[1,1,0,0,0],
[1,1,2,0,0],
[0,2,2,2,0],
[0,0,0,2,0],
[0,0,0,1,0]]
I'd like to identify the different regions of identical values and rewrite the list so that each region has a unique value, like so:
list = [[1,1,2,2,2],
[1,1,3,2,2],
[0,3,3,3,2],
[0,0,0,3,2],
[0,0,0,4,2]]
I've mostly tried writing variations of a loop parsing the array per value and setting adjacent values equal to each other (which yea, is redundant I guess), BUT ensuring that island of 1s in the top left is distinct from the 1 in the bottom right was just not working. My attempts were spotty at best and non-functional at worst. Examples:
for x in list_length:
for y in sublist_length:
try:
if list[x][y] == list[x+1][y]:
list[x+1][y] = list[x][y]
except:
pass
or
predetermined_unique_value = 0
for x in list_length:
for y in sublist_length:
try:
if list[x][y] == list[x+1][y]:
list[x+1][y] = predetermined_unique_value
predetermined_unique_value += 1
except:
pass
and many slight variations on which directions (up, down, left, right from current spot/point) to check, brute forcing the loop by running it until all spots had been assigned a new value, etc.
Clearly I am missing something here. I suspect the answer is actually super simple, but I can't seem to find anything on google or reddit, or other answers here (I'm probably just conceptualizing it weirdly so searching for the wrong thing).
Just to reiterate, how could you parse that list of lists to organize values into adjacent regions based on identical data and rewrite it to ensure that those regions all have unique values? (I.E. so that there is only one region of the 0 value, one region of the 1 value, etc. etc.)
I hope this is enough information to help you help me, but in truth I just as much am not sure how to do this as I am doing it wrong. Please don't hesitate to ask for more.
Based on this answer you can do it with ndimage from the scipy library.
I applied your data to his answer and that's what I got as result:
from scipy import ndimage
import numpy as np
data_tup = ((1,1,0,0,0),
(1,1,2,0,0),
(0,2,2,2,0),
(0,0,0,2,0),
(0,0,0,1,0))
data_list = [[1,1,0,0,0],
[1,1,2,0,0],
[0,2,2,2,0],
[0,0,0,2,0],
[0,0,0,1,0]]
def find_clusters(array):
clustered = np.empty_like(array)
unique_vals = np.unique(array)
cluster_count = 0
for val in unique_vals:
labelling, label_count = ndimage.label(array == val)
for k in range(1, label_count + 1):
clustered[labelling == k] = cluster_count
cluster_count += 1
return clustered, cluster_count
clusters, cluster_count = find_clusters(data_list)
clusters_tup, cluster_count_tup = find_clusters(data_tup)
print(" With list of lists, Found {} clusters:".format(cluster_count))
print(clusters, '\n')
print(" With tuples of tuple, Found {} clusters:".format(cluster_count_tup))
print(clusters_tup)
Output:
With list of lists, Found 5 clusters:
[[2 2 0 0 0]
[2 2 4 0 0]
[1 4 4 4 0]
[1 1 1 4 0]
[1 1 1 3 0]]
With tuples of tuple, Found 5 clusters:
[[2 2 0 0 0]
[2 2 4 0 0]
[1 4 4 4 0]
[1 1 1 4 0]
[1 1 1 3 0]]
Both times the Output is a list of list. If you wish to have it different, the function needs to be changed inside.
You can use skimage.measure.label:
>>> import numpy as np
>>> from skimage import measure
>>>
>>> a = np.array([[1,1,0,0,0],
[1,1,2,0,0],
[0,2,2,2,0],
[0,0,0,2,0],
[0,0,0,1,0]])
>>> measure.label(a, background=a.max()+1)
array([[1, 1, 2, 2, 2],
[1, 1, 3, 2, 2],
[4, 3, 3, 3, 2],
[4, 4, 4, 3, 2],
[4, 4, 4, 5, 2]])
Note that the label function has an argument connectivity which determines how blobs/clusters are identified. The default for a 2D array is to consider diagonal neighbors. If that is undesired, connectivity=1 will consider only horizontal/vertical neighbors.
I'm not sure how good the performance of this solution is but here's a recursive approach to identify a connected segment. It will take a coordinate and return the same list of islands with every coordinate that was part of the same island as the given coordinate with True.
islands = [[1,1,0,0,0],
[1,1,2,0,0],
[0,2,2,2,0],
[0,0,0,2,0],
[0,0,0,0,0]]
def print_islands():
for row in islands:
print(row)
def get_bool_map(i, j):
checked_indexes = [[False] * len(islands[0]) ] * len(islands)
checked_cords = []
def check_island_indexes(island_value, m, i, j):
if i < 0 or j < 0:
return
try:
if m[i][j] != island_value:
return
else:
if [i, j] in checked_cords:
return
else:
checked_cords.append([i, j])
m[i][j] = True
except IndexError:
return
check_island_indexes(island_value, m, i - 1, j)
check_island_indexes(island_value, m, i + 1, j)
check_island_indexes(island_value, m, i, j - 1)
check_island_indexes(island_value, m, i, j + 1)
check_island_indexes(islands[i][j], islands, i, j)
get_bool_map(0, 4)
print_islands()
[1, 1, True, True, True]
[1, 1, 2, True, True]
[0, 2, 2, 2, True]
[0, 0, 0, 2, True]
[0, 0, 0, 1, True]

How to print first and last index of the longest sequence of same-values neighbors?

I am interested in finding the first and last index of the longest sequence of same-valued neighbours for a given list. The closest question I could find was: First and last index of a sequence, but this does not exactly answer my question.
So let's say for example:
a = [4, 6, 1, 0, 0, 0, 2, 4, 4, 1]
I would like to output to be
[3, 5]
Here is what I have come up with so far, and it works for this example. However, once the list changes to have a 0 somewhere before or after the sequence (non-neighbouring) it does not work anymore.
# Find distinct values of a
distinct = []
for x in a:
if x not in distinct:
distinct.append(x)
# Check which value has the longest sequence
countMax = 0
countNow = 1
num = 0
for i in a:
if a[i] == a[i+1]:
countNow += 1
num = i
else:
countNow = 1
if countNow > countMax:
countMax = countNow
longest = distinct[num-1]
# Find first index value of number with longest sequence
firstIndex = a.index(longest)
# Find last index value of number with longest sequence
a_reverse = a[::-1]
firstIndex_reverse = a_reverse.index(longest)
lastIndex = len(a) - 1 - firstIndex_reverse
print(firstIndex, lastIndex)
I don't know how better to find the first and last index for only the sequence in question.
I'd just iterate the list and keep track of everything:
a = [4, 6, 1, 0, 0, 0, 2, 4, 4, 4, 4, 4, 4, 1]
# indices
best_start = 0
best_end = 0
curr_start = 0
curr_end = 0
prev_val = a[0]
for (idx, curr_val) in enumerate(a[1:]):
curr_end = idx + 1 # +1 since slicing shifted everything
if prev_val == curr_val:
if curr_end - curr_start > best_end - best_start:
best_start = curr_start
best_end = curr_end
else:
curr_start = curr_end
prev_val = curr_val
print(best_start, best_end)

Remove smallest value from array after append to New array

I am running a selection sort function in Python that works with numpy arrays instead of lists (so I can't use .pop for this, I don't think).
The function is:
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append((smallest))
arr = arr[(arr > smallest)]
return newArr
I want that "arr = arr[(arr > smallest)] which obviously doesn't work, to remove the smallest value (or, the value appended to newArr i.e the same value) from the passed array in the same way that .pop would do with a list.
I've tried things along these lines:
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
index = [2, 3, 6]
new_a = np.delete(a, index)
But couldn't get it to work. At the end of the day, I need to get something in the format of:
arr = randint(0,10,20)
to return an array sorted in ascending order. All I can manage is returning the smallest values repeated.
Thanks for any help
Try
arr = arr[np.where(arr > smallest)]
You may try:
arr = arr[ arr != np.min(a)]
This way you'll take from arr all the elements except the smallest one and reassign them to arr.
Your algorithm is almost correct. Indeed, it works if there are no duplicate values in arr:
import numpy as np
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append((smallest))
arr = arr[(arr > smallest)]
return newArr
findSmallest = np.min
# no duplicate values
auniq = np.random.choice(np.arange(20), (10,), replace=False)
print(auniq)
print(selectionSort(auniq))
Sample run:
[ 0 1 7 4 10 14 13 16 9 12]
[0, 1, 4, 7, 9, 10, 12, 13, 14, 16]
If there are duplicates it will crash because upon removing a minimum with duplicates the duplicates will be removed as well and that throws off the logic of the loop.
# duplicate values
adupl = np.random.randint(0, 9, (10,))
print(adupl)
# next line would crash
#print(selectionSort(adupl))
One fix is to only remove one copy of duplicates. This can for example be done using argmin which returns the index of the/one minimum, not its value.
def selectionSort2(arr):
arr = np.array(arr)
sorted = np.empty_like(arr)
for i in range(len(sorted)):
j = arr.argmin()
sorted[i] = arr[j]
arr = np.delete(arr, j)
return sorted
print(selectionSort2(adupl))
This works but is terribly inefficient because np.delete is more or less O(n). It is cheaper to swap the minimum element with a boundary element and then cut that off:
def selectionSort3(arr):
arr = np.array(arr)
sorted = np.empty_like(arr)
for i in range(len(sorted)):
j = arr[i:].argmin()
sorted[i] = arr[i + j]
arr[i], arr[i + j] = arr[i + j], arr[i]
return sorted
print(selectionSort3(adupl))
Looking at selectionSort3 we can observe that the separate output sorted is not actually needed, because arr has been sorted inplace already:
def selectionSort4(arr):
arr = np.array(arr)
for i in range(len(arr)):
j = arr[i:].argmin()
arr[i], arr[i + j] = arr[i + j], arr[i]
return arr
print(selectionSort4(adupl))
Sample output (adupl and output of selectionSort2-4):
[0 4 3 8 8 4 5 0 4 2]
[0 0 2 3 4 4 4 5 8 8]
[0 0 2 3 4 4 4 5 8 8]
[0 0 2 3 4 4 4 5 8 8]

a simple sum of columns in a 2D list

import random
ROWS = 3
COLS = 3
def main():
values = [[0,0,0], [0,0,0], [0,0,0]]
for r in range(ROWS):
for c in range(COLS):
values[r][c] = random.randint(1, 4)
print('List:')
print(values)
print(sum(values[0]))
print(sum(values[1]))
print(sum(values[2]))
main()
This is the code that I have written and what I would like to do is display the the individual totals for each column. For example, the final result would look like:
Total of column 0 is 7
Total of column 1 is 6
Total of column 2 is 7
Builtin python lists are not very ideal for working with matrix-like data. I would highly suggest using numpy:
import numpy as np
l = np.array(values)
l.sum(axis=1)
However, if you really need to use python lists, one strategy is just to reduce your outer list with your desired output.
reducer = lambda x, y : [x[i] + y[i] for i in range(len(x)]
reduce(reducer, values)
You can simply reverse the order of ROWS and COLS in another pair of nested loops:
import random
ROWS = 3
COLS = 3
values = list()
for r in range(ROWS):
values.append(list())
for c in range(COLS):
values[r].append(random.randint(1, 4))
print('List:', values, sep="\n")
for c in range(COLS):
sum = 0
for r in range(ROWS):
sum += values[r][c]
print("Total of column {} is {}".format(c, sum))
Produces:
List:
[[3, 2, 4], [4, 3, 4], [4, 2, 3]]
Total of column 0 is 11
Total of column 1 is 7
Total of column 2 is 11
You can try :
import random
ROWS=3
COLS=3
def main():
values = []
for i in range(ROWS):
list1=[]
for j in range(COLS):
val =random.randint(1,4)
list1.append(val)
values.append(list1)
print (values)
for col in range(COLS):
sum = 0
for row in range(ROWS):
sum += values[row][col]
print("Total of column "+str(col)+" is " +str(sum))
main()
[[4, 1, 2], [2, 3, 4], [3, 3, 1]]
Total of column 0 is 9
Total of column 1 is 7
Total of column 2 is 7
I ended up using something which now seems quite simple:
thank you for your responses
values = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for r in range(ROWS):
for c in range(COLS):
values[r][c] = random.randint(1, 4)
print('List:')
print(values)
print('Total of row 0 is', sum(values[0]))
print('Total of row 1 is', sum(values[1]))
print('Total of row 2 is', sum(values[2]))
valuescolumn1 = [row[0] for row in values]
print('Total of column 0 is', sum(valuescolumn1))
valuescolumn2 = [row[1] for row in values]
print('Total of column 1 is', sum(valuescolumn2))
valuescolumn3 = [row[1] for row in values]
print('Total of column 2 is', sum(valuescolumn3))

Categories

Resources