Konvertieren von numpy dtypes in native Python-Typen

237

Wie konvertiere ich einen numpy dtype automatisch in den nächsten Python-Datentyp? Beispielsweise,

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

Ich könnte versuchen, eine Zuordnung all dieser Fälle zu erstellen, aber bietet numpy eine automatische Möglichkeit, seine d-Typen in die nächstmöglichen nativen Python-Typen zu konvertieren? Diese Zuordnung muss nicht vollständig sein, sollte jedoch die gängigen dtypes konvertieren, die ein nahes Python-Analogon haben. Ich denke, das passiert schon irgendwo in Numpy.

conradlee
quelle

Antworten:

325

Verwenden Sie val.item()diese Option, um die meisten NumPy-Werte in einen nativen Python-Typ zu konvertieren:

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

(Eine andere Methode ist np.asscalar(val)jedoch seit NumPy 1.16 veraltet).


Für Neugierige, um eine Tabelle mit Konvertierungen von NumPy-Array-Skalaren für Ihr System zu erstellen :

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

Es gibt ein paar NumPy Typen , die keine native Python Äquivalent auf einigen Systemen, einschließlich: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoubleund longfloat. Diese müssen vor der Verwendung in das nächste NumPy-Äquivalent konvertiert werden .item().

Mike T.
quelle
Ich benutze Pandas (0.23.0). Zumindest für diese Version hat np.str nicht die .item () -Methode, daher sah ich nur, dass .item () in einen try-Block eingeschlossen wurde.
Robert Lugg
3
@RobertLugg np.strist kein Numpy-Typ, dh np.str is stres ist nur ein Alias ​​für einen Standard-Python-Typ. Das Gleiche gilt für np.float, np.int, np.bool, np.complex, und np.object. Die Numpy-Typen haben ein Trailing _, z np.str_.
Mike T
Ich verstehe. Das Problem ist also "es wäre schön, wenn" ich tun könnte: np.float64(0).item()und auch np.float(0).item(). Mit anderen Worten, für die Fälle, in denen bekannt ist, was zu tun ist, unterstützen Sie die .item()Methode, auch wenn sie einfach denselben Wert zurückgibt. Auf diese Weise konnte ich .item()ohne spezielle Hülle auf weitaus mehr numpy Skalare auftragen. Scheinbar parallele Konzepte unterscheiden sich aufgrund der zugrunde liegenden Implementierung. Ich verstehe vollkommen, warum dies getan wurde. Aber es ist ein Ärger für den Bibliotheksbenutzer.
Robert Lugg
45

Ich hatte eine gemischte Reihe von Numpy-Typen und Standard-Python. Da alle Numpy-Typen abgeleitet sind numpy.generic, können Sie wie folgt alles in Python-Standardtypen konvertieren:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
quelle
5
Wie die akzeptierte Antwort vermerkt , hat NumPy 1.16 die np.asscalar()Methode abgelehnt . Warum? Wahrscheinlich ohne erkennbaren Grund. Trotz eines Jahrzehnts relativer Stabilität ist die NumPy-API jetzt ein instabiles bewegliches Ziel, das eine ständige Wartung durch nachgeschaltete Anwendungen erfordert. Zumindest haben sie uns die item()Methode überlassen ... für jetzt.
Cecil Curry
asscalar Methode hat seit v1.6 von numpy
Eswar
Sie können die Antwort einfach durch ersetzen if isinstance(o, numpy.generic): return o.item() raise TypeErrorund sie wird wieder zu einer nicht veralteten Antwort: D
Buggy
19

Wenn Sie (numpy.array ODER numpy scalar ODER nativer Typ ODER numpy.darray) in einen nativen Typ konvertieren möchten, können Sie einfach Folgendes tun:

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

tolist konvertiert Ihren Skalar oder Ihr Array in den nativen Python-Typ. Die Standard-Lambda-Funktion kümmert sich um den Fall, dass der Wert bereits nativ ist.

v.thorey
quelle
2
Sauberster Ansatz für gemischte Typen (native und nicht native), gut gemacht! Und für diejenigen, die sich fragen, ja, tolist gibt nur einen einzelnen Wert (den Skalar) zurück, wenn Sie ihn für einen einzelnen Wert aufrufen, nicht für eine Liste, wie Sie vielleicht denken. Bemerkenswert ist, dass der einfachere Weg, das Lambda zu schreiben, darin besteht, dass lambda: valuewir keine Eingaben wollen.
fgblomqvist
getattr+ tolistCombo ist nicht nur universell, sondern sogar vektorisiert! (unlinke .item ())
mirekphd
11

