Here's one way you could do it. The function get_shape(a)
is a recursive function that returns either the shape of a
as a tuple, or None
if a
does not have a regular shape (i.e. a
is ragged). The tricky part is actually the function is_scalar(a)
: we want string
and bytes
instances, and arbitrary noniterable objects (such as None
, or Foo()
where Foo
is class Foo: pass
) to be considered scalars. np.iscalar()
does some of the work; an attempt to evaluate len(a)
does the rest. The NumPy docs suggest the simple expression np.ndim(a) == 0
, but that will invoke the array creation for a
, which will trigger the warning if a
is ragged. (The is_scalar
function might miss some cases, so test it carefully with typical data that you use.)
import numpy as np
def is_scalar(a):
if np.isscalar(a):
return True
try:
len(a)
except TypeError:
return True
return False
def get_shape(a):
"""
Returns the shape of `a`, if `a` has a regular array-like shape.
Otherwise returns None.
"""
if is_scalar(a):
return ()
shapes = [get_shape(item) for item in a]
if len(shapes) == 0:
return (0,)
if any([shape is None for shape in shapes]):
return None
if not all([shapes[0] == shape for shape in shapes[1:]]):
return None
return (len(shapes),) + shapes[0]
def is_ragged(a):
return get_shape(a) is None
For example,
In [114]: is_ragged(123)
Out[114]: False
In [115]: is_ragged([1, 2, 3])
Out[115]: False
In [116]: is_ragged([1, 2, [3, 4]])
Out[116]: True
In [117]: is_ragged([[[1]], [[2]], [[3]]])
Out[117]: False
In [118]: is_ragged([[[1]], [[2]], [[3, 99]]])
Out[118]: True