Finding Non-Overlapping Sub Matrices in Python - python

Non-Overlapping Sub Matrices
I'm stuck at finding a way to get only the non-overlapping sub matrices.
My code below finds all the sub matrices.
Code:
n = 4
matrix = [[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
k = 2 #Finding 2x2 submatrices
t=[]
for i in range(n-k+1):
for j in range(n-k+1):
l=[]
for x in range(i,i+k):
for y in range(j,j+k):
if x==i or x==i+k-1 or y==j or y==j+k-1:
l.append(matrix[x][y])
t.append(l)
So if I print t I get:
for i in t:
print(i)
O/P:
[1, 2, 5, 6]
[2, 3, 6, 7]
[3, 4, 7, 8]
[5, 6, 9, 10]
[6, 7, 10, 11]
[7, 8, 11, 12]
[9, 10, 13, 14]
[10, 11, 14, 15]
[11, 12, 15, 16]
But the o/p I want is:
[1,2,5,6]
[3,4,7,8]
[9,10,13,14]
[11,12,15,16]

When you're looping over elements in range(n-k+1), you're calling range with arguments start=0, stop=n-k+1=3, step=1, which results in the array [0, 1, 2]. Using this as your starting point is the problem.
To ensure your result includes only mutually exclusive elements, change the step argument to k:
In [7]: t = []
...: for i in range(0, n, k):
...: for j in range(0, n, k):
...: t.append([
...: matrix[i+ii][j+jj]
...: for ii in range(k) for jj in range(k)])
In [8]: t
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]

Related

Split list into chunks with repeats between chunks

I have an array of elements, for example r = np.arange(15).
I'm trying to split this array into chunks of consecutive elements, where each chunk (except maybe the last one) has size M and there are m repeating elements between each pair of chunks.
For example: split_to_chunks(np.arange(15), M=5, m=1) should yield four lists:
[0, 1, 2, 3, 4], [4, 5, 6, 7, 8], [8, 9, 10, 11, 12], [12, 13, 14]
Obviously this can be done iteratively, but I'm looking for a more "pythonic" (and faster) way of doing this.
Something like this with list comprehension:
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
Example:
import math
l = list(range(15))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
m, M = 2, 5
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [[0, 1, 2, 3, 4],
# [3, 4, 5, 6, 7],
# [6, 7, 8, 9, 10],
# [9, 10, 11, 12, 13],
# [12, 13, 14]]
m, M = 3, 5
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [[0, 1, 2, 3, 4],
# [2, 3, 4, 5, 6],
# [4, 5, 6, 7, 8],
# [6, 7, 8, 9, 10],
# [8, 9, 10, 11, 12],
# [10, 11, 12, 13, 14]]
l = range(5)
m, M = 2, 3
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [range(0, 3), range(1, 4), range(2, 5)]
Explanation:
Chunk i starts at index i*(M-m) and ends M positions later at index i*(M-m) + M.
chunk index starts ends
-------------------------------------------------
0 0 M
1 M-m M-m+M = 2*M-m
2 2*M-m-m=2(M-m) 2*(M-m)+M = 3M-2m
...
Now the problem is to determine how many chunks.
At each step we increase the initial index by M-m, so to count the total number of steps we need to divide the length of the list by M-m (but after subtracting m because in the first chunk we're not skipping anything).
Finally, use the ceiling function to add the last incomplete chunk in case the division is not exact.
This should do the job:
def split_to_chunks(r, M=5, m=1):
return [r[i*(M-m): (i+1)*M-i*m] for i in range(len(r)//(M-m)+1) if i*(M-m) < len(r)]
Explanation: in a list comprehension loop through the indexes in the way explained in the question. Each start of a chunk will start at i*(M-m) and end at (i+1)*M-i*m. Finally if the start of the chunk is after the length of the array it will skip it.

Counter of iterations in comprehension list with nested loops

X =[
[2, 10, 3, 1],
[4, 2, 0, 7],
[6, 9, 6, 9],
[1, 3, 4, 5]
]
Y =[
[4, 2, 3, 1],
[9, 9, 9, 7],
[1, 2, 3, 4],
[5, 6, 7, 8]
]
So i have to add these two matrixes. I solved it, but i have to create counter of iterations.
def summary(X, Y):
return [X[i][j] + Y[i][j] for j in range(len(X[0]))] for i in range(len(X))]
I was searching for the solution and also trying to figure it out by myself, but i have no idea what to do if there is already two loops inside this list.
Desired output:
[6, 12, 6, 2]
[13, 11, 9, 14]
[7, 11, 9, 13]
[6, 9, 11, 13]
number of iterations: 16
import numpy as np
np.reshape(np.array(X) + np.array(Y), (4,4)).tolist()
# output
# [[6, 12, 6, 2], [13, 11, 9, 14], [7, 11, 9, 13], [6, 9, 11, 13]]
You can use a nested list comprehension to do element-wise addition
>>> [[i+j for i,j in zip(a, b)] for a,b in zip(X,Y)]
[[6, 12, 6, 2],
[13, 11, 9, 14],
[7, 11, 9, 13],
[6, 9, 11, 13]]
In general (unless this is a learning exercise) I'd suggest using numpy as it is significantly faster for linear algebra operations
>>> import numpy as np
>>> np.array(X) + np.array(Y)
array([[ 6, 12, 6, 2],
[13, 11, 9, 14],
[ 7, 11, 9, 13],
[ 6, 9, 11, 13]])
If you wanted to do this "by hand" while keeping a counter you could do something like
def add(X,Y):
ops = 0
result = []
for a, b in zip(X,Y):
row = []
for i,j in zip(a, b):
row.append(i + j)
ops += 1
result.append(row)
print('number of iterations: {}'.format(ops))
return result
>>> add(X, Y)
number of iterations: 16
[[6, 12, 6, 2], [13, 11, 9, 14], [7, 11, 9, 13], [6, 9, 11, 13]]

Splitting list based on difference between consecutive elements

I found this question that is related to mine. In that question a specific case is treated, and that's splitting a list of integers when a difference of more than 1 is present between consecutive elements.
I was wondering: is there way to make this work for a difference of N, a parameter? Namely, suppose we have this list:
[1,2,3,6,8,10,14,15,17,20]
For N=2, the output should be:
[[1,2,3], [6,8,10], [14,15,17], [20]]
For N=3, the output should be:
[[1,2,3,6,8,10], [14,15,17,20]]
And for N=4, the output should be the same input list.
I did it like this:
from itertools import takewhile
input_list = [1,2,3,6,8,10,14,15,17,20]
N = 4
def fun(l, N, output=[]):
if len(l):
output.append([x[1] for x in takewhile(lambda x: x[1]-x[0]<=N,
zip([l[0]]+l, l))])
fun(l[len(output[-1]):], N, output)
return output
fun(input_list, N)
But I don't really like it: it's unreadable. Something stylish as a one-liner or something pretty pythonic would be appreciated!
Two lines with list-comprehension:
def split_list(l, n):
index_list = [None] + [i for i in range(1, len(l)) if l[i] - l[i - 1] > n] + [None]
return [l[index_list[j - 1]:index_list[j]] for j in range(1, len(index_list))]
test:
example = [1, 2, 3, 6, 8, 10, 14, 15, 17, 20]
for i in range(2,5):
print(split_list(example, i))
# [[1, 2, 3], [6, 8, 10], [14, 15, 17], [20]]
# [[1, 2, 3, 6, 8, 10], [14, 15, 17, 20]]
# [[1, 2, 3, 6, 8, 10, 14, 15, 17, 20]]
def spacer(data, n=1):
set(data)
output = [[data[0]]]
for i in data[1:]:
if i - output[-1][-1] > n:
output.append([i])
else:
output[-1].append(i)
return output
data = [1, 2, 3, 6, 8, 10, 14, 15, 17, 20]
for i in range(1, 4):
print("N={}, {}".format(i, spacer(data, n=i)))
output:
N=1, [[1, 2, 3], [6], [8], [10], [14, 15], [17], [20]]
N=2, [[1, 2, 3], [6, 8, 10], [14, 15, 17], [20]]
N=3, [[1, 2, 3, 6, 8, 10], [14, 15, 17, 20]]

Generate a 2D array with counter using list comprehension

I am new to python and learning more about list comprehensions. I want to generate a simple 2D array like this:
Expected List:
[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]
What I have tried:
[[j for j in range(6)] for _ in range(6)]
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
Maybe i need some kind of counter instead of j inside the second loop, however doing count += 1 is not allowed here and will give a syntax error.
Simple solution using range(start, stop[, step]) function with step argument:
result = [list(range(i, i+4)) for i in range(1, 16, 4)]
print(result)
The output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
For a positive step, the contents of a range r are determined by the
formula r[i] = start + step*i where i >= 0 and r[i] < stop.
[[a for a in range(1+(4*b), 5+(4*b))] for b in range(0, 4)]
this uses the same list comprehension.
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Using Nested list comprehensions :
[[j+i*4 for j in range(1,5)] for i in range(4)]
Output :
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Do your derivation by creating a for loop:
l = []
n = 4
for i in range(3):
l2 = [j + (n*i) for j in range(n)]
l.append(l2)
Then convert that to a list comprehension:
x = [[j + (n*i) for j in range(n)] for i in range(3)]
Output:
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
You may also use get_chunks() function of utilspie library:
>>> from utilspie.iterutils import get_chunks
>>> list(get_chunks(range(1, 17), 4))
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Note: utilspie is not a built-in libary. You have to install it explicitly from pip via doing:
sudo pip install utilspie

Efficient way to find index of elements in a large list of integers starting with max to min elements

I have a large list of integers unsorted, numbers might be duplicated. I would like to create another list which is a list of sub-lists of indexes from the first list starting with max element to min, in decreasing order.
For example, if I have a list like this:
list = [4, 1, 4, 8, 5, 13, 2, 4, 3, 7, 14, 4, 4, 9, 12, 1, 6, 14, 10, 8, 6, 4, 11, 1, 2, 11, 3, 9]
The output should be:
indexList = [[10, 17], [5], [14], [22, 25], [18], [13, 27], [3, 19], [9], [16, 20], [4], [0, 2, 7, 11, 12, 21], [8, 26], [6, 24], [1, 15, 23]]
where, [10, 17] is the index of where '14' is present and so on...
Shared my code below. Profiling it using cProfile for a list of around 9000 elements takes around ~6 seconds.
def indexList(list):
# List with sorted elements
sortedList = sorted(list, reverse = True)
seen = set()
uSortedList = [x for x in sortedList if x not in seen and not seen.add(x)]
indexList = []
for e in uSortedList:
indexList.append([i for i, j in enumerate(list) if j == e])
return indexList
Here you go:
def get_list_indices(ls):
indices = {}
for n, i in enumerate(ls):
try:
indices[i].append(n)
except KeyError:
indices[i] = [n]
return [i[1] for i in sorted(indices.items(), reverse=True)]
test_list = [4, 1, 4, 8, 5, 13, 2, 4, 3, 7, 14, 4, 4, 9, 12, 1, 6, 14, 10, 8, 6, 4, 11, 1, 2, 11, 3, 9]
print(get_list_indices(test_list))
Based on some very basic testing, it is about twice as fast as the code you posted.

Categories