Convertendo tipos numpy em tipos nativos de python

238

Se eu tenho um tipo de numpy, como faço para convertê-lo automaticamente no tipo de dados python mais próximo? Por exemplo,

numpy.float32 -> "python float"
numpy.float64 -> "python float"
numpy.uint32  -> "python int"
numpy.int16   -> "python int"

Eu poderia tentar criar um mapeamento de todos esses casos, mas o numpy fornece uma maneira automática de converter seus tipos nos tipos de python nativos mais próximos possíveis? Esse mapeamento não precisa ser exaustivo, mas deve converter os tipos comuns que possuem um análogo python próximo. Eu acho que isso já acontece em algum lugar entorpecido.

conradlee
fonte

Respostas:

325

Use val.item()para converter a maioria dos valores NumPy em um tipo Python nativo:

import numpy as np

# for example, numpy.float32 -> python float
val = np.float32(0)
pyval = val.item()
print(type(pyval))         # <class 'float'>

# and similar...
type(np.float64(0).item()) # <class 'float'>
type(np.uint32(0).item())  # <class 'long'>
type(np.int16(0).item())   # <class 'int'>
type(np.cfloat(0).item())  # <class 'complex'>
type(np.datetime64(0, 'D').item())  # <class 'datetime.date'>
type(np.datetime64('2001-01-01 00:00:00').item())  # <class 'datetime.datetime'>
type(np.timedelta64(0, 'D').item()) # <class 'datetime.timedelta'>
...

(Outro método é np.asscalar(val), no entanto, está obsoleto desde o NumPy 1.16).


Para os curiosos, crie uma tabela de conversões de escalares de array NumPy para o seu sistema:

for name in dir(np):
    obj = getattr(np, name)
    if hasattr(obj, 'dtype'):
        try:
            if 'time' in name:
                npn = obj(0, 'D')
            else:
                npn = obj(0)
            nat = npn.item()
            print('{0} ({1!r}) -> {2}'.format(name, npn.dtype.char, type(nat)))
        except:
            pass

Existem alguns tipos Numpy que não têm equivalente Python nativa em alguns sistemas, incluindo: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoublee longfloat. Eles precisam ser convertidos no equivalente NumPy mais próximo antes do uso .item().

Mike T
fonte
Estou usando pandas (0.23.0). Pelo menos para essa versão, o np.str não possui o método .item (), então a única maneira que vi foi envolver o .item () dentro de um bloco try.
Robert Lugg
3
O @RobertLugg np.strnão é do tipo Numpy, ou seja np.str is str, é apenas um apelido para um tipo Python padrão. Mesmo com np.float, np.int, np.bool, np.complex, e np.object. Os tipos Numpy têm um final _, por exemplo np.str_.
Mike T
Compreendo. Portanto, a questão é "seria bom se" eu pudesse fazer: np.float64(0).item()e também np.float(0).item(). Em outras palavras, para os casos em que se sabe o que fazer, suporte o .item()método mesmo se ele simplesmente retornar o mesmo valor. Dessa forma, eu poderia aplicar .item()em escalares muito mais entorpecidos sem revestimento especial. No entanto, conceitos aparentemente paralelos diferem devido à implementação subjacente. Entendo perfeitamente por que isso foi feito. Mas é um aborrecimento para o usuário da biblioteca.
Robert Lugg
45

me vi tendo um conjunto misto de tipos numpy e python padrão. como todos os tipos numpy derivam numpy.generic, veja como você pode converter tudo em tipos padrão python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
fonte
5
Como observa a resposta aceita , o NumPy 1.16 reprovou o np.asscalar()método. Por quê? Provavelmente por nenhuma razão discernivelmente boa. Apesar de uma década de relativa estabilidade, a API do NumPy agora é um alvo móvel instável que exige manutenção constante de aplicativos a jusante. Pelo menos eles nos deixaram o item()método ... por enquanto.
Cecil Curry
método asscalar depreciou desde v1.6 de numpy
Eswar
Você pode substituir facilmente a resposta if isinstance(o, numpy.generic): return o.item() raise TypeErrore ela se transforma em uma resposta não obsoleta novamente: D
Buggy
19

Se você deseja converter (numpy.array OR numpy escalar OU tipo nativo OR numpy.darray) em tipo nativo, você pode simplesmente:

converted_value = getattr(value, "tolist", lambda: value)()

O tolist converterá seu escalar ou array em tipo nativo python. A função lambda padrão cuida do caso em que o valor já é nativo.

v.thorey
fonte
2
Abordagem mais limpa para tipos mistos (nativos e não nativos), muito bem! E para aqueles que se perguntam, sim, o tolist retorna apenas um único valor (o escalar) quando você o chama em um único valor, não em uma lista como você imagina. É importante notar que a maneira mais simples de escrever o lambda é lambda: valueporque não queremos nenhuma entrada.
Fgblomqvist 12/09/19
getattr+ tolistcombo não é apenas universal, mas também vetorizado! (unlinke .item ())
mirekphd 18/03
11

