how to compare current and last element iterated from numpy without loop - python

In 2d array get the last column and compare current value with its last iterated value and if the difference is == 1 then get row indexes of both of them . I able to do it using for loop but it gets slow when array grows as there are multiple conditions next after getting indexes
x=np.array ([[79, 50, 18, 55, 35],
[46, 71, 46, 95, 80], #1
[97, 37, 71, 2, 79], #2
[80, 96, 60, 85, 72],
[ 6, 52, 63, 86, 38],
[35, 50, 13, 93, 54], #5
[69, 21, 4, 40, 53], #6
[18, 34, 91, 67, 89],
[82, 16, 16, 24, 80]])
last_column = x[:,[-1]]
for kkk in range(1,len(last_column)):
if last_column[kkk] == last_column[kkk-1] + 1 \
or last_column[kkk] == last_column[kkk-1] - 1 :
print('range',last_column[kkk],last_column[kkk-1])
ouput is -
[80]
[79]
range [79] [80]
[72]
[38]
[54]
[53]
range [53] [54]
[89]
[80]

I don't think there is a way of doing this in python that doesn't use a loop (or a function that utilizes a loop). But I would suggest something like this, it worked quite well for me:
import numpy as np
x = np.array([[79, 50, 18, 55, 35], [46, 71, 46, 95, 80], [97, 37, 71, 2, 79],[80, 96, 60, 85, 72], [ 6, 52, 63, 86, 38], [35, 50, 13, 93, 54], [69, 21, 4, 40, 53], [18, 34, 91, 67, 89], [82, 16, 16, 24, 80]])
last_column = x[:,-1]
for kkk in range(1,len(last_column)):
if abs(last_column[kkk] - last_column[kkk-1]) == 1:
print('range',last_column[kkk],last_column[kkk-1])
This performed quite well on my device in about, 0.00023s possibly less.

You can use np.diff to compute the difference between all the numbers, and select those that are 1 or -1, and use np.pad to shift the resulting mask to get the n-1 items, and finally zip them together:
diff = np.insert(np.diff(x[:, -1]), 0, 0)
ix = (diff == -1) | (diff == 1)
z = zip(x[:,-1][ix], x[:,-1][np.pad(ix, (0,1))[1:]])
for first, second in z:
print('range', first, second)
Output:
range 79 80
range 53 54

Related

Why my loop is not exiting when after if condition I return

