python: como identificar se uma variável é uma matriz ou um escalar

283

Eu tenho uma função que aceita o argumento NBins. Quero fazer uma chamada para essa função com um escalar 50ou uma matriz [0, 10, 20, 30]. Como posso identificar dentro da função, qual é o tamanho NBins? ou dito de forma diferente, se for um escalar ou um vetor?

Eu tentei isso:

>>> 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()
>>> 

Como você pode ver, eu não posso aplicar lenpara P, uma vez que não é um array .... Existe algo parecido isarrayou isscalarem python?

obrigado

otmezger
fonte
3
Você já tentou testar isso type?
Sukrit Kalra

Respostas:

390
>>> isinstance([0, 10, 20, 30], list)
True
>>> isinstance(50, list)
False

Para suportar qualquer tipo de sequência, marque em collections.Sequencevez de list.

nota : isinstancetambém suporta uma tupla de classes, a verificação type(x) in (..., ...)deve ser evitada e é desnecessária.

Você também pode querer verificar not isinstance(x, (str, unicode))

jamylak
fonte
3
obrigado, eu não imaginava invertendo listpara obter false para escalares ... obrigado
otmezger
3
Embora essa seja uma ótima resposta, também collections.Sequenceé um ABC para string, portanto, isso deve ser levado em consideração. Estou usando algo parecido if type(x) is not str and isinstance(x, collections.Sequence):. Isso não é ótimo, mas é confiável.
bbenne10
2
@ bbenne10 certeza, mas evitar type, e também verificar not isinstance(x, (str, unicode))em Python 2
jamylak
Por que você disse que "o tipo de verificação (x) em (..., ...) deve ser evitado e é desnecessário"? Se você diz, seria muito gentil explicar o porquê, talvez eu não seja o único a se perguntar por que deve ser evitado.
Olivier Pons
118

As respostas anteriores assumem que a matriz é uma lista padrão do python. Como alguém que usa sonolento com frequência, eu recomendaria um teste muito pythonico de:

if hasattr(N, "__len__")
jpaddison3
fonte
12
cordas têm um __len__atributo (então eu acho, não é tecnicamente um tipo escalar)
xofer
20
if hasattr(N, '__len__') and (not isinstance(N, str))seria responsável por seqüências de caracteres.
Thucydides411
1
Também são responsáveis por Dict em Python 3
de Bruno Henrique
44

Combinando as respostas @jamylak e @ jpaddison3, se você precisar ser robusto com matrizes numpy como entrada e manipulá-las da mesma maneira que as listas, use

import numpy as np
isinstance(P, (list, tuple, np.ndarray))

Isso é robusto contra subclasses de matrizes de lista, tupla e numpy.

E se você quiser ser robusto com todas as outras subclasses de sequência (não apenas lista e tupla), use

import collections
import numpy as np
isinstance(P, (collections.Sequence, np.ndarray))

Por que você deve fazer as coisas dessa maneira isinstancee não comparar type(P)com um valor-alvo? Aqui está um exemplo, onde criamos e estudamos o comportamento de NewList, uma subclasse trivial da lista.

>>> 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

Apesar xe ycomparando como iguais, lidar com eles typeresultaria em um comportamento diferente. No entanto, uma vez que xé uma instância de uma subclasse de list, using isinstance(x,list)fornece o comportamento e os tratamentos desejados xe yda mesma maneira.

scottclowe
fonte
Esta é a resposta que mais se adequou às minhas necessidades. Acabei de adicionar também. Porque eu não quero ser robusto contra ditados. isinstance(P, (list, tuple, set, np.ndarray))
Santiago
32

Existe um equivalente a isscalar () em numpy? Sim.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
jmhl
fonte
6
Seria melhor e um exemplo: >>> np.isscalar('abcd')retorna True.
Syrtis Major
obrigado! este é um exemplo muito mais geral do que qualquer um dos itens acima e deve ser preferido. É também uma resposta direta à pergunta do OP.
Cristóbal Sifón
1
Agradável. Embora um problema seja que isscalar (None) retorne False. O Numpy implementa isso comoreturn (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))
Shital Shah
5
Não, infelizmente. A numpy.isscalar()função sofre várias falhas irreconciliáveis ​​de design e provavelmente será preterida em alguma revisão futura. Parafraseando a documentação oficial : "Em quase todos os casos, np.ndim(x) == 0deve ser usado em vez de np.isscaler(x), pois o primeiro também retornará corretamente true para matrizes 0d". Uma alternativa para a frente-compatível robusto para numpy.isscalar()seria, assim, para embrulhar trivialmente numpy.ndim(): por exemplo,def is_scalar(obj): return np.ndim(obj) == 0
Cecil Curry
Na verdade, isso não deve ser votado porque np.isscalaré confuso. Doc oficial sugeriu o uso de np.array.ndimtodos os lugares, ou seja, np.isscalar(np.array(12))é False enquanto ele deve ser considerado como escalar uma vez np.array(12).ndimé 0.
knh190
17

