Como identificar tipos entorpecidos em python?

100

Como alguém pode determinar com segurança se um objeto tem um tipo entorpecido?

Percebo que essa pergunta vai contra a filosofia da digitação duck, mas a ideia é garantir que uma função (que usa scipy e numpy) nunca retorne um tipo numpy, a menos que seja chamada com um tipo numpy. Isso surge na solução de outra questão, mas acho que o problema geral de determinar se um objeto tem um tipo numpy está longe o suficiente da questão original para que eles sejam separados.

Douglas B. Staple
fonte
Uma pergunta: se você (ou, digamos, scipy) definir um tipo que subclasse um tipo numpy, isso deveria contar ou não? (Eu acredito que você não pode criar subclasses de tipos numpypy em Python, mas você pode em um módulo C, e eu acho que você também pode criar subclasses de tipos numpypy em PyPy ... então provavelmente não importa, mas não é inconcebível que pudesse.)
abarnert em
Eu não tinha pensado nisso; basicamente, seu comentário aponta que a questão é mais difícil do que o esperado. Honestamente, esse tipo de consideração de alto nível é um exagero para a minha situação. Para uma resposta geral e portátil, eu diria que, desde que o comportamento seja definido, está tudo bem.
Douglas B. Staple

Respostas:

116

Use a typefunção integrada para obter o tipo e, em seguida, você pode usar a __module__propriedade para descobrir onde foi definido:

>>> import numpy as np
a = np.array([1, 2, 3])
>>> type(a)
<type 'numpy.ndarray'>
>>> type(a).__module__
'numpy'
>>> type(a).__module__ == np.__name__
True
abarnert
fonte
por exemplo, numpy.ma.MaskedArray não é um tipo numpy o suficiente?
panda-34 de
Se você quiser qualquer coisa em numpy. *, Basta percorrer o pacote pai do módulo. (Nesse ponto, você obviamente deseja envolvê-lo em uma função.) E se você quiser que os DataFrames do pandas contem como numpyish, adicione um ou para testar isso. E assim por diante. O que quero dizer é que você precisa saber o que está realmente pedindo quando quer fazer algo tão incomum como a troca de tipo manual solta, mas uma vez que você sabe, é fácil de implementar.
abarnert de
1
Essa solução parece muito pouco otimista, contando com atributos ocultos. Mas talvez seja apenas uma questão de gosto?
j08lue
2
@ j08lue Não são atributos ocultos, são atributos especiais documentados. É, no entanto, não-pontiagudo, mas acho que isso é inerente ao problema. (E eu acho que é um ponto forte do Python que, quando você quer fazer algo que a linguagem desencoraja, a melhor solução geralmente é visivelmente feia o suficiente para dizer que você está fazendo algo que normalmente é uma má ideia.)
abarnert
69

A solução que encontrei é:

isinstance(y, (np.ndarray, np.generic) )

No entanto, não está 100% claro que todos os tipos numpy têm a garantia de ser np.ndarrayou np.generic, e essa provavelmente não é uma versão robusta.

Douglas B. Staple
fonte
1
Suponho que você possa filtrar dir(numpy)por tipos e funções embutidas (e classes, mas não acho que tenha) e usar isso para gerar uma tupla isinstancecontra, o que seria robusto. (Eu acredito que você pode passar funções embutidas para isinstance sejam eles realmente construtores de tipo ou não, mas você teria que verificar isso.)
abarnert
Sim, todos eles devem ser subclasses desses dois AFAIK.
Seberg
@seberg Obrigado. Certamente parece ser o caso por enquanto, mas a documentação do Python não é muito clara sobre isso e pode ser concebida de maneira diferente no futuro.
Douglas B. Staple,
19

Pergunta antiga, mas encontrei uma resposta definitiva com um exemplo. Não custa nada manter as perguntas atualizadas, pois tive o mesmo problema e não encontrei uma resposta clara. A chave é certificar-se de que você numpyimportou e, em seguida, execute o isinstancebool. Embora possa parecer simples, se você estiver fazendo alguns cálculos em diferentes tipos de dados, essa pequena verificação pode servir como um teste rápido antes de iniciar alguma operação vetorial entorpecida.

##################
# important part!
##################

import numpy as np

####################
# toy array for demo
####################

arr = np.asarray(range(1,100,2))

########################
# The instance check
######################## 

isinstance(arr,np.ndarray)
Linwoodc3
fonte
9

Na verdade, isso depende do que você está procurando.

  • Se você quiser testar se uma sequência é realmente a ndarray, isinstance(..., np.ndarray)provavelmente a é o mais fácil. Certifique-se de não recarregar o numpy em segundo plano, pois o módulo pode ser diferente, mas caso contrário, você deve estar bem. MaskedArrays, matrix, recarraySão todas as subclasses de ndarray, assim que você deve ser definido.
  • Se você quiser testar se um escalar é um escalar numpy, as coisas ficam um pouco mais complicadas. Você pode verificar se ele tem um shapee um dtypeatributo. Você pode compará dtype-los aos dtypes básicos, cuja lista você pode encontrar em np.core.numerictypes.genericTypeRank. Observe que os elementos desta lista são strings, então você terá que fazer um tested.dtype is np.dtype(an_element_of_the_list)...
