How can I speed-up a matrix rotation by 90 degrees clockwise? - python

Rotate a square matrix by 90 degree with O(1) extra space. I have used Python to solve it. I wanted to know if I can improve my code further.
def rotate_by_90(m):
# unpacking arguments with zip(*) in reverse with [ : :-1]
tuples = zip(*m[::-1])
# flattening tuples to list with [list(i)]
return [list(i) for i in tuples]
def makeMatrix(array, size):
# validating size of matrix for given array
if (size**2!=len(array)):
return -1
# make sub array of length size using array slicing
else:
matrix = [array[i:i+size] for i in range(0, len(array), size)]
return rotate_by_90(matrix)
arr = [1,2,3,4]
dimension = 2
result = makeMatrix(arr, dimension)
# Original Matrix: [[1, 2], [3, 4]]
# Result: [[3, 1], [4, 2]]

No external libraries or zip needed. Use this for interviews:
Flip it by the x axis.
Swap the coordinates diagonally
def rotate_by_90(m):
a.reverse()
for i in range(len(a)):
for j in range(i):
a[i][j], a[j][i] = a[j][i], a[i][j]
return a

If you are looking for speed you are using the wrong type of array. You need to switch to numpy arrays instead of lists. And in Numpy there is of course a function for operations like this: rot90

Related

How to append arrays to another numpy array?

I am trying to loop through a set of coordinates and 'stacking' these arrays of coordinates to another array (so in essence I want to have an array of arrays) using numpy.
This is my attempt:
import numpy as np
all_coordinates = np.array([[]])
for y in range(2):
for x in range(2):
coordinate = np.array([[x,y]])
# append
all_coordinates = np.append(all_coordinates,[coordinate])
print(all_coordinates)
But it's not working. It's just concatenating the individual numbers and not appending the array.
Instead of giving me (the output that I want to achieve):
[[0 0] [1 0] [0,1] [1,1]]
The output I get instead is:
[0 0 1 0 0 1 1 1]
Why? What I am doing wrong here?
The problem that stack functions don't work, is that they need that the row added is of the same size of the already present rows. Using np.array([[]]), the first row is has a length of zero, which means that you can only add rows that also have length zero.
In order to solve this, we need to tell Numpy that the first row is of size two and not zero. The array thus needs to be of size (0, 2) and not (0, 0). This can be done using one of the array-initializing functions that accept size arguments, like empty, zeros or ones. Which function does not matter, as there are no spaces to fill.
Then you can use one of the functions mentioned in comments, like vstack or stack. The code thus becomes:
import numpy as np
all_coordinates = np.zeros((0, 2))
for y in range(2):
for x in range(2):
coordinate = np.array([[x,y]])
# append
all_coordinates = np.vstack((all_coordinates, coordinate))
print(all_coordinates)
In such a case, I would use a list and only convert it into an array once you have appended all the elements you want.
here is a suggested improvement
import numpy as np
all_coordinates = []
for y in range(2):
for x in range(2):
coordinate = np.array([x,y])
# append
all_coordinates.append(coordinate)
all_coordinates = np.array(all_coordinates)
print(all_coordinates)
The output of this code is indeed
array([[0, 0],
[1, 0],
[0, 1],
[1, 1]])

vectorized matrix list application in numpy

the problem i am trying to solve is as follows. I am given a matrix of arbitrary dimension representing indices of a list, and then a list. I would like to get back a matrix with the list elements swapped for the indices. I can't figure out how to do that in a vectorized way:
i.e if z = [[0,1], [1,0]] and list = [20,10], i'd want [[20,10], [10,20]] returned.
When they both are np.array, you can do indexing in a natural way:
import numpy as np
z = np.array([[0, 1], [1, 0]])
a = np.array([20, 10])
output = a[z]
print(output)
# [[20 10]
# [10 20]]

NxN python arrays subsets

I need to carry out some operation on a subset of an NxN array. I have the center of the sub-array, x and y, and its size.
So I can easily do:
subset = data[y-size:y+size,x-size:x+size]
And this is fine.
What I ask is if there is the possibility to do the same without writing an explicit loop if x and y are both 1D arrays of positions.
Thanks!
Using a simple example of a 5x5 array and setting size=1 we can get:
import numpy as np
data = np.arange(25).reshape((5,5))
size = 1
x = np.array([1,4])
y = np.array([1,4])
subsets = [data[j-size:j+size,i-size:i+size] for i in x for j in y]
print(subsets)
Which returns a list of numpy arrays:
[array([[0, 1],[5, 6]]),
array([[15, 16],[20, 21]]),
array([[3, 4],[8, 9]]),
array([[18, 19],[23, 24]])]
Which I hope is what you are looking for.
To get the list of subset assuming you have the list of positions xList and yList, this will do the tric:
subsetList = [ data[y-size:y+size,x-size:x+size] for x,y in zip(xList,yList) ]

Slice subarray from numpy array by list of indices