E se:

In [51]: dict([(d, type(np.zeros(1,d).tolist()[0])) for d in (np.float32,np.float64,np.uint32, np.int16)])
Out[51]: 
{<type 'numpy.int16'>: <type 'int'>,
 <type 'numpy.uint32'>: <type 'long'>,
 <type 'numpy.float32'>: <type 'float'>,
 <type 'numpy.float64'>: <type 'float'>}
unutbu
fonte
1
Menciono esse tipo de solução como uma possibilidade no final da minha pergunta. Mas estou procurando uma solução sistemática em vez de codificada que cubra alguns dos casos. Por exemplo, se numpy adicionar mais tipos de dados no futuro, sua solução será interrompida. Então, eu não estou feliz com essa solução.
conradlee
O número de tipos possíveis é ilimitado. Considere np.dtype('mint8')para qualquer número inteiro positivo m. Não pode haver um mapeamento exaustivo. (Eu também não acredito que há uma função interna para fazer esta conversão para você Eu posso estar errado, mas eu não penso assim :).)
unutbu
2
Python mapeia numpy dtypes para tipos python, não sei como, mas gostaria de usar qualquer método que eles façam. Acho que isso deve acontecer para permitir, por exemplo, multiplicação (e outras operações) entre tipos numpy e tipos python. Eu acho que o método deles não mapeia exaustivamente todos os tipos numpy possíveis, mas pelo menos os mais comuns onde faz sentido.
conradlee
Ele não funciona de forma consistente: >>> print([numpy.asscalar(x) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.6499999999999999, 0.6, 0.55, 0.5, 0.44999999999999996, 0.3999999999999999, 0.35, 0.29999999999999993, 0.25, 0.19999999999999996, 0.1499999999999999, 0.09999999999999998, 0.04999999999999993, 0.0]como você vê, nem todos os valores foram convertidos corretamente.
Alex F
após o meu comentário anterior, estranhamente este funciona, embora eu devesse colocar o round no tipo nativo do Python em vez do tipo nativo do Numpy: >>> print([numpy.asscalar(round(x,2)) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1, 0.05, 0.0]
Alex F
9

tolist()é uma abordagem mais geral para conseguir isso. Funciona em qualquer tipo primitivo e também em matrizes ou matrizes.

Na verdade, eu não produz uma lista se chamado de tipos primitivos:

numpy == 1.15.2

>>> import numpy as np

>>> np_float = np.float64(1.23)
>>> print(type(np_float), np_float)
<class 'numpy.float64'> 1.23

>>> listed_np_float = np_float.tolist()
>>> print(type(listed_np_float), listed_np_float)
<class 'float'> 1.23

>>> np_array = np.array([[1,2,3.], [4,5,6.]])
>>> print(type(np_array), np_array)
<class 'numpy.ndarray'> [[1. 2. 3.]
 [4. 5. 6.]]

>>> listed_np_array = np_array.tolist()
>>> print(type(listed_np_array), listed_np_array)
<class 'list'> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Carlos Santos
fonte
8

Você também pode chamar o item()método do objeto que deseja converter:

>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>
Aryeh Leib Taurog
fonte
6

Eu acho que você pode escrever a função de conversão de tipo geral da seguinte maneira:

import numpy as np

def get_type_convert(np_type):
   convert_type = type(np.zeros(1,np_type).tolist()[0])
   return (np_type, convert_type)

print get_type_convert(np.float32)
>> (<type 'numpy.float32'>, <type 'float'>)

print get_type_convert(np.float64)
>> (<type 'numpy.float64'>, <type 'float'>)

Isso significa que não há listas fixas e seu código será escalado com mais tipos.

Matt Alcock
fonte
Você sabe onde está o código fonte da parte do método tolist () que mapeia tipos numpy para tipos python? Dei uma olhada rápida, mas não consegui encontrar.
conradlee
Isso é um pouco complicado, o que estou fazendo é gerar um numpy.ndarrayzero com ele zeros()e chamar a ndarrays tolist()função para converter em tipos nativos. Uma vez em tipos nativos, peço o tipo e o devolva. tolist()é um fucntion dondarray
Matt Alcock
Sim, eu vejo isso - funciona para o que eu quero e, portanto, aceitei sua solução. Mas eu me pergunto como tolist () faz seu trabalho de decidir em que tipo lançar, e não tenho certeza de como encontrar a fonte.
conradlee
numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 é onde a função está documentada. Pensei que a inspeção poderia ajudar a encontrar mais informações, mas sem alegria. Na próxima etapa, tentei clonar github.com/numpy/numpy.git e executar grep -r 'tolist' numpy. (ainda em andamento, numpy é enorme!)
Matt Alcock
3

numpy mantém essas informações em um mapeamento exposto typeDictpara que você possa fazer algo como o abaixo:

>>> import __builtin__
>>> import numpy as np
>>> {v: k for k, v in np.typeDict.items() if k in dir(__builtin__)}
{numpy.object_: 'object',
 numpy.bool_: 'bool',
 numpy.string_: 'str',
 numpy.unicode_: 'unicode',
 numpy.int64: 'int',
 numpy.float64: 'float',
 numpy.complex128: 'complex'}

Se você quiser os tipos reais de python, em vez de seus nomes, poderá:

>>> {v: getattr(__builtin__, k) for k, v in np.typeDict.items() if k in vars(__builtin__)}
{numpy.object_: object,
 numpy.bool_: bool,
 numpy.string_: str,
 numpy.unicode_: unicode,
 numpy.int64: int,
 numpy.float64: float,
 numpy.complex128: complex}
Meitham
fonte
3

Desculpe chegar atrasado à parte, mas eu estava olhando para um problema de conversão apenas em numpy.float64Python comum float. Eu vi três maneiras de fazer isso:

  1. npValue.item()
  2. npValue.astype(float)
  3. float(npValue)

Aqui estão os horários relevantes do IPython:

In [1]: import numpy as np

In [2]: aa = np.random.uniform(0, 1, 1000000)

In [3]: %timeit map(float, aa)
10 loops, best of 3: 117 ms per loop

In [4]: %timeit map(lambda x: x.astype(float), aa)
1 loop, best of 3: 780 ms per loop

In [5]: %timeit map(lambda x: x.item(), aa)
1 loop, best of 3: 475 ms per loop

Parece que float(npValue)parece muito mais rápido.

gt6989b
fonte
1

Minha abordagem é um pouco enérgica, mas parece ter um bom desempenho em todos os casos:

def type_np2py(dtype=None, arr=None):
    '''Return the closest python type for a given numpy dtype'''

    if ((dtype is None and arr is None) or
        (dtype is not None and arr is not None)):
        raise ValueError(
            "Provide either keyword argument `dtype` or `arr`: a numpy dtype or a numpy array.")

    if dtype is None:
        dtype = arr.dtype

    #1) Make a single-entry numpy array of the same dtype
    #2) force the array into a python 'object' dtype
    #3) the array entry should now be the closest python type
    single_entry = np.empty([1], dtype=dtype).astype(object)

    return type(single_entry[0])

Uso:

>>> type_np2py(int)
<class 'int'>

>>> type_np2py(np.int)
<class 'int'>

>>> type_np2py(str)
<class 'str'>

>>> type_np2py(arr=np.array(['hello']))
<class 'str'>

>>> type_np2py(arr=np.array([1,2,3]))
<class 'int'>

>>> type_np2py(arr=np.array([1.,2.,3.]))
<class 'float'>
Simon Streicher
fonte
Vejo que isso é essencialmente o mesmo que a resposta de Matt Alcock.
Simon Streicher
1

Uma observação lateral sobre escalares de matriz para aqueles que não precisam de conversão automática e conhecem o tipo de valor numpy do valor:

Os escalares de matriz diferem dos escalares de Python, mas na maioria das vezes eles podem ser usados ​​de forma intercambiável (a exceção principal é para versões do Python anteriores à v2.x, onde escalares de matriz inteira não podem atuar como índices para listas e tuplas). Existem algumas exceções, como quando o código requer atributos muito específicos de um escalar ou quando verifica especificamente se um valor é um escalar do Python. Geralmente, os problemas são facilmente resolvidos convertendo explicitamente escalares de array em escalares Python, usando a função do tipo Python correspondente (por exemplo, int, float, complex, str, unicode).

Fonte

Portanto, na maioria dos casos, a conversão pode não ser necessária e o escalar da matriz pode ser usado diretamente. O efeito deve ser idêntico ao uso escalar do Python:

>>> np.issubdtype(np.int64, int)
True
>>> np.int64(0) == 0
True
>>> np.issubdtype(np.float64, float)
True
>>> np.float64(1.1) == 1.1
True

Mas se, por algum motivo, a conversão explícita for necessária, usar a função interna correspondente do Python é o caminho a percorrer. Como mostrado na outra resposta, também é mais rápido que o item()método escalar de array .

wombatonfire
fonte
0

Traduza o ndarray inteiro em vez de um objeto de dados da unidade:

def trans(data):
"""
translate numpy.int/float into python native data type
"""
result = []
for i in data.index:
    # i = data.index[0]
    d0 = data.iloc[i].values
    d = []
    for j in d0:
        if 'int' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        elif 'float' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        else:
            res = j
        d.append(res)
    d = tuple(d)
    result.append(d)
result = tuple(result)
return result

No entanto, leva alguns minutos ao lidar com grandes quadros de dados. Também estou procurando uma solução mais eficiente. Espero uma resposta melhor.

Qinhong Ma
fonte