Reputation: 10996
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.
Upvotes: 15
Views: 3337
Reputation: 15364
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.
Upvotes: 10
Reputation: 280182
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
Upvotes: 5
Reputation: 2720
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
Upvotes: 1
Reputation: 2298
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.
Upvotes: 2
Reputation: 535
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)
Upvotes: 1