How can I slice 2D array in python without Numpy module? - python

What I wanna process is slice 2D array partially without numpy module like following example with numpy.
and I want to know Time Complexity of Slicing Lists in python basic function
import numpy as np
A = np.array([ [1,2,3,4,5,6,7,8] for i in range(8)])
n = len(A[0])
x = int(n/2)
TEMP = [[None]*2 for i in range(2)]
for w in range(2):
for q in range(2):
TEMP[w][q] = A[w*x:w*x+x,q*x:q*x+x]
for w in range(2):
for q in range(2):
print(TEMP[w][q])
here is the result that i wanna get
[[1 2 3 4]
[1 2 3 4]
[1 2 3 4]
[1 2 3 4]]
[[5 6 7 8]
[5 6 7 8]
[5 6 7 8]
[5 6 7 8]]
[[1 2 3 4]
[1 2 3 4]
[1 2 3 4]
[1 2 3 4]]
[[5 6 7 8]
[5 6 7 8]
[5 6 7 8]
[5 6 7 8]]
Process finished with exit code 0

For the first question:
A = [ [1,2,3,4,5,6,7,8] for i in range(8)]
n = len(A[0])
x = int(n/2)
TEMP = [[None]*2 for i in range(2)]
for w in range(2):
for q in range(2):
TEMP[w][q] = [item[q * x:(q * x) + x] for item in A[w * x:(w * x) + x]]
for w in range(2):
for q in range(2):
print("{i}, {j}: {item}".format(i=w, j=q, item=repr(TEMP[w][q])))

Numpy makes the vertical slicing of 2 dimensional array easier. However, you can achieve the same results without it. Imagine if we have the following 2D list:
arr1=[[1,1,1,0,0,0],[0,1,0,0,0,0],[1,1,1,0,0,0],[0,0,2,4,4,0],[0,0,0,2,0,0],[0,0,1,2,4,0]]
which is represented as the following matrix:
[[1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 0, 2, 4, 4, 0],
[0, 0, 0, 2, 0, 0],
[0, 0, 1, 2, 4, 0]]
Let's say that you want to slice this to form the pattern below :
This can be achieved without numpy by the following :
for x in range (0,4):
for y in range (0,4):
# Here we traverse through the 2D array vertically
temp_matrix= arr1[x][y:y+3],arr1[x+1][y:y+3],arr1[x+2][y:y+3]
print (temp_matrix)
If we use numpy instead we can re-write the following code :
arr=np.array(arr1)
rows = arr.shape[0]
cols = arr.shape[1]
for x in range (0,rows-2):
for y in range (0, cols-2):
print(arr[x:x+3,y:y+3])

Related

Generate 3D numpy array based on provided pattern

Could you tell me if there is any 'smart' way to generate a 3D numpy array based on provided pattern? Let me explain what I mean by this. E.g., if pattern is [1, 4, 6, 4, 1], corresponding 2D array for it would be:
[
[1, 1, 1, 1, 1],
[1, 4, 4, 4, 1],
[1, 4, 6, 4, 1],
[1, 4, 4, 4, 1],
[1, 1, 1, 1, 1]
]
And 3D array is similar to 2D. If you imagine that 3D array as a cube: just one 6 in the center of 'cube', twenty six 4s around it in the closest neighborhood, and the rest 1s.
I apologize for potentially confusing explanation, I'm not a native English speaker. Please ask if something is unclear.
Thanks!
Any python library can be used.
You can use numpy.pad to add "layers" around your center number one by one (like an onion (well, a very cubic onion, actually) ):
pattern = [1,4,6]
x = np.array(pattern[-1]).reshape([1,1,1])
for p in reversed(pattern[:-1]):
x = np.pad(x, mode='constant', constant_values=p, pad_width=1)
print(x)
#[[[1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]]
#
# [[1 1 1 1 1]
# [1 4 4 4 1]
# [1 4 4 4 1]
# [1 4 4 4 1]
# [1 1 1 1 1]]
#
# [[1 1 1 1 1]
# [1 4 4 4 1]
# [1 4 6 4 1]
# [1 4 4 4 1]
# [1 1 1 1 1]]
#
# [[1 1 1 1 1]
# [1 4 4 4 1]
# [1 4 4 4 1]
# [1 4 4 4 1]
# [1 1 1 1 1]]
#
# [[1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]
# [1 1 1 1 1]]]
The code above should work with an arbitrary number of layers (in fact, it also works for an arbitrary amount of dimensions, if you adapt the reshape). However, it scales poorly with the number of layers, due to the for-loop. While it certainly is overkill to vectorize this for-loop in this application, I'd be open for suggestions if anyone has an idea.
The tricky part is generating the indices matching the pattern. The following should work for palindromes:
a = np.array([1,4,6,4,1])
i = np.ceil((np.r_[:2, 2:-1:-1][:, None] * np.r_[:2, 2:-1:-1]) / 2).astype(int)
a[i]
output:
array([[1, 1, 1, 1, 1],
[1, 4, 4, 4, 1],
[1, 4, 6, 4, 1],
[1, 4, 4, 4, 1],
[1, 1, 1, 1, 1]])

