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]])
Related
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.
This question already has answers here:
How to perform an ND coordinate sweep using numpy meshgrid
(3 answers)
Closed 2 years ago.
I'm experimenting with some matplotlib_3d plots, and as such I'm starting to delve into numpy too.
I want to create a cube on a 3d plot, within certain bounds. This requires an array of coordinates, each of length 3.
Eg. for a cube of side length 3, the result would be a 2d array of length 27, comprised of tuples:
[0, 0, 0], [0, 1, 0], [0, 2, 0], [1, 0, 0], [1, 1, 0], [1, 2, 0], [2, 0, 0], [2, 1, 0], [2, 2, 0] # first "layer" on the z axis
[0, 0, 1], [0, 1, 1], [0, 2, 1], [1, 0, 1], [1, 1, 1], [1, 2, 1], [2, 0, 1], [2, 1, 1], [2, 2, 1] # second "layer" on the z axis
[0, 0, 2], [0, 1, 2], [0, 2, 2], [1, 0, 2], [1, 1, 2], [1, 2, 2], [2, 0, 2], [2, 1, 2], [2, 2, 2] # thrid "layer" on the z axis
I understand the use of numpy.meshgridto generate the coordinate like arrays when given a series of ranges, however trying to "zip" those arrays together into the format described above (order is irrelevant), I get lost completely. So far, I have tried the following:
x, y, z= np.meshgrid(np.arange(0, 3), np.arange(0, 3), np.arange(0, 3))
x >> [[[0 0 0]
[1 1 1]
[2 2 2]]
[[0 0 0]
[1 1 1]
[2 2 2]]
[[0 0 0]
[1 1 1]
[2 2 2]]]
y >> [[[0 0 0]
[0 0 0]
[0 0 0]]
[[1 1 1]
[1 1 1]
[1 1 1]]
[[2 2 2]
[2 2 2]
[2 2 2]]]
z >> [[[0 1 2]
[0 1 2]
[0 1 2]]
[[0 1 2]
[0 1 2]
[0 1 2]]
[[0 1 2]
[0 1 2]
[0 1 2]]]
Now I try to "zip" them together using numpy.dstack([x, y, z]), the result is:
[[[0 0 0 0 0 0 0 1 2]
[1 1 1 0 0 0 0 1 2]
[2 2 2 0 0 0 0 1 2]]
[[0 0 0 1 1 1 0 1 2]
[1 1 1 1 1 1 0 1 2]
[2 2 2 1 1 1 0 1 2]]
[[0 0 0 2 2 2 0 1 2]
[1 1 1 2 2 2 0 1 2]
[2 2 2 2 2 2 0 1 2]]]
I understand that in reshapemight be of use here as it is in other cases, but I don't know how to apply it to this case.
Does anyone have any suggestions as to how to create the array mentioned at the start of the post? More examples and short explanations would be much appreciated, thanks and stay safe!
You were almost there, just need to flatten the coordinates:
x, y, z= np.meshgrid(np.arange(0, 3), np.arange(0, 3), np.arange(0, 3))
np.stack([x.flatten(), y.flatten(), z.flatten()], axis = -1)
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]]
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)
I have a 2D numpy Array filled with integer-values from 0 to N, how can i get the indices of all entries that are directly connected and share the same value.
Addition: Most of the entries are zero and can be ignored!
Example Input array:
[ 0 0 0 0 0 ]
[ 1 1 0 1 1 ]
[ 0 1 0 1 1 ]
[ 1 0 0 0 0 ]
[ 2 2 2 2 2 ]
Wished output indices:
1: [ [1 0] [1 1] [2 1] [3 0] ] # first 1 cluster
[ [1 3] [1 4] [2 3] [2 4] ] # second 1 cluster
2: [ [4 0] [4 1] [4 2] [4 3] [4 4] ] # only 2 cluster
the formating of the output arrays is not important, i just need separated value clusters where it is possible to address the single indices
What i was first thinking of is:
N = numberClusters
x = myArray
for c in range(N):
for i in np.where(x==c):
# fill output array with i
but this misses the separation of clusters that have the same value
You can use skimage.measure.label (install it with pip install scikit-image, if needed) for this:
import numpy as np
from skimage import measure
# Setup some data
np.random.seed(42)
img = np.random.choice([0, 1, 2], (5, 5), [0.7, 0.2, 0.1])
# [[2 0 2 2 0]
# [0 2 1 2 2]
# [2 2 0 2 1]
# [0 1 1 1 1]
# [0 0 1 1 0]]
# Label each region, considering only directly adjacent pixels connected
img_labeled = measure.label(img, connectivity=1)
# [[1 0 2 2 0]
# [0 3 4 2 2]
# [3 3 0 2 5]
# [0 5 5 5 5]
# [0 0 5 5 0]]
# Get the indices for each region, excluding zeros
idx = [np.where(img_labeled == label)
for label in np.unique(img_labeled)
if label]
# [(array([0]), array([0])),
# (array([0, 0, 1, 1, 2]), array([2, 3, 3, 4, 3])),
# (array([1, 2, 2]), array([1, 0, 1])),
# (array([1]), array([2])),
# (array([2, 3, 3, 3, 3, 4, 4]), array([4, 1, 2, 3, 4, 2, 3]))]
# Get the bounding boxes of each region (ignoring zeros)
bboxes = [area.bbox for area in measure.regionprops(img_labeled)]
# [(0, 0, 1, 1),
# (0, 2, 3, 5),
# (1, 0, 3, 2),
# (1, 2, 2, 3),
# (2, 1, 5, 5)]
The bounding boxes can be found using the very helpful function skimage.measure.regionprops, which contains a plethora of information on the regions. For the bounding box it returns a tuple of (min_row, min_col, max_row, max_col), where pixels belonging to the bounding box are in the half-open interval [min_row; max_row) and [min_col; max_col).