Pierre GM
fonte
+1. Se você está realmente procurando por algo além de "é um numpytipo" e pode definir o que é esse algo, isso é melhor do que as outras respostas. E na maioria dos casos, você deve procurar algo específico que possa definir.
abarnert em
8

Para obter o tipo, use a typefunção embutida. Com o inoperador, você pode testar se o tipo é um tipo numpy, verificando se ele contém a string numpy;

In [1]: import numpy as np

In [2]: a = np.array([1, 2, 3])

In [3]: type(a)
Out[3]: <type 'numpy.ndarray'>

In [4]: 'numpy' in str(type(a))
Out[4]: True

(Este exemplo foi executado em IPython , a propósito. Muito útil para uso interativo e testes rápidos.)

Roland Smith
fonte
2
Isso funciona, mas se você definir um tipo chamado, digamos, "numpygroup", você obterá falsos positivos. Além disso, dependendo da sequência de caracteres de representação dos tipos, é uma má ideia se você puder evitá-lo - e, neste caso, você pode. Em vez disso, observe seu módulo.
abarnert
Usar o módulo é de fato uma solução melhor.
Roland Smith
Regex poderia ser usado
omkaartg
@ Omkaar.K Regex poderia ser usado para quê? Fazer exatamente a mesma verificação de uma maneira um pouco mais complicada?
Aberto de
@abamert "poderia" é o que eu disse, também regex pode parecer complicado para tarefas simples como essas, mas é extremamente útil para tarefas de processamento de strings grandes, portanto, não é uma má ideia aprendê-lo. Eu acho que você já sabe disso, já que seu portfólio o retrata como um programador sênior?
omkaartg
3

Observe que o type(numpy.ndarray)é o typepróprio e esteja atento aos tipos booleanos e escalares. Não desanime se não for intuitivo ou fácil, é uma dor no início.

Veja também: - https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.dtypes.html - https://github.com/machinalis/mypy-data/tree/master/numpy- mypy

>>> import numpy as np
>>> np.ndarray
<class 'numpy.ndarray'>
>>> type(np.ndarray)
<class 'type'>
>>> a = np.linspace(1,25)
>>> type(a)
<class 'numpy.ndarray'>
>>> type(a) == type(np.ndarray)
False
>>> type(a) == np.ndarray
True
>>> isinstance(a, np.ndarray)
True

Diversão com booleanos:

>>> b = a.astype('int32') == 11
>>> b[0]
False
>>> isinstance(b[0], bool)
False
>>> isinstance(b[0], np.bool)
False
>>> isinstance(b[0], np.bool_)
True
>>> isinstance(b[0], np.bool8)
True
>>> b[0].dtype == np.bool
True
>>> b[0].dtype == bool  # python equivalent
True

Mais diversão com tipos escalares, consulte: - https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.scalars.html#arrays-scalars-built-in

>>> x = np.array([1,], dtype=np.uint64)
>>> x[0].dtype
dtype('uint64')
>>> isinstance(x[0], np.uint64)
True
>>> isinstance(x[0], np.integer)
True  # generic integer
>>> isinstance(x[0], int)
False  # but not a python int in this case

# Try matching the `kind` strings, e.g.
>>> np.dtype('bool').kind                                                                                           
'b'
>>> np.dtype('int64').kind                                                                                          
'i'
>>> np.dtype('float').kind                                                                                          
'f'
>>> np.dtype('half').kind                                                                                           
'f'

# But be weary of matching dtypes
>>> np.integer
<class 'numpy.integer'>
>>> np.dtype(np.integer)
dtype('int64')
>>> x[0].dtype == np.dtype(np.integer)
False

# Down these paths there be dragons:

# the .dtype attribute returns a kind of dtype, not a specific dtype
>>> isinstance(x[0].dtype, np.dtype)
True
>>> isinstance(x[0].dtype, np.uint64)
False  
>>> isinstance(x[0].dtype, np.dtype(np.uint64))
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types
# yea, don't go there
>>> isinstance(x[0].dtype, np.int_)
False  # again, confusing the .dtype with a specific dtype


# Inequalities can be tricky, although they might
# work sometimes, try to avoid these idioms:

>>> x[0].dtype <= np.dtype(np.uint64)
True
>>> x[0].dtype <= np.dtype(np.float)
True
>>> x[0].dtype <= np.dtype(np.half)
False  # just when things were going well
>>> x[0].dtype <= np.dtype(np.float16)
False  # oh boy
>>> x[0].dtype == np.int
False  # ya, no luck here either
>>> x[0].dtype == np.int_
False  # or here
>>> x[0].dtype == np.uint64
True  # have to end on a good note!
Darren Weber
fonte