In this code I need to exit loop on certain condition. if position + 1 == len(triangle)
Maybe I am not good at Python and don't understand clearly its behaviour.
It is not listening to my command and keep calling same function instead of leaving the loop.
The only other thing I tried is to call break in the loop itself when same condition is met but it is not working as well.
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
total = 0
index = 0
skip = False
position = 0
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
if position + 1 == len(triangle): return <-----------------HERE I AM EXPECTING IT TO EXIT
for row in range(position, len(triangle)):
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
print(position, int(len(triangle)), index, values.get("max"), total)
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
Hehey, Got it working finally, so the solution was to break from loop earlier.
I had to put the condition in the beginning of the loop otherwise it was doing the same process and condition was wrong.
total = 0
index = 0
skip = False
position = 0
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
for row in range(position, len(triangle)):
if position == int(len(triangle)): break <<<--------------- I HAD TO CALL BREAK EARLIER, OTHERWISE FOR LOOP WAS KEEP WORKING INSTEAD OF STOPPING
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
Recursive brute force solution
def sliding_triangle(triangle, row = 0, index = 0):
if row >= len(triangle) or index >= len(triangle[row]):
return 0 # row or index out of bounds
# Add parent value to max of child triangles
return triangle[row][index] + max(sliding_triangle(triangle, row+1, index), sliding_triangle(triangle, row+1, index+1))
Tests
print(sliding_triangle([[3], [7, 4], [2, 4, 6], [8, 5, 9, 3]]))
# Output: 23
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
# Output: 1074
However, brute force approach times out on larges dataset
Optimized Solution
Apply memoization to brute force solution.
Uses cache to avoid repeatedly solving for subpaths of a parent triangle node
Code
def sliding_triangle(triangle):
' Wrapper setup function '
def sliding_triangle_(row, index):
' Memoized function which does the calcs'
if row >= len(triangle) or index >= len(triangle[row]):
return 0
if not (row, index) in cache:
# Update cache
cache[(row, index)] = (triangle[row][index] +
max(sliding_triangle_(row+1, index),
sliding_triangle_(row+1, index+1)))
return cache[(row, index)]
cache = {} # init cache
return sliding_triangle_(0, 0) # calcuate starting from top most node
Tests
Same results as brute force solution for simple test cases
Works on large dataset i.e. https://projecteuler.net/project/resources/p067_triangle.txt
Find and Show Optimal Path*
Modify Brute Force to Return Path
Show highlighted path in triangle
Code
####### Main function
def sliding_triangle_path(triangle, row = 0, index = 0, path = None):
'''
Finds highest scoring path (using brute force)
'''
if path is None:
path = [(0, 0)] # Init path with top most triangle node
if row >= len(triangle) or index >= len(triangle[row]):
path.pop() # drop last item since place out of bounds
return path
# Select best path of child nodes
path_ = max(sliding_triangle_path(triangle, row+1, index, path + [(row+1, index)]),
sliding_triangle_path(triangle, row+1, index+1, path + [(row+1, index+1)]),
key = lambda p: score(triangle, p))
return path_
####### Utils
def getter(x, args):
'''
Gets element of multidimensional array using tuple as index
Source (Modified): https://stackoverflow.com/questions/40258083/recursive-itemgetter-for-python
'''
try:
for k in args:
x = x[k]
return x
except IndexError:
return 0
def score(tri, path):
' Score for a path through triangle tri '
return sum(getter(tri, t) for t in path)
def colored(r, g, b, text):
'''
Use rgb code to color text'
Source: https://www.codegrepper.com/code-examples/python/how+to+print+highlighted+text+in+python
'''
return "\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text)
def highlight_path(triangle, path):
' Created string that highlight path in red through triangle'
result = "" # output string
for p in path: # Looop over path tuples
row, index = p
values = triangle[row] # corresponding values in row 'row' of triangle
# Color in red path value at index, other values are in black (color using rgb)
row_str = ' '.join([colored(255, 0, 0, str(v)) if i == index else colored(0, 0, 0, str(v)) for i, v in enumerate(values)])
result += row_str + '\n'
return result
Test
# Test
triangle = ([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
])
path = sliding_triangle_path(triangle)
print(f'Score: {score(tri, path)}')
print(f"Path\n {'->'.join(map(str,path))}")
print(f'Highlighted path\n {highlight_path(tri, path)}')
Output
Score: 1074
Path
(0, 0)->(1, 1)->(2, 2)->(3, 2)->(4, 2)->(5, 3)->(6, 3)->(7, 3)->(8, 4)->(9, 5)->(10, 6)->(11, 7)->(12, 8)->(13, 8)->(14, 9)
Got my own correct answer for the kata, which can handle big triangles and passed all tests
def longest_slide_down(triangle):
temp_arr = []
first = triangle[-2]
second = triangle[-1]
if len(triangle) > 2:
for i in range(len(first)):
for _ in range(len(second)):
summary = first[i] + max(second[i], second[i + 1])
temp_arr.append(summary)
break
del triangle[-2:]
triangle.append(temp_arr)
return longest_slide_down(triangle)
summary = triangle[0][0] + max(triangle[1][0], triangle[1][1])
return summary
You can try using an else and a pass, like so:
def max_value():
# code
def sliding_triangle():
if not skip:
# code
if position + 1 == len(triangle):
pass
else:
for row in range(position, len(triangle)):
# code
return total
print sliding_triangle()
As far as I know, you can't interrupt a def by throwing a return in two or more different points of the script just like in Java. Instead, you can just place a condition that, whether is respected, you skip to the return. Instead, you continue with the execution.
I synthesized your code to let you understand the logic easier, but it's not a problem if I have to write it fully

How to create multiple lists through one loop?