Wie wäre es mit:

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
quelle
1
Ich erwähne diese Art der Lösung als eine Möglichkeit am Ende meiner Frage. Aber ich suche eher nach einer systematischen Lösung als nach einer fest codierten, die nur einige der Fälle abdeckt. Wenn numpy beispielsweise in Zukunft weitere dtypes hinzufügt, ist Ihre Lösung fehlerhaft. Ich bin also nicht zufrieden mit dieser Lösung.
Conradlee
Die Anzahl der möglichen d-Typen ist unbegrenzt. Berücksichtigen Sie np.dtype('mint8')jede positive Ganzzahl m. Es kann keine erschöpfende Zuordnung geben. (Ich glaube auch nicht, dass es eine eingebaute Funktion gibt, um diese Konvertierung für Sie durchzuführen. Ich könnte mich irren, aber ich denke nicht :))
unutbu
2
Python ordnet numpy dtypes Python-Typen zu. Ich bin mir nicht sicher, wie, aber ich würde gerne die Methode verwenden, die sie verwenden. Ich denke, dies muss geschehen, um beispielsweise die Multiplikation (und andere Operationen) zwischen numpy dtypes und python types zu ermöglichen. Ich denke, ihre Methode bildet nicht alle möglichen Numpy-Typen erschöpfend ab, aber zumindest die häufigsten, bei denen es Sinn macht.
Conradlee
Es funktioniert nicht konsistent: >>> 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]Wie Sie sehen, wurden nicht alle Werte korrekt konvertiert.
Alex F
Nach meinem vorherigen Kommentar funktioniert dieser seltsamerweise, obwohl ich hätte, obwohl Sie die Runde auf den nativen Python-Typ anstelle des numpy nativen Typs setzen müssten: >>> 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()ist ein allgemeinerer Ansatz, um dies zu erreichen. Es funktioniert in jedem primitiven Typ und auch in Arrays oder Matrizen.

Ich erhalte keine Liste, wenn ich von primitiven Typen aufgerufen werde:

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

Sie können auch die item()Methode des Objekts aufrufen, das Sie konvertieren möchten:

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

Ich denke, Sie können einfach eine allgemeine Typkonvertierungsfunktion wie folgt schreiben:

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

Dies bedeutet, dass es keine festen Listen gibt und Ihr Code mit mehr Typen skaliert wird.

Matt Alcock
quelle
Wissen Sie, wo sich der Quellcode für den Teil der tolist () -Methode befindet, der Numpy-Typen Python-Typen zuordnet? Ich warf einen kurzen Blick darauf, konnte es aber nicht finden.
Conradlee
Dies ist ein bisschen wie ein Hack, den ich mache, indem ich eine numpy.ndarraymit 1 Null darin generiere zeros()und die ndarrays tolist()Funktion aufrufe, um sie in native Typen zu konvertieren. Einmal in nativen Typen frage ich nach dem Typ und gebe ihn zurück. tolist()ist eine Funktion derndarray
Matt Alcock
Ja, ich sehe das --- es funktioniert für das, was ich will und deshalb habe ich Ihre Lösung akzeptiert. Aber ich frage mich, wie tolist () seine Entscheidung trifft, in welchen Typ es umgewandelt werden soll, und ich bin mir nicht sicher, wie ich die Quelle finden soll.
Conradlee
In numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 wird die Funktion dokumentiert. Ich dachte, inspect könnte helfen, mehr Informationen zu finden, aber keine Freude. Im nächsten Schritt habe ich versucht, github.com/numpy/numpy.git zu klonen und auszuführen grep -r 'tolist' numpy. (noch in Bearbeitung, numpy ist massiv!)
Matt Alcock
3

numpy enthält diese Informationen in einem Mapping, typeDictdamit Sie so etwas wie das Folgende tun können:

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

Wenn Sie die tatsächlichen Python-Typen anstelle ihrer Namen möchten, können Sie Folgendes tun:

>>> {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
quelle
3

Es tut mir leid, dass ich zu spät zum Teil gekommen bin, aber ich habe mir ein Problem mit der Konvertierung nur numpy.float64in reguläres Python angesehen float. Ich habe 3 Möglichkeiten gesehen, das zu tun:

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

Hier sind die relevanten Timings von 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

Es klingt wie float(npValue)scheint viel schneller.

gt6989b
quelle
1

Mein Ansatz ist ein bisschen energisch, scheint aber in allen Fällen gut zu spielen:

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

Verwendung:

>>> 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
quelle
Ich sehe, dass dies im Wesentlichen die gleiche ist wie die Antwort von Matt Alcock.
Simon Streicher
1

Eine Randnotiz zu Array-Skalaren für diejenigen, die keine automatische Konvertierung benötigen und den numpy dtype des Werts kennen:

Array-Skalare unterscheiden sich von Python-Skalaren, können jedoch größtenteils austauschbar verwendet werden (die Hauptausnahme sind Versionen von Python älter als v2.x, bei denen ganzzahlige Array-Skalare nicht als Indizes für Listen und Tupel fungieren können). Es gibt einige Ausnahmen, z. B. wenn Code sehr spezifische Attribute eines Skalars erfordert oder wenn speziell geprüft wird, ob ein Wert ein Python-Skalar ist. Im Allgemeinen können Probleme leicht behoben werden, indem Array-Skalare explizit in Python-Skalare konvertiert werden, wobei die entsprechende Python-Typ-Funktion (z. B. int, float, complex, str, unicode) verwendet wird.

Quelle

In den meisten Fällen ist daher möglicherweise überhaupt keine Konvertierung erforderlich, und der Array-Skalar kann direkt verwendet werden. Der Effekt sollte mit der Verwendung des Python-Skalars identisch sein:

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

Wenn jedoch aus irgendeinem Grund die explizite Konvertierung erforderlich ist, ist die Verwendung der entsprechenden integrierten Python-Funktion der richtige Weg. Wie in der anderen Antwort gezeigt, ist es auch schneller als die Array-Skalarmethode item().

Wombatonfire
quelle
0

Übersetzen Sie stattdessen das gesamte ndarray ein Einheitsdatenobjekt:

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

Bei der Verarbeitung großer Datenrahmen dauert es jedoch einige Minuten. Ich suche auch nach einer effizienteren Lösung. Hoffe eine bessere Antwort.

Qinhong Ma
quelle