Conversione di tipi numpy in tipi di pitone nativi


238

Se ho un dtype intorpidito, come posso convertirlo automaticamente nel tipo di dati Python più vicino? Per esempio,

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

Potrei provare a trovare una mappatura di tutti questi casi, ma numpy fornisce un modo automatico di convertire i suoi tipi nei tipi di pitone nativi più vicini possibili? Questa mappatura non deve essere esaustiva, ma dovrebbe convertire i tipi comuni che hanno un analogo python vicino. Penso che questo accada già da qualche parte in intorpidimento.

Risposte:


325

Utilizzare val.item()per convertire la maggior parte dei valori NumPy in un 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'>
...

(Un altro metodo è np.asscalar(val), tuttavia è deprecato da NumPy 1.16).


Per i curiosi, per creare una tabella di conversioni di scalari di array NumPy per il tuo 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

Ci sono alcuni tipi NumPy che non hanno nativa Python equivalente su alcuni sistemi, tra cui: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoublee longfloat. Questi devono essere convertiti nell'equivalente NumPy più vicino prima dell'uso .item().


Sto usando i panda (0.23.0). Almeno per quella versione, np.str non ha il metodo .item () quindi l'unico modo che ho visto è stato quello di avvolgere .item () all'interno di un blocco try.
Robert Lugg,

3
@RobertLugg np.strnon è un tipo Numpy np.str is str, quindi è solo un alias di un tipo Python standard. Stessa cosa con np.float, np.int, np.bool, np.complex, e np.object. I tipi Numpy hanno un finale _, ad es np.str_.
Mike T,

Capisco. Quindi il problema è "sarebbe bello se" potessi fare: np.float64(0).item()e anche np.float(0).item(). In altre parole, per i casi in cui è noto cosa fare, supportare il .item()metodo anche se restituisce semplicemente lo stesso valore. In questo modo potrei applicare .item()su scalari molto più intorpiditi senza involucro speciale. Allo stato attuale, i concetti apparentemente paralleli differiscono a causa dell'implementazione sottostante. Capisco perfettamente perché è stato fatto. Ma è un fastidio per l'utente della biblioteca.
Robert Lugg,

45

mi sono ritrovato ad avere un insieme misto di tipi intorpiditi e pitone standard. poiché tutti i tipi intorpiditi derivano numpy.generic, ecco come è possibile convertire tutto in tipi standard di Python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)

5
Come nota la risposta accettata , NumPy 1.16 ha deprecato il np.asscalar()metodo. Perché? Probabilmente per nessuna ragione evidentemente buona. Nonostante un decennio di relativa stabilità, l'API NumPy è ora un obiettivo mobile instabile che richiede una manutenzione costante dalle applicazioni a valle. Almeno ci hanno lasciato il item()metodo ... per ora.
Cecil Curry,

Metodo asscalar è deprezzato dal v1.6 di NumPy
Eswar

Puoi facilmente sostituire la risposta con if isinstance(o, numpy.generic): return o.item() raise TypeErrore si trasforma nuovamente in una risposta non deprecata: D
Buggy

19

Se vuoi convertire (numpy.array O numpy scalare O tipo nativo O numpy.darray) in tipo nativo puoi semplicemente fare:

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

tolist convertirà il tuo scalare o array in tipo nativo python. La funzione lambda predefinita si occupa del caso in cui il valore è già nativo.


2
Approccio più pulito per tipi misti (nativi e non nativi), ben fatto! E per quelli che si chiedono, sì, tolist restituisce solo un singolo valore (lo scalare) quando lo chiami su un singolo valore, non un elenco come potresti pensare. Vale la pena notare che il modo più semplice per scrivere la lambda è lambda: valuedato che non vogliamo alcun input.
fgblomqvist,

getattr+ tolistcombo non è solo universale, ma anche vettorializzato! (unlinke .item ())
mirekphd,

11

Che ne dite di:

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

1
Cito quel tipo di soluzione come possibilità alla fine della mia domanda. Ma sto cercando una soluzione sistematica piuttosto che una hard-coded che copra solo alcuni dei casi. Ad esempio, se numpy aggiungesse altri tipi in futuro, la soluzione si spezzerebbe. Quindi non sono contento di quella soluzione.
Conradlee,

Il numero di possibili tipi è illimitato. Considerare np.dtype('mint8')qualsiasi numero intero positivo m. Non può esserci una mappatura esaustiva. (Anche io non credo che ci sia una funzione incorporata per fare questa conversione per te. Potrei sbagliarmi, ma non la penso così :))
unutbu,