I want to create multiple list by using a single loop with some condition.
I know how to create one list which is done by appending, but here all the results of loop goes in one single list which is not what I want.
So lets say we run a loop on first 100 numbers and I want to create multiple list where first list contains numbers till 5 , second from 6 to 10, third from 11 to 15 and so.
This code is just for one list created by me.
number = range(100)
first = []
for i in number:
first.append(i)
first
Something like that:
l = []
for x in range(6, 102, 5):
l.append([y for y in range(x-5, x)])
Output:
[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28, 29, 30],
[31, 32, 33, 34, 35],
[36, 37, 38, 39, 40],
[41, 42, 43, 44, 45],
[46, 47, 48, 49, 50],
[51, 52, 53, 54, 55],
[56, 57, 58, 59, 60],
[61, 62, 63, 64, 65],
[66, 67, 68, 69, 70],
[71, 72, 73, 74, 75],
[76, 77, 78, 79, 80],
[81, 82, 83, 84, 85],
[86, 87, 88, 89, 90],
[91, 92, 93, 94, 95],
[96, 97, 98, 99, 100]]
The range function takes three parameters start, stop and step.
Try this, This will create what you want:
lists = []
for i in range(1, 100, 5): # range(start, end, size_of_each_list)
lists.append(list(range(i,i + 5)))
The lists will be:
[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28, 29, 30],
[31, 32, 33, 34, 35],
[36, 37, 38, 39, 40],
[41, 42, 43, 44, 45],
[46, 47, 48, 49, 50],
[51, 52, 53, 54, 55],
[56, 57, 58, 59, 60],
[61, 62, 63, 64, 65],
[66, 67, 68, 69, 70],
[71, 72, 73, 74, 75],
[76, 77, 78, 79, 80],
[81, 82, 83, 84, 85],
[86, 87, 88, 89, 90],
[91, 92, 93, 94, 95],
[96, 97, 98, 99, 100]]
This is one way to do it. Basically you create an iterator from your list and then get all the lists you want from it.
number = range(100)
number_iter = iter(number)
lists = []
while True:
try:
lists.append([next(number_iter) for _ in range(5)])
except StopIteration as e:
break
lists
this has the advantage that your initial `number' list can be anything...
I would use second list inside the first.
number = range(100)
cnt = 0
first = []
second = []
for i in number:
cnt += 1
second.append(i)
if cnt == 5:
first.append(second)
cnt = 0
second = []
first
output:
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
...
[95, 96, 97, 98, 99]]
I would do a list comprehension similar to this:
[[i, i+1, i+2, i+3, i+4] for i in range (1, 100, 5)]
The output would look like this:
[[1, 2, 3, 4, 5],
...,
[96, 97, 98, 99, 100]]
https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
It is possible. We can do this by directly creating variables in our globals() environment.
(Use locals() if it should exist only in the enclosing function).
You can do this with the following code:
# Run the loop
for i in range(100):
# for 0-4, list_name = 'list_1'
list_name = 'list_' + str(i//5 + 1)
# Try to append to that list
try:
globals()[list_name].append(i)
# If if doesn't exist, create it on the run!
except KeyError:
globals()[list_name] = [i]
This globals()[list_name] = [i] first:
Gets the module environment dictionary.
Creates list_name variable.
Initialises it to a list containing i.
Let us print them all:
for i in range(20):
# Print list_1 through list_20
list_name = 'list_' + str(i+1)
print(list_name + ':', globals()[list_name])
You get:
list_1: [0, 1, 2, 3, 4]
list_2: [5, 6, 7, 8, 9]
list_3: [10, 11, 12, 13, 14]
list_4: [15, 16, 17, 18, 19]
list_5: [20, 21, 22, 23, 24]
list_6: [25, 26, 27, 28, 29]
list_7: [30, 31, 32, 33, 34]
list_8: [35, 36, 37, 38, 39]
list_9: [40, 41, 42, 43, 44]
list_10: [45, 46, 47, 48, 49]
list_11: [50, 51, 52, 53, 54]
list_12: [55, 56, 57, 58, 59]
list_13: [60, 61, 62, 63, 64]
list_14: [65, 66, 67, 68, 69]
list_15: [70, 71, 72, 73, 74]
list_16: [75, 76, 77, 78, 79]
list_17: [80, 81, 82, 83, 84]
list_18: [85, 86, 87, 88, 89]
list_19: [90, 91, 92, 93, 94]
list_20: [95, 96, 97, 98, 99]
Note: Why not have some number fun! See python inflect package.

List Index out of range error in python

I'm trying to solve the max path sum problem from project euler.
CODE:
def main():
data = [map(int,row.split()) for row in open("Triangle.txt")]
print data
for i in range(len(data)-2,-1,-1):
for j in range(i+1):
data[i][j] += max([data[i+1][j],data[i+1][j+1]]) #list out of range error
print (data[0][0])
if __name__ == '__main__':
main()
The data value has 16 internal lists as follows:
[[75], [95, 64], [17, 47, 82], [18, 35, 87, 10], [20, 4, 82, 47, 65], [19, 1, 23, 75, 3, 34], [88, 2, 77, 73, 7, 63, 67], [99, 65, 4, 28, 6, 16, 70, 92], [41, 41, 26, 56, 83, 40, 80, 70, 33], [41, 48, 72, 33, 47, 32, 37, 16, 94, 29], [53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14], [70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57], [91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48], [63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31], [4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23], []]
And I am getting list index out of range error in the line:
data[i][j] += max([data[i+1][j],data[i+1][j+1]])
IndexError: list index out of range
How can i get rid of this error?
Thanks in advance...
The problem is the last item in data. It's an empty list. Try removing it and executing the script, as follows:
In [392]: data[-1]
Out[392]: []
In [393]: data = data[:-1]
In [394]: for i in range(len(data)-2,-1,-1):
.....: for j in range(i+1):
.....: data[i][j] += max([data[i+1][j],data[i+1][j+1]]) #list out of range error
.....: print (data[0][0])
.....:
1074
In order to eliminate the error altogether, without the need to manually alter the contents of data, you can read it correctly at the first place, as follows:
data = [map(int,row.split()) for row in open("Triangle.txt") if row.strip()]

Subset of an ndarray based on another array

I have an array of ints, they need to be grouped by 4 each. I'd also like to select them based on another criterion, start < t < stop. I tried
data[group].reshape((-1,4))[start < t < stop]
but that complains about the start < t < stop because that's hardcoded syntax. Can I somehow intersect the two arrays from start < t and t < stop?
The right way of boolean indexing for an array should be like this:
>>> import numpy as np
>>> a=np.random.randint(0,20,size=24)
>>> b=np.arange(24)
>>> b[(8<a)&(a<15)] #rather than 8<a<15
array([ 3, 5, 6, 11, 13, 16, 17, 18, 20, 21, 22, 23])
But you may not be able to reshape the resulting array into a shape of (-1,4), it is a coincidence that the resulting array here contains 3*4 elements.
EDIT, now I understand your OP better. You always reshape data[group] first, right?:
>>> b=np.arange(96)
>>> b.reshape((-1,4))[(8<a)&(a<15)]
array([[12, 13, 14, 15],
[20, 21, 22, 23],
[24, 25, 26, 27],
[44, 45, 46, 47],
[52, 53, 54, 55],
[64, 65, 66, 67],
[68, 69, 70, 71],
[72, 73, 74, 75],
[80, 81, 82, 83],
[84, 85, 86, 87],
[88, 89, 90, 91],
[92, 93, 94, 95]])
How about this?
import numpy as np
arr = np.arange(32)
t = np.arange(300, 364, 2)
start = 310
stop = 352
mask = np.logical_and(start < t, t < stop)
print mask
print arr[mask].reshape((-1,4))
I did the masking before the reshaping, not sure if that's what you wanted. The key part is probably the logical_and().

Numpy array of random matrices

I'm new to python/numpy and I need to create an array containing matrices of random numbers.
What I've got so far is this:
for i in xrange(samples):
SPN[] = np.random.random((6,5)) * np.random.randint(0,100)
Which make sense for me as PHP developer but is not working for python. So how do I create a 3 dimensional array to contain this matrices/arrays?
Both np.random.randint and np.random.uniform, like most of the np.random functions, accept a size parameter, so in numpy we'd do it in one step:
>>> SPN = np.random.randint(0, 100, (3, 6, 5))
>>> SPN
array([[[45, 95, 56, 78, 90],
[87, 68, 24, 62, 12],
[11, 26, 75, 57, 12],
[95, 87, 47, 69, 90],
[58, 24, 49, 62, 85],
[38, 5, 57, 63, 16]],
[[61, 67, 73, 23, 34],
[41, 3, 69, 79, 48],
[22, 40, 22, 18, 41],
[86, 23, 58, 38, 69],
[98, 60, 70, 71, 3],
[44, 8, 33, 86, 66]],
[[62, 45, 56, 80, 22],
[27, 95, 55, 87, 22],
[42, 17, 48, 96, 65],
[36, 64, 1, 85, 31],
[10, 13, 15, 7, 92],
[27, 74, 31, 91, 60]]])
>>> SPN.shape
(3, 6, 5)
>>> SPN[0].shape
(6, 5)
.. actually, it looks like you may want np.random.uniform(0, 100, (samples, 6, 5)), because you want the elements to be floating point, not integers. Well, it works the same way. :^)
Note that what you did isn't equivalent to np.random.uniform, because you're choosing an array of values between 0 and 1 and then multiplying all of them by a fixed integer. I'm assuming that wasn't actually what you were trying to do, because it's a little unusual; please comment if that is what you actually wanted.

Categories