I have a 2D numpy array input_array and two lists of indices (x_coords and y_coords). Id like to slice a 3x3 subarray for each x,y pair centered around the x,y coordinates. The end result will be an array of 3x3 subarrays where the number of subarrays is equal to the number of coordinate pairs I have.
Preferably by avoiding for loops. Currently I use a modification of game of life strides from the scipy cookbook:
http://wiki.scipy.org/Cookbook/GameOfLifeStrides
shape = (input_array.shape[0] - 2, input_array.shape[0] - 2, 3, 3)
strides = input_array.strides + input_array.strides
strided = np.lib.stride_trics.as_strided(input_array, shape=shape, strides=strides).\
reshape(shape[0]*shape[1], shape[2], shape[3])
This creates a view of the original array as a (flattened) array of all possible 3x3 subarrays. I then convert the x,y coordinate pairs to be able to select the subarrays I want from strided:
coords = x_coords - 1 + (y_coords - 1)*shape[1]
sub_arrays = strided[coords]
Although this works perfectly fine, I do feel it is a bit cumbersome. Is there a more direct approach to do this? Also, in the future I would like to extend this to the 3D case; slicing nx3x3 subarrays from a nxmxk array. It might also be possible using strides but so far I haven't been able to make it work in 3D
Here is a method that use array broadcast:
x = np.random.randint(1, 63, 10)
y = np.random.randint(1, 63, 10)
dy, dx = [grid.astype(int) for grid in np.mgrid[-1:1:3j, -1:1:3j]]
Y = dy[None, :, :] + y[:, None, None]
X = dx[None, :, :] + x[:, None, None]
then you can use a[Y, X] to select blocks from a. Here is an example code:
img = np.zeros((64, 64))
img[Y, X] = 1
Here is graph ploted by pyplot.imshow():
A very straight forward solution would be a list comprehension and itertools.product:
import itertools
sub_arrays = [input_array[x-1:x+2, y-1:y+2]
for x, y in itertools.product(x_coords, y_coords)]
This creates all possible tuples of coordinates and then slices the 3x3 arrays from the input_array.
But this is sort-of a for loop. And you will have to take care, that x_coords and y_coords are not on the border of the matrix.

A particular way of resizing a matrix

Having a nxn (6x6 in the example below) matrix filled only with 0 and 1:
old_matrix=[[0,0,0,1,1,0],
[1,1,1,1,0,0],
[0,0,1,0,0,0],
[1,0,0,0,0,1],
[0,1,1,1,1,0],
[1,0,0,1,1,0]]
I want to resize it in a particular way. Taking (2x2) sub-matrice and checking if there are more ones or zeros. This means the new matrix will be (3x3) If there are more 1 than 0 un the sub-matrice a 1 value will be assigned in the new matrix. Otherwise, (if there are less or equal) its new value will be 0.
new_matrix=[[0,1,0],
[0,0,0],
[0,1,0]]
I've tried to achieve this by using lots of whiles. However it doesn seem to work. Here's what I got so far:
def convert_track(a):
#converts original map to a 8x8 tile Track
NEW_TRACK=[]
w=0 #matrix width
h=0 #matrix heigth
t_w=0 #submatrix width
t_h=0 #submatrix heigth
BLACK=0 #number of ones in submatrix
WHITE=0 #number of zeros in submatrix
while h<=6:
while w<=6:
l=[]
while t_h<=2 and h<=6:
t_w=0
while t_w<=2 and w<=6:
if a[h][w]==1:
BLACK+=1
else:
WHITE+=1
t_w+=1
w+=1
h+=1
t_h+=1
t_w=0
t_h+=1
if BLACK<=WHITE:
l.append(0)
else:
l.append(1)
BLACK=0
WHITE=0
t_h=0
NEW_TRACK.append(l)
return NEW_TRACK
Raises the error list index out of range or returns the list
[[0]]
is there an easier way to achieve this? What am i doing wrong?
If you are willing/able to use NumPy you can do something like this. If you're working with anything like the data you've shown it's well worth your time to learn as operations like these can be done very efficiently and with very little code.
import numpy as np
from scipy.signal import convolve2d
old_matrix=[[0,0,0,1,1,0],
[1,1,1,1,0,0],
[0,0,1,0,0,0],
[1,0,0,0,0,1],
[0,1,1,1,1,0],
[1,0,0,1,1,0]]
a = np.array(old_matrix)
k = np.ones((2,2))
# compute sums at each submatrix
local_sums = convolve2d(a, k, mode='valid')
# restrict to sums corresponding to non-overlapping
# sub-matrices with local_sums[::2, ::2] and check if
# there are more 1 than 0 elements
result = local_sums[::2, ::2] > 2
# convert back to Python list if needed
new_matrix = result.astype(np.int).tolist()
Result:
>>> result.astype(np.int).tolist()
[[0, 1, 0], [0, 0, 0], [0, 1, 0]]
Here I've used convolve2d to compute the sums at each submatrix. From what I can tell you are only interested in non-overlapping sub-matrices, so the part local_sums[::2, ::2] chops out only the sums corresponding to those.

Categories