2
Python associa i tipi numpy ai tipi Python, non sono sicuro di come, ma mi piacerebbe usare qualunque metodo facciano. Penso che questo debba accadere per consentire, ad esempio, la moltiplicazione (e altre operazioni) tra tipi di numpy e tipi di pitone. Immagino che il loro metodo non mappi esaurientemente tutti i possibili tipi intorpiditi, ma almeno quelli più comuni dove ha senso.
Conradlee,

Non funziona in modo coerente: >>> 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]come vedi non tutti i valori sono stati convertiti correttamente.
Alex F,

seguendo il mio commento precedente, stranamente funziona, anche se avrei dovuto mettere il round sul tipo nativo Python invece che sul tipo nativo 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()è un approccio più generale per raggiungere questo obiettivo. Funziona in qualsiasi tipo primitivo e anche in matrici o matrici.

In realtà non produce un elenco se chiamato da tipi primitivi:

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

8

Puoi anche chiamare il item()metodo dell'oggetto che vuoi convertire:

>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>

6

Penso che puoi semplicemente scrivere la funzione di conversione di tipo generale in questo modo:

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

Ciò significa che non esistono elenchi fissi e il codice verrà ridimensionato con più tipi.


Sai dove si trova il codice sorgente per la parte del metodo tolist () che mappa i tipi numpy sui tipi python? Ho dato una rapida occhiata ma non sono riuscito a trovarlo.
Conradlee,

Questo è un po 'di un hack quello che sto facendo è generare un numpy.ndarraycon 1 zero usando zeros()e chiamando la ndarrays tolist()funzione per convertire in tipi nativi. Una volta in tipi nativi chiedo il tipo e restituirlo. tolist()è una sparntion dindarray
Matt Alcock il

Sì, lo vedo --- funziona per quello che voglio e quindi ho accettato la tua soluzione. Ma mi chiedo come tolist () faccia il suo lavoro nel decidere in quale tipo lanciare, e non sono sicuro di come trovare la fonte.
Conradlee,

numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 è dove è documentata la funzione. Ho pensato che ispezionare potesse essere in grado di aiutare a trovare più informazioni ma nessuna gioia. Il passo successivo ho provato a clonare github.com/numpy/numpy.git ed eseguire grep -r 'tolist' numpy. (ancora in corso, numpy è enorme!)
Matt Alcock,

3

numpy mantiene tali informazioni in una mappatura esposta in typeDictmodo da poter fare qualcosa di simile al seguente:

>>> 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 vuoi i tipi reali di Python piuttosto che i loro nomi, puoi fare ::

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

3

Mi dispiace venire in ritardo alla parte, ma stavo guardando un problema di conversione solo in numpy.float64Python normale float. Ho visto 3 modi per farlo:

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

Ecco i tempi rilevanti di 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

Suona come float(npValue)sembra molto più veloce.


1

Il mio approccio è un po 'forte, ma sembra giocare bene in tutti i casi:

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

Vedo che questo è essenzialmente lo stesso della risposta di Matt Alcock.
Simon Streicher,

1

Una nota a margine sugli scalari di array per coloro che non necessitano di conversione automatica e conoscono il dtype intorpidito del valore:

Gli scalari di array differiscono dagli scalari di Python, ma per la maggior parte possono essere usati in modo intercambiabile (l'eccezione principale è per le versioni di Python precedenti a v2.x, dove gli scalari di array interi non possono fungere da indici per elenchi e tuple). Vi sono alcune eccezioni, ad esempio quando il codice richiede attributi molto specifici di uno scalare o quando verifica specificamente se un valore è uno scalare di Python. Generalmente, i problemi possono essere facilmente risolti convertendo esplicitamente gli scalari di array in scalari di Python, usando la corrispondente funzione di tipo Python (ad es. Int, float, complex, str, unicode).

fonte

Pertanto, nella maggior parte dei casi la conversione potrebbe non essere affatto necessaria e lo scalare array può essere utilizzato direttamente. L'effetto dovrebbe essere identico all'utilizzo dello scalare 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

Ma se, per qualche motivo, è necessaria la conversione esplicita, utilizzare la funzione integrata Python corrispondente è la strada da percorrere. Come mostrato nell'altra risposta, è anche più veloce dello scalare di arrayitem() metodo .


0

Traduci l'intero ndarray invece un oggetto dati unità:

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

Tuttavia, ci vogliono alcuni minuti quando si gestiscono grandi frame di dati. Cerco anche una soluzione più efficiente. Spero una risposta migliore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.