Reputation: 10784
I have a function that takes the argument NBins
. I want to make a call to this function with a scalar 50
or an array [0, 10, 20, 30]
. How can I identify within the function, what the length of NBins
is? or said differently, if it is a scalar or a vector?
I tried this:
>>> N=[2,3,5]
>>> P = 5
>>> len(N)
3
>>> len(P)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>>
As you see, I can't apply len
to P
, since it's not an array.... Is there something like isarray
or isscalar
in python?
thanks
Upvotes: 427
Views: 624372
Reputation: 133574
>>> import collections.abc
>>> isinstance([0, 10, 20, 30], collections.abc.Sequence) and not isinstance([0, 10, 20, 30], (str, unicode))
True
>>> isinstance(50, collections.abc.Sequence) and not isinstance(50, (str, unicode))
False
note: isinstance
also supports a tuple of classes, check type(x) in (..., ...)
should be avoided and is unnecessary.
You may also wanna check not isinstance(x, (str, unicode))
As noted by @2080 and also here this won't work for numpy
arrays. eg.
>>> import collections.abc
>>> import numpy as np
>>> isinstance((1, 2, 3), collections.abc.Sequence)
True
>>> isinstance(np.array([1, 2, 3]), collections.abc.Sequence)
False
In which case you may try the answer from @jpaddison3:
>>> hasattr(np.array([1, 2, 3]), "__len__")
True
>>> hasattr([1, 2, 3], "__len__")
True
>>> hasattr((1, 2, 3), "__len__")
True
However as noted here, this is not perfect either, and will incorrectly (at least according to me) classify dictionaries as sequences whereas isinstance
with collections.abc.Sequence
classifies correctly:
>>> hasattr({"a": 1}, "__len__")
True
>>> from numpy.distutils.misc_util import is_sequence
>>> is_sequence({"a": 1})
True
>>> isinstance({"a": 1}, collections.abc.Sequence)
False
You could customise your solution to something like this, add more types to isinstance
depending on your needs:
>>> isinstance(np.array([1, 2, 3]), (collections.abc.Sequence, np.ndarray))
True
>>> isinstance([1, 2, 3], (collections.abc.Sequence, np.ndarray))
True
Upvotes: 554
Reputation: 171
Determining if something is a scalar is easier (there are fewer ambiguities) than determining if it is a sequence (array/list/vector, etc.).
as @jamylak notes, not numpy.distutils.misc_util.is_sequence
will identify scalars well. It simply checks that the input is not a str
AND does not have a __len__
attribute.
But just because something is not a scalar does not mean it is a sequence. As @jamylak notes, this also identifies dictionaries as sequences, which differs from collections.abc.Sequence (dict
is Iterable
, but not a Sequence
).
collections.abc.Sequence
returns True
for a list
but not for a numpy array (it also returns True for a str
):
issubclass(type(np.array([1])), collections.abc.Sequence) == False
collections.abc.Iterable
returns True
for a (normal) numpy array, but unfortunately also returns True for a zero-dimensional numpy array:
issubclass(type(np.array(1)), collections.abc.Iterable) == True
which cannot actually be iterated over, indexed into, and does not have a __len__
.
As the numpy documentation suggests, numpy.isscalar should not be used. However, their suggested alternative, np.ndim(x) == 0
also doesn't work as I would expect.
It correctly calls np.array(1)
a scalar (np.ndim(np.array(1)) == 0
), however, as noted in the numpy.isscalar documentation, it also calls almost everything that is not a list or np.array a scalar, including strings, dicts, and other objects.
The only thing I've found that reliably works is
def is_scalar(x):
return issubclass(type(np.asarray(x)[()]), numbers.Number)
def is_sequence(x):
if is_scalar(x):
return False
return np.ndim(x) > 0
is_scalar(1) # True
is_scalar(np.array(1)) # True
is_scalar([1]) # False
is_scalar(np.array([1])) # False
is_scalar('a string') # False
is_scalar({'key': 10}) # False
is_sequence(1) # False
is_sequence(np.array(1)) # False
is_sequence([1]) # True
is_sequence(np.array([1])) # True
is_sequence('a string') # False
is_sequence({'key': 10}) # False
Note how both is_scalar and is_sequence call the str
and dict
neither a scalar nor a sequence
Upvotes: 2
Reputation: 41
You can easily use function isinstance(object, classinfo)
in Python.
>>> isinstance(5, list)
False
>>> isinstance([2, 3, 5], list)
True
See ducomentation for this function.
Upvotes: 3
Reputation: 1696
Is there an equivalent to isscalar() in numpy? Yes.
>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
>>> np.isscalar('abcd')
True
Upvotes: 67
Reputation: 79
Since the general guideline in Python is to ask for forgiveness rather than permission, I think the most pythonic way to detect a string/scalar from a sequence is to check if it contains an integer:
try:
1 in a
print('{} is a sequence'.format(a))
except TypeError:
print('{} is a scalar or string'.format(a))
Upvotes: 1
Reputation: 2120
To answer the question in the title, a direct way to tell if a variable is a scalar is to try to convert it to a float. If you get TypeError
, it's not.
N = [1, 2, 3]
try:
float(N)
except TypeError:
print('it is not a scalar')
else:
print('it is a scalar')
Upvotes: 4
Reputation: 68738
Here is the best approach I have found: Check existence of __len__
and __getitem__
.
You may ask why? The reasons includes:
isinstance(obj, abc.Sequence)
fails on some objects including PyTorch's Tensor because they do not implement __contains__
.__len__
and __getitem__
which I feel are minimal methods for array-like objects.So without further ado:
def is_array_like(obj, string_is_array=False, tuple_is_array=True):
result = hasattr(obj, "__len__") and hasattr(obj, '__getitem__')
if result and not string_is_array and isinstance(obj, (str, abc.ByteString)):
result = False
if result and not tuple_is_array and isinstance(obj, tuple):
result = False
return result
Note that I've added default parameters because most of the time you might want to consider strings as values, not arrays. Similarly for tuples.
Upvotes: 6
Reputation: 1
preds_test[0] is of shape (128,128,1) Lets check its data type using isinstance() function isinstance takes 2 arguments. 1st argument is data 2nd argument is data type isinstance(preds_test[0], np.ndarray) gives Output as True. It means preds_test[0] is an array.
Upvotes: 0
Reputation: 87
Simply use size
instead of len
!
>>> from numpy import size
>>> N = [2, 3, 5]
>>> size(N)
3
>>> N = array([2, 3, 5])
>>> size(N)
3
>>> P = 5
>>> size(P)
1
Upvotes: 2
Reputation: 2292
Combining @jamylak and @jpaddison3's answers together, if you need to be robust against numpy arrays as the input and handle them in the same way as lists, you should use
import numpy as np
isinstance(P, (list, tuple, np.ndarray))
This is robust against subclasses of list, tuple and numpy arrays.
And if you want to be robust against all other subclasses of sequence as well (not just list and tuple), use
import collections
import numpy as np
isinstance(P, (collections.Sequence, np.ndarray))
Why should you do things this way with isinstance
and not compare type(P)
with a target value? Here is an example, where we make and study the behaviour of NewList
, a trivial subclass of list.
>>> class NewList(list):
... isThisAList = '???'
...
>>> x = NewList([0,1])
>>> y = list([0,1])
>>> print x
[0, 1]
>>> print y
[0, 1]
>>> x==y
True
>>> type(x)
<class '__main__.NewList'>
>>> type(x) is list
False
>>> type(y) is list
True
>>> type(x).__name__
'NewList'
>>> isinstance(x, list)
True
Despite x
and y
comparing as equal, handling them by type
would result in different behaviour. However, since x
is an instance of a subclass of list
, using isinstance(x,list)
gives the desired behaviour and treats x
and y
in the same manner.
Upvotes: 79
Reputation: 2421
I am surprised that such a basic question doesn't seem to have an immediate answer in python. It seems to me that nearly all proposed answers use some kind of type checking, that is usually not advised in python and they seem restricted to a specific case (they fail with different numerical types or generic iteratable objects that are not tuples or lists).
For me, what works better is importing numpy and using array.size, for example:
>>> a=1
>>> np.array(a)
Out[1]: array(1)
>>> np.array(a).size
Out[2]: 1
>>> np.array([1,2]).size
Out[3]: 2
>>> np.array('125')
Out[4]: 1
Note also:
>>> len(np.array([1,2]))
Out[5]: 2
but:
>>> len(np.array(a))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-40-f5055b93f729> in <module>()
----> 1 len(np.array(a))
TypeError: len() of unsized object
Upvotes: 3
Reputation: 1996
Previous answers assume that the array is a python standard list. As someone who uses numpy often, I'd recommend a very pythonic test of:
if hasattr(N, "__len__")
Upvotes: 177
Reputation: 892
Another alternative approach (use of class name property):
N = [2,3,5]
P = 5
type(N).__name__ == 'list'
True
type(P).__name__ == 'int'
True
type(N).__name__ in ('list', 'tuple')
True
No need to import anything.
Upvotes: 9
Reputation: 1681
You can check data type of variable.
N = [2,3,5]
P = 5
type(P)
It will give you out put as data type of P.
<type 'int'>
So that you can differentiate that it is an integer or an array.
Upvotes: 2
Reputation: 21690
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False
Upvotes: 3
Reputation: 34493
While, @jamylak's approach is the better one, here is an alternative approach
>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Upvotes: 34