Python Numpy make a pattern with a square matrix of any dimension

I am having difficulties trying to generate a specific pattern that would work for any square matrix with any square dimension using NumPy
For example:
User input: n = 3
Output:
[[1 2 0]
[2 3 2]
[0 2 1]]
User input: n = 5
Output:
[[1 2 3 0 0]
[2 3 4 0 0]
[3 4 5 4 3]
[0 0 4 3 2]
[0 0 3 2 1]]
User input: n = 8
Output:
[[1 2 3 4 5 0 0 0]
[2 3 4 5 6 0 0 0]
[3 4 5 6 7 0 0 0]
[4 5 6 9 8 7 6 5]
[5 6 7 8 9 6 5 4]
[0 0 0 7 6 5 4 3]
[0 0 0 6 5 4 3 2]
[0 0 0 5 4 3 2 1]]
Since a square matrix can be generated with any number in the form of (n x n), there would be instances where the user input is an odd number, how would I start figuring out the equations needed to make this work?
I got this going on but I was only able to do it on one corner of the matrix, any suggestion or idea is appreciated, thank you!
def input_number(n):
matrix = np.zeros(shape=(n, n), dtype=int)
for y in range(round(n // 2) + 1):
for x in range(round(n // 2) + 1):
matrix[y, x] = x + y + 1
y += 1
Input: n = 4
Output:
[[1 2 3 0 0]
[2 3 4 0 0]
[3 4 5 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]
Try this:
def foo(n):
half = (n+1) // 2
res = np.zeros((n, n), int)
res[:half, :half] = np.arange(1, 1 + half) + np.arange(half).reshape(half, 1)
res[n//2:, n//2:] = res[half-1::-1, half-1::-1]
return res
This creates the top-left quarter then mirrors it to the bottom-right quarter.
Note it behaves a little differently to your example of n=8, the output is:
[[1, 2, 3, 4, 0, 0, 0, 0],
[2, 3, 4, 5, 0, 0, 0, 0],
[3, 4, 5, 6, 0, 0, 0, 0],
[4, 5, 6, 7, 0, 0, 0, 0],
[0, 0, 0, 0, 7, 6, 5, 4],
[0, 0, 0, 0, 6, 5, 4, 3],
[0, 0, 0, 0, 5, 4, 3, 2],
[0, 0, 0, 0, 4, 3, 2, 1]]
But I'll leave it this way because it seems more logical...you can edit the code if needed to make the center 4 values mix with each other as you showed.
I looked around a bit more and was eventually able to pull it off, here's my take on it.
import numpy as np
def input_number(n):
matrix = np.zeros(shape=(n, n), dtype=int)
for y in range(round(n // 2) + 1):
for x in range(round(n // 2) + 1):
matrix[y, x] = y + x + 1
matrix[(n - y) - 1][(n - x) - 1] = matrix[y, x]
print(matrix)
input_number(n)
Input: 3
Output:
[[1 2 0]
[2 3 2]
[0 2 1]]
Input: 5
Output:
[[1 2 3 0 0]
[2 3 4 0 0]
[3 4 5 4 3]
[0 0 4 3 2]
[0 0 3 2 1]]
Input: 8
Output:
[[1 2 3 4 5 0 0 0]
[2 3 4 5 6 0 0 0]
[3 4 5 6 7 0 0 0]
[4 5 6 9 8 7 6 5]
[5 6 7 8 9 6 5 4]
[0 0 0 7 6 5 4 3]
[0 0 0 6 5 4 3 2]
[0 0 0 5 4 3 2 1]]
I am not sure what is the pattern of zeros in these examples. In the cases of n=3 and n=5 the zero subarrays in the corners are of the size (n//2)x(n//2), but for n=8 they are of the size 3x3 and not 4x4. Assuming that the size of these subarrays should be (n//2)x(n//2) the following code should work:
n = 7
A = np.zeros((n, n), dtype=int)
for i in range(n):
A.ravel()[i:(n-i)*n:n+1] = n-i
m = n//2
A[:m, :m] = 0
A[-m:, -m:] = 0
A = A + A.T - np.diag(np.diag(A))
A = A[:, ::-1]
If the zero subarrays should be smaller, it suffices to change the value of m.

Shift a list in Python

How would you shift left a list by x in Python, and fill the empty values with zeros?
shift left by 1
input: [1, 2, 3, 4]
output: [2, 3, 4, 0]
shift left by 2
input [1, 2, 3, 4]
output [3, 4, 0, 0]
As far as I'm concerned, there's no 'easy' way, since Python's lists are not constrained by a size, but you can easily implement an algorithm which handles this:
def shift_left(arr, n):
return arr[n:] + [0 for _ in range(n)]
or a bit more consise:
def shift_left(arr, n):
return arr[n:] + [0] * n
You can concat two lists as:
arr[shift:]+[0]*shift
Or if you are a fan of chaining like me:
arr[shift:].__add__([0]*shift)
You can combine numpy's append and roll methods:
import numpy as np
def shift_left(x_, r):
return np.append(np.roll(x_, -r)[:-r], [0 for _ in range(0, r)])
print(shift_left([1, 2, 3, 4], 1))
print(shift_left([1, 2, 3, 4], 2))
Result:
[2 3 4 0]
[3 4 0 0]
Explanation
When you use roll on a list:
print(np.roll([1, 2, 3, 4], -2))
Result:
[3 4 1 2]
You move the each element to the left by r times (r= -2). But we don't want the last r elements so:
print(np.roll([1, 2, 3, 4], -2)[:-2])
Result:
[3 4]
We want the last r values to be 0. So we can append r 0 to the end of the array.
print(np.append(np.roll([1, 2, 3, 4], -2)[:-2], [0 for _ in range(0, 2)]))
Result:
[3 4 0 0]

how to find common values of rows of a matrix in tensorflow

I have a tensor like this:
tf_a2 = tf.constant([[1, 2, 5 ],
[1, 4, 6 ],
[0, 10, 10],
[2, 4, 6 ],
[2, 4, 10]])
I would like to find whole indices in this matrice which are repeated more than n time.
For example: 1 being repeated two times. 2 being repeated three times. 5 being repeated one time. Repetition between rows is considered. Also, I want to skip the number 10 totally(10 is constant).
here n=2, So the result looks like: because 2 and 4 being repeated more than two times.
[[0, 2, 0 ],
[0, 4, 0 ],
[0, 0, 0 ],
[2, 4, 0 ],
[2, 4, 0 ]]
I found an example here but the explanations are for Matlab code.
Thanks in advance:)
First you can use tf.unique_with_counts to finds unique elements in a 1-D tensor.
import tensorflow as tf
tf_a2 = tf.constant([[1, 2, 5 ],
[1, 4, 6 ],
[0, 10, 10],
[2, 4, 6 ],
[2, 4, 10]])
n = 2
constant = 10
y, idx, count = tf.unique_with_counts(tf.reshape(tf_a2,[-1,]))
# y = [ 1 2 5 4 6 0 10]
# idx = [0 1 2 0 3 4 5 6 6 1 3 4 1 3 6]
# count = [2 3 1 3 2 1 3]
Then you can map repetition times to original tensor.
count_mask = tf.reshape(tf.gather(count,idx),tf_a2.shape)
# [[2 3 1]
# [2 3 2]
# [1 3 3]
# [3 3 2]
# [3 3 3]]
Finally you can skip the number 10 and get the result you expect by tf.where.
# skip constant and filter n time
result = tf.where(tf.logical_and(tf.greater(count_mask,n),
tf.not_equal(tf_a2,constant)),
tf_a2,
tf.zeros_like(tf_a2))
with tf.Session() as sess:
print(sess.run(result))
# [[0 2 0]
# [0 4 0]
# [0 0 0]
# [2 4 0]
# [2 4 0]]

numpy replace 2d bool array with sum of consecutive elements across an axis efficiently

I have a bool array (bool_arr) that I want to replace the consecutive non-zero numbers along the columns with their count (consecutive_count) (which is also the max/last number of the consecutive group)
bool_arr = consecutive_count =
[[1 1 1 1 0 1] [[3 6 1 6 0 1]
[1 1 0 1 1 0] [3 6 0 6 5 0]
[1 1 1 1 1 1] [3 6 3 6 5 2]
[0 1 1 1 1 1] [0 6 3 6 5 2]
[1 1 1 1 1 0] [2 6 3 6 5 0]
[1 1 0 1 1 1]] [2 6 0 6 5 1]]
I've created my own function that gets the cumulative sum of consecutive non-zero elements along the columns
consecutive_cumsum =
[[1 1 1 1 0 1]
[2 2 0 2 1 0]
[3 3 1 3 2 1]
[0 4 2 4 3 2]
[1 5 3 5 4 0]
[2 6 0 6 5 1]]
I currently use the following to get consecutive_count:
bool_arr = np.array([[1,1,1,1,0,1],
[1,1,0,1,1,0],
[1,1,1,1,1,1],
[0,1,1,1,1,1],
[1,1,1,1,1,0],
[1,1,0,1,1,1]])
consecutive_cumsum = np.array([[1,1,1,1,0,1],
[2,2,0,2,1,0],
[3,3,1,3,2,1],
[0,4,2,4,3,2],
[1,5,3,5,4,0],
[2,6,0,6,5,1]])
consecutive_count = consecutive_cumsum.copy()
for x in range(consecutive_count.shape[1]):
maximum = 0
for y in range(consecutive_count.shape[0]-1, -1, -1):
if consecutive_cumsum[y,x] > 0:
if consecutive_cumsum[y,x] < maximum: consecutive_count[y,x] = maximum
else: maximum = consecutive_cumsum[y,x]
else: maximum = 0
print(consecutive_count)
It works great but I am iterating over every element to replace with the max, between zeros.
Is there a way to use numpy to vectorize this instead of looping over all elements. And as a bonus, specify which axis (row vs column) it will perform it on
The new (v1.15.0 I believe) append and prepend keywords of np.diff make this easy:
bnd = np.diff(bool_arr, axis=0, prepend=0, append=0)
x, y = np.where(bnd.T)
bnd.T[x, y] *= (y[1::2]-y[::2]).repeat(2)
bnd[:-1].cumsum(axis=0)
# array([[3, 6, 1, 6, 0, 1],
# [3, 6, 0, 6, 5, 0],
# [3, 6, 3, 6, 5, 2],
# [0, 6, 3, 6, 5, 2],
# [2, 6, 3, 6, 5, 0],
# [2, 6, 0, 6, 5, 1]])
With selectable axis:
def count_ones(a, axis=-1):
a = a.swapaxes(-1, axis)
bnd = np.diff(a, axis=-1, prepend=0, append=0)
*idx, last = np.where(bnd)
bnd[(*idx, last)] *= (last[1::2]-last[::2]).repeat(2)
return bnd[..., :-1].cumsum(axis=-1).swapaxes(-1, axis)
UPDATE: and a version that works with general (not just 0/1) entries:
def sum_stretches(a, axis=-1):
a = a.swapaxes(-1, axis)
dtype = np.result_type(a, 'i1')
bnd = np.diff((a!=0).astype(dtype), axis=-1, prepend=0, append=0)
*idx, last = np.where(bnd)
A = np.concatenate([np.zeros((*a.shape[:-1], 1), a.dtype), a.cumsum(axis=-1)], -1)[(*idx, last)]
bnd[(*idx, last)] *= (A[1::2]-A[::2]).repeat(2)
return bnd[..., :-1].cumsum(axis=-1).swapaxes(-1, axis)
Using itertools.groupby:
import itertools
for i in range(b.shape[1]):
counts = []
for k,v in itertools.groupby(b[:,i]):
g = list(v)
counts.extend([sum(g)] * len(g))
b[:,i] = counts
Output:
array([[3, 6, 1, 6, 0, 1],
[3, 6, 0, 6, 5, 0],
[3, 6, 3, 6, 5, 2],
[0, 6, 3, 6, 5, 2],
[2, 6, 3, 6, 5, 0],
[2, 6, 0, 6, 5, 1]])
building on paulpanzer's answer for poor souls (like me) who dont have numpy v1.15+
def sum_stretches(a, axis=-1):
a = a.swapaxes(-1, axis)
padding = [[0,0].copy()]*a.ndim
padding[-1] = [1,1]
padded = np.pad((a!=0), padding, 'constant', constant_values=0).astype('int32')
bnd = np.diff(padded, axis=-1)
*idx, last = np.where(bnd)
A = np.concatenate([np.zeros((*a.shape[:-1], 1), 'int32'), a.cumsum(axis=-1)], -1)[(*idx, last)]
bnd[(*idx, last)] *= (A[1::2]-A[::2]).repeat(2)
return bnd[..., :-1].cumsum(axis=-1).swapaxes(-1, axis)

Categories