Finding all diagonals of a matrix [duplicate] - python

I'm looking for a Pythonic way to get all the diagonals of a (square) matrix, represented as a list of lists.
Suppose I have the following matrix:
matrix = [[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]]
Then the large diagonals are easy:
l = len(matrix[0])
print([matrix[i][i] for i in range(l)]) # [-2, -6, 7, 8]
print([matrix[l-1-i][i] for i in range(l-1,-1,-1)]) # [ 2, 5, 2, -1]
But I have trouble coming up with a way to generate all the diagonals. The output I'm looking for is:
[[-2], [9, 5], [3,-6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8],
[2], [3,1], [5, 5, 3], [-2, -6, 7, 8], [9, 2, -4], [3, 8], [-1]]

There are probably better ways to do it in numpy than below, but I'm not too familiar with it yet:
import numpy as np
matrix = np.array(
[[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]])
diags = [matrix[::-1,:].diagonal(i) for i in range(-3,4)]
diags.extend(matrix.diagonal(i) for i in range(3,-4,-1))
print [n.tolist() for n in diags]
Output
[[-2], [9, 5], [3, -6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8], [2], [3, 1], [5, 5, 3], [-2, -6, 7, 8], [9, 2, -4], [3, 8], [-1]]
Edit: Updated to generalize for any matrix size.
import numpy as np
# Alter dimensions as needed
x,y = 3,4
# create a default array of specified dimensions
a = np.arange(x*y).reshape(x,y)
print a
print
# a.diagonal returns the top-left-to-lower-right diagonal "i"
# according to this diagram:
#
# 0 1 2 3 4 ...
# -1 0 1 2 3
# -2 -1 0 1 2
# -3 -2 -1 0 1
# :
#
# You wanted lower-left-to-upper-right and upper-left-to-lower-right diagonals.
#
# The syntax a[slice,slice] returns a new array with elements from the sliced ranges,
# where "slice" is Python's [start[:stop[:step]] format.
# "::-1" returns the rows in reverse. ":" returns the columns as is,
# effectively vertically mirroring the original array so the wanted diagonals are
# lower-right-to-uppper-left.
#
# Then a list comprehension is used to collect all the diagonals. The range
# is -x+1 to y (exclusive of y), so for a matrix like the example above
# (x,y) = (4,5) = -3 to 4.
diags = [a[::-1,:].diagonal(i) for i in range(-a.shape[0]+1,a.shape[1])]
# Now back to the original array to get the upper-left-to-lower-right diagonals,
# starting from the right, so the range needed for shape (x,y) was y-1 to -x+1 descending.
diags.extend(a.diagonal(i) for i in range(a.shape[1]-1,-a.shape[0],-1))
# Another list comp to convert back to Python lists from numpy arrays,
# so it prints what you requested.
print [n.tolist() for n in diags]
Output
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[0], [4, 1], [8, 5, 2], [9, 6, 3], [10, 7], [11], [3], [2, 7], [1, 6, 11], [0, 5, 10], [4, 9], [8]]