Embora a abordagem do @ jamylak seja a melhor, aqui está uma abordagem alternativa

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Sukrit Kalra
fonte
2
Seria ótimo se a pessoa que rebaixou a resposta também tivesse uma razão.
Sukrit Kalra
Eu realmente votei, mas percebi que ele não funcionava no 2.7: >>> p = [] >>> type (p) in (list) Traceback (última chamada mais recente): Arquivo "<stdin>" , linha 1, em <module>
Oleg Gryb 12/06
@OlegGryb: Tente type(p) in (list, ).
Sukrit Kalra
ah, é uma tupla à direita, não uma lista, entendi, obrigado e funciona agora. Lamento, eu não posso upvote 2 vezes - a melhor solução até agora :)
Oleg Gryb
3

Outra abordagem alternativa (uso da propriedade do nome da classe ):

N = [2,3,5]
P = 5

type(N).__name__ == 'list'
True

type(P).__name__ == 'int'
True

type(N).__name__ in ('list', 'tuple')
True

Não há necessidade de importar nada.

Marek
fonte
3

Aqui está a melhor abordagem que eu encontrei: Verifique a existência de __len__e __getitem__.

Você pode perguntar por quê? Os motivos incluem:

  1. O método popular isinstance(obj, abc.Sequence)falha em alguns objetos, incluindo o Tensor do PyTorch, porque eles não são implementados __contains__.
  2. Infelizmente, não há nada nas coleções do Python.abc que verifique apenas __len__e __getitem__que considero métodos mínimos para objetos do tipo array.
  3. Funciona em lista, tupla, ndarray, tensor etc.

Portanto, sem mais delongas:

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

Observe que eu adicionei parâmetros padrão, porque na maioria das vezes você pode considerar cadeias de caracteres como valores, não matrizes. Da mesma forma para tuplas.

Shital Shah
fonte
2
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False
suhailvs
fonte
2

Você pode verificar o tipo de dados da variável.

N = [2,3,5]
P = 5
type(P)

Ele fornecerá o tipo de dados P.

<type 'int'>

Para que você possa diferenciar que é um número inteiro ou uma matriz.

unnati patil
fonte
2

Estou surpreso que uma pergunta tão básica não pareça ter uma resposta imediata em python. Parece-me que quase todas as respostas propostas usam algum tipo de verificação de tipo, que geralmente não é recomendada em python e parecem restritas a um caso específico (elas falham com tipos numéricos diferentes ou objetos iteráveis ​​genéricos que não são tuplas ou listas).

Para mim, o que funciona melhor é importar numpy e usar array.size, por exemplo:

>>> 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

Observe também:

>>> len(np.array([1,2]))

Out[5]: 2

mas:

>>> 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
Vincenzooo
fonte
Também estou surpreso que nenhum deles pareça lidar com geradores também.
RhysC
2

Basta usar em sizevez de 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
Mathieu Villion
fonte
2
NameError: nome 'tamanho' não está definido
thang
1
Isso é verdade. Eu estava usando tamanho numpy sem perceber. Você precisa: do tamanho de importação numpy
Mathieu Villion 4/16
2
np.size(5)e np.size([5])são ambos == 1, portanto, isso não distingue corretamente o tipo (ou seja, identifica um escalar), que acredito ser o objetivo.
michael
Esta é uma observação interessante. A pergunta original refere-se a isscalar, que é uma função do Matlab. No Matlab, não há absolutamente nenhuma diferença entre um escalar e um array de tamanho 1, seja um vetor ou um array N-dim. IMHO, isso é uma vantagem para o Matlab.
Mathieu Villion
0

preds_test [0] é da forma (128,128,1) Permite verificar seu tipo de dados usando a função isinstance () isinstance recebe 2 argumentos. O 1º argumento é dados O 2º argumento é o tipo de dados isinstance (preds_test [0], np.ndarray) fornece Output como True. Isso significa que o preds_test [0] é uma matriz.

Sumanth Meenan
fonte
0

Para responder à pergunta no título, uma maneira direta de saber se uma variável é escalar é tentar convertê-la em um ponto flutuante. Se você conseguir TypeError, não é.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
Puck
fonte