An n-dimensional array has 2n sides (a 1-dimensional array has 2 endpoints; a 2-dimensional array has 4 sides or edges; a 3-dimensional array has 6 2-dimensional faces; a 4-dimensional array has 8 sides; etc.). This is analogous to what happens with abstract n-dimensional cubes.
I want to check if all sides of an n-dimensional array are composed by only zeros. Here are three examples of arrays whose sides are composed by zeros:
# 1D
np.array([0,1,2,3,0])
# 2D
np.array([[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 2, 3, 0],
[0, 0, 1, 0],
[0, 0, 0, 0]])
# 3D
np.array([[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[0, 0, 0, 0],
[0, 1, 2, 0],
[0, 0, 0, 0]],
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]])
How can I check if all sides of a multidimensional numpy array are arrays of zeros? For example, with a simple 2-dimensional array I can do this:
x = np.random.rand(5, 5)
assert np.sum(x[0:, 0]) == 0
assert np.sum(x[0, 0:]) == 0
assert np.sum(x[0:, -1]) == 0
assert np.sum(x[-1, 0:]) == 0
While this approach works for 2D cases, it does not generalize to higher dimensions. I wonder if there is some clever numpy trick I can use here to make it efficient and also more maintainable.
Accessing Values The data elements in two dimesnional arrays can be accessed using two indices. One index referring to the main or parent array and another index referring to the position of the data element in the inner array. If we mention only one index then the entire inner array is printed for that index position.
Here's how you can do it:
assert(all(np.all(np.take(x, index, axis=axis) == 0)
for axis in range(x.ndim)
for index in (0, -1)))
np.take
does the same thing as "fancy" indexing.
Here's an answer that actually examines the parts of the array you're interested in, and doesn't waste time constructing a mask the size of the whole array. There's a Python-level loop, but it's short, with iterations proportional to the number of dimensions instead of the array's size.
def all_borders_zero(array):
if not array.ndim:
raise ValueError("0-dimensional arrays not supported")
for dim in range(array.ndim):
view = numpy.moveaxis(array, dim, 0)
if not (view[0] == 0).all():
return False
if not (view[-1] == 0).all():
return False
return True
I reshaped the array and then iterated through it. Unfortunately, my answer assumes you have at least three dimensions and will error out for normal matrices, you would have to add a special clause for 1 & 2 dimensional shaped arrays. In addition, this will be slow so there are likely better solutions.
x = np.array(
[
[
[0 , 1, 1, 0],
[0 , 2, 3, 0],
[0 , 4, 5, 0]
],
[
[0 , 6, 7, 0],
[0 , 7, 8, 0],
[0 , 9, 5, 0]
]
])
xx = np.array(
[
[
[0 , 0, 0, 0],
[0 , 2, 3, 0],
[0 , 0, 0, 0]
],
[
[0 , 0, 0, 0],
[0 , 7, 8, 0],
[0 , 0, 0, 0]
]
])
def check_edges(x):
idx = x.shape
chunk = np.prod(idx[:-2])
x = x.reshape((chunk*idx[-2], idx[-1]))
for block in range(chunk):
z = x[block*idx[-2]:(block+1)*idx[-2], :]
if not np.all(z[:, 0] == 0):
return False
if not np.all(z[:, -1] == 0):
return False
if not np.all(z[0, :] == 0):
return False
if not np.all(z[-1, :] == 0):
return False
return True
Which will produce
>>> False
>>> True
Basically I stack all the dimensions on top of each other and then look through them to check their edges.
maybe the ellipsis operator is what you are looking for, which will work for many dimensions:
import numpy as np
# data
x = np.random.rand(2, 5, 5)
x[..., 0:, 0] = 0
x[..., 0, 0:] = 0
x[..., 0:, -1] = 0
x[..., -1, 0:] = 0
test = np.all(
[
np.all(x[..., 0:, 0] == 0),
np.all(x[..., 0, 0:] == 0),
np.all(x[..., 0:, -1] == 0),
np.all(x[..., -1, 0:] == 0),
]
)
print(test)
You can make use of slice
and boolean masking to get the job done:
def get_borders(arr):
s=tuple(slice(1,i-1) for i in a.shape)
mask = np.ones(arr.shape, dtype=bool)
mask[s] = False
return(arr[mask])
This function first shapes the "core" of the array into the tuple s
, and then builds a mask that shows True
only for the bordering points. Boolean indexing then delivers the border points.
Working example:
a = np.arange(16).reshape((4,4))
print(a)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
borders = get_borders(a)
print(borders)
array([ 0, 1, 2, 3, 4, 7, 8, 11, 12, 13, 14, 15])
Then, np.all(borders==0)
will give you the desired information.
Note: this breaks for one-dimensional arrays, though I consider those an edge case. You're probably better off just checking the two points in question there
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With