I came across another interesting solution to this issue.
The row, column, forward, and backward diagonal can all be immediately discovered by looking at a combination of x and y.
Column = x Row = y F-Diag = x+y B-Diag = x-y B-Diag` = x-y-MIN
| 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2
--|--------- --|--------- --|--------- --|--------- --|---------
0 | 0 1 2 0 | 0 0 0 0 | 0 1 2 0 | 0 1 2 0 | 2 3 4
1 | 0 1 2 1 | 1 1 1 1 | 1 2 3 1 |-1 0 1 1 | 1 2 3
2 | 0 1 2 2 | 2 2 2 2 | 2 3 4 2 |-2 -1 0 2 | 0 1 2
From the diagram you can see that each diagonal and axis is uniquely identifiable using these equations. Take each unique number from each table and create a container for that identifier.
Note that the backward diagonals have been offset to start at a zero index, and that the length of forward diagonals is always equal to the length of backward diagonals.
test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
max_col = len(test[0])
max_row = len(test)
cols = [[] for _ in range(max_col)]
rows = [[] for _ in range(max_row)]
fdiag = [[] for _ in range(max_row + max_col - 1)]
bdiag = [[] for _ in range(len(fdiag))]
min_bdiag = -max_row + 1
for x in range(max_col):
for y in range(max_row):
cols[x].append(test[y][x])
rows[y].append(test[y][x])
fdiag[x+y].append(test[y][x])
bdiag[x-y-min_bdiag].append(test[y][x])
print(cols)
print(rows)
print(fdiag)
print(bdiag)
Which will print
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
[[1], [2, 4], [3, 5, 7], [6, 8, 10], [9, 11], [12]]
[[10], [7, 11], [4, 8, 12], [1, 5, 9], [2, 6], [3]]
Using a defaultdict and a lambda, this can be generalized further:
from collections import defaultdict
def groups(data, func):
grouping = defaultdict(list)
for y in range(len(test)):
for x in range(len(test[y])):
grouping[func(x, y)].append(data[y][x])
return list(map(grouping.get, sorted(grouping)))
test = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
cols = groups(test, lambda x, y: x)
rows = groups(test, lambda x, y: y)
fdiag = groups(test, lambda x, y: x + y)
bdiag = groups(test, lambda x, y: x - y)

Start with the diagonals that slope up-and-right.
If (x,y) is a rectangular coordinate inside the matrix, you want to transform to/from a coordinate scheme (p,q), where p is the number of the diagonal and q is the index along the diagonal. (So p=0 is the [-2] diagonal, p=1 is the [9,5] diagonal, p=2 is the [3,-6,3] diagonal, and so on.)
To transform a (p,q) into an (x,y), you can use:
x = q
y = p - q
Try plugging in values of p and q to see how this is working.
Now you just loop... For p from 0 to 2N-1, and q from max(0, p-N+1) to min(p, N-1). Transform p,q to x,y and print.
Then for the other diagonals, repeat the loops but use a different transformation:
x = N - 1 - q
y = p - q
(This effectively just flips the matrix left-right.)
Sorry I did not actually code this in Python. :-)

This is for Moe, who asked a similar question.
I start off by making simple functions to copy rows or columns of any rectangular matrix.
def get_rows(grid):
return [[c for c in r] for r in grid]
def get_cols(grid):
return zip(*grid)
With these two functions I then get the diagonals by adding an increasing/decreasing buffer to the start/end of each row. I then get the columns of this buffered grid, then remove the buffer on each column afterwards. ie)
1 2 3 |X|X|1|2|3| | | |1|2|3|
4 5 6 => |X|4|5|6|X| => | |4|5|6| | => [[7],[4,8],[1,5,9],[2,6],[3]]
7 8 9 |7|8|9|X|X| |7|8|9| | |
.
def get_backward_diagonals(grid):
b = [None] * (len(grid) - 1)
grid = [b[i:] + r + b[:i] for i, r in enumerate(get_rows(grid))]
return [[c for c in r if c is not None] for r in get_cols(grid)]
def get_forward_diagonals(grid):
b = [None] * (len(grid) - 1)
grid = [b[:i] + r + b[i:] for i, r in enumerate(get_rows(grid))]
return [[c for c in r if c is not None] for r in get_cols(grid)]

I ended up reinventing this wheel recently. Here's an easy-to-reuse/extend method to find the diagonals in a square list-of-lists:
def get_diagonals(grid, bltr = True):
dim = len(grid)
assert dim == len(grid[0])
return_grid = [[] for total in xrange(2 * len(grid) - 1)]
for row in xrange(len(grid)):
for col in xrange(len(grid[row])):
if bltr: return_grid[row + col].append(grid[col][row])
else: return_grid[col - row + (dim - 1)].append(grid[row][col])
return return_grid
Assuming list indices:
00 01 02 03
10 11 12 13
20 21 22 23
30 31 32 33
then setting bltr = True (the default), returns the diagonals from bottom-left to top-right, i.e.
00 # row + col == 0
10 01 # row + col == 1
20 11 02 # row + col == 2
30 21 12 03 # row + col == 3
31 22 13 # row + col == 4
32 23 # row + col == 5
33 # row + col == 6
setting bltr = False, returns the diagonals from bottom-left to top-right, i.e.
30 # (col - row) == -3
20 31 # (col - row) == -2
10 21 32 # (col - row) == -1
00 11 22 33 # (col - row) == 0
01 12 23 # (col - row) == +1
02 13 # (col - row) == +2
03 # (col - row) == +3
Here's a runnable version using OP's input matrix.

I guess there's an easier way to do this now. (But only use this if you are already familiar with the above answers).
from collections import defaultdict
There's this method called defaultdict which is imported from the collections module, is used to create dictionaries if you don't know the key you are going to have.
We use this in these situations:
If you don't know the key but want to assign some value to a particular key.
Normal dictionary raises keyerror if the key is not present in the dictionary. But this won't ( you can assign some function to it if you want)
After Importing, you can run the following code and check.
rows,cols = 3,3
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
diagonal1 = defaultdict(list) # For the top right to bottom left
diagonal2 = defaultdict(list) # For the top left to bottom right
for i in range(rows):
for j in range(cols):
diagonal1[i-j].append(matrix[i][j])
diagonal2[i+j].append(matrix[i][j])
print(diagonal1,'\n',diagonal2)
The list parameter will create a list of values for that particular key.
The output is as follows:
defaultdict(<class 'list'>, {0: [1, 5, 9], -1: [2, 6], -2: [3], 1: [4, 8], 2: [7]})
defaultdict(<class 'list'>, {0: [1], 1: [2, 4], 2: [3, 5, 7], 3: [6, 8], 4: [9]})
Now you can use both the diagonals as you want.
To know more about defaultdict use this link :
Click here

This only works for matricies of equal width and height.
But it also doesn't rely on any third parties.
matrix = [[11, 2, 4],[4, 5, 6],[10, 8, -12]]
# only works for diagnoals of equal width and height
def forward_diagonal(matrix):
if not isinstance(matrix, list):
raise TypeError("Must be of type list")
results = []
x = 0
for k, row in enumerate(matrix):
# next diag is (x + 1, y + 1)
for i, elm in enumerate(row):
if i == 0 and k == 0:
results.append(elm)
break
if (x + 1 == i):
results.append(elm)
x = i
break
return results
print 'forward diagnoals', forward_diagonal(matrix)

Code based on Nemo's answer above:
def print_diagonals(matrix):
n = len(matrix)
diagonals_1 = [] # lower-left-to-upper-right diagonals
diagonals_2 = [] # upper-left-to-lower-right diagonals
for p in range(2*n-1):
diagonals_1.append([matrix[p-q][q] for q in range(max(0, p - n + 1), min(p, n - 1) + 1)])
diagonals_2.append([matrix[n-p+q-1][q] for q in range(max(0, p - n + 1), min(p, n - 1) + 1)])
print("lower-left-to-upper-right diagonals: ", diagonals_1)
print("upper-left-to-lower-right diagonals: ", diagonals_2)
print_diagonals([
[1, 2, 1, 1],
[1, 1, 4, 1],
[1, 3, 1, 6],
[1, 7, 2, 5],
])
lower-left-to-upper-right diagonals: [[1], [1, 2], [1, 1, 1], [1, 3, 4, 1], [7, 1, 1], [2, 6], [5]]
upper-left-to-lower-right diagonals: [[1], [1, 7], [1, 3, 2], [1, 1, 1, 5], [2, 4, 6], [1, 1], [1]]

Pythonic approach
For a pure Python implementation I would suggest to work in 1D.
W, H = len(mat[0]), len(mat)
idx = range(W-1) + range(W-1, W*H, W)
rng = range(1, W) + range(H, 0, -1)
rng = map(lambda x: x if (x < min(W, H)) else min(W, H), rng)
dia = [[i + (W-1) * m for m in xrange(r)] for i, r in zip(idx, rng)]
Here dia returns a list of indices for each diagonal. To retrieve the corresponding values:
arr = [e for row in mat for e in row] #Flatten the matrix
for d in dia:
print [arr[e] for e in d][::-1]
[-2]
[9, 5]
[3, -6, 3]
[-1, 2, 5, 2]
[8, 7, 1]
[-4, 3]
[8]
If you want to return the values in the opposite direction:
arr2 = [e for row in zip(*mat[::-1]) for e in row] #Flatten and rotate the matrix by 90°
for d in dia[::-1]:
print [arr2[e] for e in d]
[2]
[3, 1]
[5, 5, 3]
[-2, -6, 7, 8]
[9, 2, -4]
[3, 8]
[-1]
Numpy approach
tril = [np.flip(np.fliplr(mat).diagonal(n)) for n in xrange(mat.shape[0])][::-1]
trir = [np.flipud(mat).diagonal(n) for n in xrange(1, mat.shape[0])]
dia = tril + trir
[array([-2]),
array([9, 5]),
array([ 3, -6, 3]),
array([-1, 2, 5, 2]),
array([8, 7, 1]),
array([-4, 3]),
array([8])]

Try this :
import numpy as np
matrix = [[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]]
matrix = np.array(matrix)
matrix = np.flipud(matrix)
a = matrix.shape[0]
list_ = [np.diag(matrix, k=i).tolist() for i in range(-a+1,a)]
print(list_)
Output :
[[-2], [9, 5], [3, -6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8]]

Try using dict
mat = [[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]]
dct = dict()
for i in range(len(mat)-1,-len(mat[0]),-1):
dct[i] = []
for i in range(len(mat)):
for j in range(len(mat[0])):
dct[i-j].append(mat[i][j])
print(dct)
Output:
{3: [-1], 2: [3, 8], 1: [9, 2, -4], 0: [-2, -6, 7, 8], -1: [5, 5, 3], -2: [3, 1], -3: [2]}

Using itertools
matrix = [[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]]
import itertools as it
def show_diagonals(alist):
# get row/col lenght
a = len(alist)
# creating a fliped matrix
rlist = []
for r in alist:
new = r.copy()
new.reverse()
rlist.append(new)
flatten_list = list(it.chain.from_iterable(alist))
flatten_rlist = list(it.chain.from_iterable(rlist))
b = len(flatten_list)
first_diag = list(it.islice(flatten_list, 0, b+1, a+1))
second_diag = list(it.islice(flatten_rlist, 0, b+1, a+1))
return first_diag, second_diag
a, b = show_diagonals(matrix)

Using some numpy-fu to get the main diagonal:
import numpy as np
r = np.arange(36)
r.resize((6, 6))
print(r)
r = r.reshape(len(r)**2)[::len(r)+1]
print(r)
Prints:
[[ 0 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]]
[ 0 7 14 21 28 35]

From here : np.Diagonal
np.diagonal(matrix)

Related

How to return an array of anti-diagonals or diagonals of given N*N square matrix

I need to print the diagonals of matrix
if diagonals then i to i + 1 and j to j -1
if anti-diagonals then i to i-1 and j to j + 1
matrix is
A = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
Expected out is
1 0 0
2 4 0
3 5 7
6 8 0
9 0 0
Code is below for diagonal
def print_diagonal(A):
m = len(A)
n = len(A[0])
#result = [[0 for i in range(m)] for i in range(n)]
result = []
for k in range(m):
i = k
j = 0
while i>=0:
result.append(A[i][j])
i = i -1
j = j + 1
for k in range(1,n):
i = m - 1
j = k
while (j <= n-1):
result.append(A[i][j])
i = i -1
j = j + 1
return result
A = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print_diagonal(A)
My output is
[[0, 0, 0], [0, 0, 0], [0, 0, 0], 1, 4, 2, 7, 5, 3, 8, 6, 9]
The numbers in my output is matching correctly, but its not coming in order as expected
The order of iteration was wrong, you were going from the bottom-left to the top-right each time. Also the length to fill with zeroes is based on the minimum of n and m since n or m can only be decreased at most min(n, m) times before it reaches 0.
def print_diagonal(A):
n = len(A)
m = len(A[0])
result = []
length = min(n, m)
# Top-left to top-right
for k in range(m):
result.append([0 for _ in range(length)])
i = 0
j = k
while j >= 0 and i < n:
result[-1][i] = A[i][j]
i = i + 1
j = j - 1
# Top-right to bottom-right
for k in range(1,n):
result.append([0 for _ in range(length)])
i = k
j = m - 1
while j >= 0 and i < n:
result[-1][i - k] = A[i][j]
i = i + 1
j = j - 1
return result
A = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print(print_diagonal(A))
prints out [[1, 0, 0], [2, 4, 0], [3, 5, 7], [6, 8, 0], [9, 0, 0]].
Just create a matrix of 0s and then fill the diagonals one by one:
def print_diagonal(A):
m = len(A)
result = [[0]*m for _ in range(2*m-1)]
for i in range(m):
for k in range(i+1):
result[i][k] = A[k][i-k]
for i in range(0,m-1):
for k in range(m-1-i):
result[m+i][k] = A[i+k+1][m-1-k]
return result
The following algorithm takes advantage of the fact that the diagonals of a matrix are simply every (n-1)th element when iterating the columns of an nxn matrix from left to right and top to bottom and restricting the result to one element per row. I wrote the programme for a similar case, but omitting the leading zeros. However, I added the necessary rows to add fills. To work with nxm matrices as well, some adjustments are necessary.
def print_diagonal(A, zeros=True):
Al = np.concatenate(A)
d = max(np.array(A).shape)
results = []
for start in range(0, 2 * d - 1):
line = []
row = max(start - d + 1, 0)
p = start if row == 0 else (row + 1) * d - 1
while np.floor(p / d) == row and p < len(Al):
line.append(Al[p])
p = p + d - 1
row += 1
if zeros and len(line) < d:
line = [0] * (d - len(line)) + line if start < d else line + [0] * (d - len(line))
results.append(line)
return results
Given your example, the algorith has the following output:
print_diagonal(A, zeros=False) # [[1], [2, 4], [3, 5, 7], [6, 8], [9]]
print_diagonal(A, zeros=True) # [[0, 0, 1], [0, 2, 4], [3, 5, 7], [6, 8, 0], [9, 0, 0]]
You could make a function that extracts the first row and last columns (outer edge) to form the first elements in the diagonals, then repeat the process iteratively on the remaining sub-matrix padding with leading&trailing zeroes and adding elements to the diagonals at each iteration:
def diagsDownLeft(M):
diags,pad = [],[]
while any(M):
edge = [*M[0][:-1] ,*next(zip(*map(reversed,M)))]
M = [r[:-1] for r in M[1:]]
diags.append(pad+edge+pad)
pad.append(0)
return [*map(list,zip(*diags))]
Output (for any rectangular matrix):
A = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print(diagsDownLeft(A))
[[1, 0, 0], [2, 4, 0], [3, 5, 7], [6, 8, 0], [9, 0, 0]]
B = [[1, 2, 3, 10],
[4, 5, 6, 11],
[7, 8, 9, 12]]
print(diagsDownLeft(B))
[[1, 0, 0], [2, 4, 0], [3, 5, 7], [10, 6, 8], [11, 9, 0], [12, 0, 0]]
C = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]]
print(diagsDownLeft(C))
[[1, 0, 0], [2, 4, 0], [3, 5, 7], [6, 8, 10], [9, 11, 0], [12, 0, 0]]
How it works (visually):
1 2 3 1 2 3
4 5 6 x x 6
7 8 9 x x 9 ==> 1 2 3 6 9 (pad = none)
| | | | |
4 5 4 5 | | | | |
7 8 x 8 ==> 0 4 5 8 0 (pad = 0)
| | | | |
7 7 ==> 0 0 7 0 0 (pad = 0,0)
| | | | |
\ \ \ \ \__ [9,0,0] zipped
\ \ \ \___ [6,8,0]
\ \ \____ [3,5,7]
\ \_____ [2,4,0]
\______ [1,0,0]
If you also need the other diagonals:
def diagsDownRight(M):
diags,pad = [],[]
while any(M):
edge = [*next(zip(*reversed(M))), *M[0][1:]]
M = [r[1:] for r in M[1:]]
diags.append(pad+edge+pad)
pad.append(0)
return [*map(list,zip(*diags))]
Output:
print(diagsDownRight(A))
[[7, 0, 0], [4, 8, 0], [1, 5, 9], [2, 6, 0], [3, 0, 0]]
print(diagsDownRight(B))
[[7, 0, 0], [4, 8, 0], [1, 5, 9], [2, 6, 12], [3, 11, 0], [10, 0, 0]]
print(diagsDownRight(C))
[[10, 0, 0], [7, 11, 0], [4, 8, 12], [1, 5, 9], [2, 6, 0], [3, 0, 0]]
And opposite directions:
def diagsUpRight(M):
diags,pad = [],[]
while any(M):
edge = [*next(zip(*M)), *M[-1][1:]]
M = [r[1:] for r in M[:-1]]
diags.append(pad+edge+pad)
pad.append(0)
return [*map(list,zip(*diags))]
def diagsUpLeft(M):
diags,pad = [],[]
while any(M):
edge = [*M[-1][:-1],*next(zip(*map(reversed,M[::-1])))]
M = [r[:-1] for r in M[:-1]]
diags.append(pad+edge+pad)
pad.append(0)
return [*map(list,zip(*diags))]

How to get the indexes of the greatest N values greater than a threshold in Numpy?

For a project I need to be able to get, from a vector with shape (k, m), the indexes of the N greatest values of each row greater than a fixed threshold.
For example, if k=3, m=5, N=3 and the threshold is 5 and the vector is :
[[3 2 6 7 0],
[4 1 6 4 0],
[7 10 6 9 8]]
I should get the result (or the flattened version, I don't care) :
[[2, 3],
[2],
[1, 3, 4]]
The indexes don't have to be sorted.
My code is currently :
indexes = []
for row, inds in enumerate(np.argsort(results, axis=1)[:, -N:]):
for index in inds:
if results[row, index] > threshold:
indexes.append(index)
but I feel like I am not using Numpy to its full capacity.
Does anybody know a better and more elegant solution ?
How about this method:
import numpy as np
arr = np.array(
[[3, 2, 6, 7, 0],
[4, 1, 6, 4, 0],
[7, 10, 6, 9, 8]]
)
t = 5
n = 3
sorted_idxs = arr.argsort(1)[:, -n:]
sorted_arr = np.sort(arr, 1)[:, -n:]
item_nums = np.cumsum((sorted_arr > t).sum(1))
masked_idxs = sorted_idxs[sorted_arr > t]
idx_lists = np.split(masked_idxs, item_nums)
output:
[array([2, 3]), array([2]), array([4, 3, 1])]

How to take input in list in python in which all input are in one line?

s=input().split()
n,m=int(s[0]),int(s[1])
arr=(int(i) for i in s[2:])
input like
3 4 1 2 3 4 5 6 7 8 9 10 11 12
in this first two are row and column
and remaining are 2d list element
output be like
[[1,2,3,4],[5,6,7,8],[9,10,11,12]]
Try this way:
s = input().split()
s = list(map(int, s))
[s[2 + (i - 1) * s[1]: 2 + i * s[1]] for i in range(1, s[0] + 1)]
Output:
[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
Following your code:
arr = list(arr)
blocks = [arr[i:i+m] for i in range(0, len(arr), m)]
assert len(blocks) == n
If numpy is an option, you can simply reshape the 1D array:
s = input().split()
n,m = int(s[0]),int(s[1])
arr = numpy.array(s[2:], dtype=int).reshape((n,m))
You can output it as a numpy array
print(arr)
# array([[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12]])
or convert it to a nested list
print(arr.tolist())
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

How to split a nested list of ints into a a list

lst = [[1, 5],
[2, 2]
this is my nested list, I need to make a list of the points of this:
output = [[1, 5, 2, 2]
here is my attempt at this which works for this case but fails if I have an example where the row length is 6 or greater than 4
new_lst = []
for x in range(len(lst)):
for y in range(0, len(lst[x]), 2):
new_lst.append([lst[x][y],lst[x][y+1]])
counter_a = 0
counter_b = 1
output = []
while counter_b - 4 <= len(lst):
output.append(new_lst[counter_a] + new_lst[counter_a + 2])
output.append(new_lst[counter_b] + new_lst[counter_b + 2])
counter_a += 4
counter_b += 4
print(output)
How about this? This is general for all lists with size nxm where n and m are even numbers. The logic is to iterate with a step of 2 in both row and column, then take the block of 2x2 elements and append it to the output list.
lst = [[1, 6, 5, 6],
[2, 5, 6, 8],
[7, 2, 8, 1],
[4, 4, 7, 3]]
output = []
for j in range(0, len(lst), 2):
for i in range(0, len(lst[0]), 2):
output.append([lst[j][i], lst[j][i+1], lst[j+1][i], lst[j+1][i+1]])
output : [[1, 6, 2, 5], [5, 6, 6, 8], [7, 2, 4, 4], [8, 1, 7, 3]]
Try using:
print([x for i in list(zip(*[[i[:2], i[2:]] for i in lst])) for x in [i[0] + i[1], i[2] + i[3]]])

matrix 90 degree clockwise rotation using list in python

I'm trying to rotate a 3*3 matrix clockwise 90 degrees in python.
I've identified that element at [ i ][ j ] goes to [ j ][ new_i ].
Here new_i depends upon the previous i, so i made a function for it called circular subtraction.
if i is 0 then new_i is 2
if i is 1 then new_i is 1
if i is 2 then new_i is 0
after execution, it gave me unexpected results.
I've printed everything that is happening in each iteration.
I am unable to figure out how some elements are getting replaced with different ones.
'''
1 2 3 7 4 1
4 5 6 rotate 90 degrees 8 5 2
7 8 9 9 6 3
'''
def circular_subtraction(i):
new_i = i
if(i==0):
new_i = 2
elif(i==1):
new_i = 1
elif(i==2):
new_i = 0
return new_i
def rotate_clock(matrix):
new_matrix = matrix
for i in range(len(matrix)):
for j in range(len(matrix)):
new_i = circular_subtraction(i)
new_matrix[j][new_i] = matrix[i][j]
print("New element added from {},{} to {},{} ::: {} to {}".format(i+1,j+1,j+1,new_i+1,matrix[i][j],new_matrix[j][new_i]))
for each_row in new_matrix:
print(each_row)
matrix = [[1,2,3],[4,5,6],[7,8,9]]
print("Length of the matrix : ",len(matrix))
for each_row in matrix:
print(each_row)
print()
matrix = rotate_clock(matrix)
the input matrix was
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
The expected result was:
[7, 4, 1]
[8, 5, 2]
[9, 6, 3]
Result is:
[7, 4, 1]
[2, 5, 2]
[1, 2, 1]
You could do something like this:
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
rotated = [list(reversed(col)) for col in zip(*matrix)]
for row in rotated:
print(*row)
Output
7 4 1
8 5 2
9 6 3
The for col in zip(*matrix) gets the column of the matrix, once you have the columns you need to reverse them using list(reversed(col)), then use a list comprehension to put all together:
rotated = [list(reversed(col)) for col in zip(*matrix)]
The above list comprehension is equivalent to the following less pythonic for loop:
rotated = []
for col in zip(*matrix):
rotated.append(list(reversed(col)))
Further
Documentation on zip, reversed and list.
The notation *matrix is known as tuple unpacking, more here.
You can use the numpy rot90 function for this: np.rot90
mat = [[1, 2, 3], [4,5,6,], [7,8,9]]
np.rot90(mat, k=1, axes=(1,0))
k - indicates number of rotations
axes - indicates the direction of rotation
Output
array([[7, 4, 1],
[8, 5, 2],
[9, 6, 3]])
The issue in your code
The code is missing indents (but assuming they are correct)
the line :
new_matrix = matrix
assigns a new reference to the matrix variable.
In python the default isn't copy by value.
You can use deep copy function : copy.deepcopy(x[, memo])
import copy
def rotate_clock(matrix):
new_matrix = copy.deepcopy(matrix)
OR
def rotate_clock(matrix):
new_matrix = [row[:] for row in matrix]
Otherwise, each change you make to new_matrix is being done in the original matrix as well. (since new_matrix is just a reference to matrix)
In very simple python, this code works:
def rotate_matrix(a):
b = []
i = len(a)-1
while i>=0:
for j in range(0, len(a)):
if (len(b) < (j+1)):
b.append([a[i][j]])
else:
b[j].append(a[i][j])
i -= 1
return b
I printed b. That looked like this:
[[7, 4, 1], [8, 5, 2], [9, 6, 3]]
numpy.rot90 could come handy as well:
import numpy as np
a = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
a_rot = np.rot90(a, k=3).tolist()
for row in a_rot:
print(row)
Output
[7, 4, 1]
[8, 5, 2]
[9, 6, 3]
A general method to rotate the Matrix, irrespective of shape.
import numpy as np
A=np.array([[1, 2, 3, 33], [4, 5, 6, 66], [7, 8, 9, 99]])
A
array([[ 1, 2, 3, 33],
[ 4, 5, 6, 66],
[ 7, 8, 9, 99]])
rotated_A=np.zeros((len(A[0]),len(A)))
for i in range(len(A)):
for j in range(len(A[0])):
rotated_A[j][len(A)-1-i]=A[i][j]
rotated_A
array([[ 7., 4., 1.],
[ 8., 5., 2.],
[ 9., 6., 3.],
[ 99., 66., 33.]])
A simple solution to rotate any matrix would be
import copy
def rotateImage(a):
out = copy.deepcopy(a)
x = 0;
y = 0;
for i in a:
l = len(i)
for j in i:
out[y][x+l-1] = j
y += 1
if(y == l):
y=0
x -= 1
return(out)
Generally all solution are for square matrix,
Below one will for any matrix:
mat = [[1, 2, 3, 5],
[5, 6, 7, 1],
[9, 10, 11, 8],
[4, 7, 4, 3]]
def rotate_matrix(a):
b = []
i = len(a)-1
while i>=0:
if len(a) == len(a[-1]):
for j in range(0, len(a)):
print(j)
if (len(b) < (j+1)):
b.append([a[i][j]])
print(b)
else:
b[j].append(a[i][j])
print(b)
i -= 1
else:
for j in range(0, len(a)+1):
print(j)
if (len(b) < (j+1)):
b.append([a[i][j]])
print(b)
else:
b[j].append(a[i][j])
print(b)
i -= 1
return b
print(rotate_matrix(mat))

Categories