Konwertowanie typów numpy na rodzime typy python

238

Jeśli mam typ numpy, jak automatycznie przekonwertować go na najbliższy typ danych python? Na przykład,

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

Mógłbym spróbować wymyślić mapowanie wszystkich tych przypadków, ale czy numpy zapewnia jakiś automatyczny sposób konwersji jego typów na najbliższe możliwe rodzime typy python? To mapowanie nie musi być wyczerpujące, ale powinno przekształcić typowe dtypy, które mają bliski analog python. Myślę, że to już zdarza się gdzieś w numpy.

Conradlee
źródło

Odpowiedzi:

325

Służy val.item()do konwersji większości wartości NumPy na rodzimy typ Pythona:

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

(Inna metoda jest np.asscalar(val)jednak przestarzała od NumPy 1.16).


Dla ciekawskich, aby zbudować tabelę konwersji skalarów tablicowych NumPy dla twojego systemu:

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

Istnieje kilka rodzajów NumPy, które nie mają odpowiednika natywną Pythona na niektórych systemach, w tym: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoublei longfloat. Przed użyciem należy je przekonwertować na ich najbliższy odpowiednik NumPy .item().

Mike T.
źródło
Używam pand (0.23.0). Przynajmniej dla tej wersji np.str nie ma metody .item (), więc widziałem tylko sposób na zawinięcie .item () w bloku try.
Robert Lugg,
3
@RobertLugg np.strnie jest typem Numpy, tzn np.str is str. Jest tylko aliasem do standardowego typu Python. To samo z np.float, np.int, np.bool, np.complex, i np.object. Typy Numpy mają końcowe _, np np.str_.
Mike T
Rozumiem. Problem polega na tym, że „byłoby miło, gdybym” mógł zrobić: np.float64(0).item()i także np.float(0).item(). Innymi słowy, w przypadkach, w których wiadomo, co robić, należy wspierać .item()metodę, nawet jeśli po prostu zwraca tę samą wartość. W ten sposób mogłem aplikować .item()na znacznie bardziej numpy skalarach bez specjalnej obudowy. W tej chwili pozornie równoległe koncepcje różnią się ze względu na implementację. Całkowicie rozumiem, dlaczego tak się stało. Ale to irytuje użytkownika biblioteki.
Robert Lugg,
45

odkryłem, że mam mieszany zestaw typów numpy i standardowego pytona. ponieważ wszystkie typy numpy pochodzą numpy.generic, oto jak możesz przekonwertować wszystko na standardowe typy python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
źródło
5
Jak zauważono w przyjętej odpowiedzi , NumPy 1.16 wycofało np.asscalar()metodę. Czemu? Prawdopodobnie bez wyraźnego powodu. Pomimo dekady względnej stabilności, NumPy API jest obecnie niestabilnym ruchomym celem, wymagającym stałej konserwacji od dalszych aplikacji. Przynajmniej zostawili nam item()metodę ... na razie.
Cecil Curry
metoda asscalar straciła na wartości od wersji 1.6 numpy
Eswar
Możesz łatwo zamienić odpowiedź na, if isinstance(o, numpy.generic): return o.item() raise TypeErrora ona ponownie staje się nieaktualną odpowiedzią: D
Buggy
19

Jeśli chcesz przekonwertować (numpy.array LUB numpy skalar LUB typ rodzimy LUB numpy.darray) na typ rodzimy, możesz po prostu zrobić:

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

tolist skonwertuje twój skalar lub tablicę na rodzimy typ Pythona. Domyślna funkcja lambda zajmuje się przypadkiem, w którym wartość jest już natywna.

v.thorey
źródło
2
Najczystsze podejście do typów mieszanych (rodzimych i nienatywnych), dobra robota! A dla tych, którzy zastanawiają się, tak, tolist zwraca tylko jedną wartość (skalar), gdy nazywasz ją jedną wartością, a nie listą, jak mogłoby się wydawać. Warto zauważyć, że najprostszym sposobem napisania lambda jest lambda: valueto, że nie chcemy żadnych danych wejściowych.
fgblomqvist
getattr+ tolistcombo jest nie tylko uniwersalne, ale nawet wektoryzowane! (unlinke .item ())
mirekphd
11

Co powiesz na:

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
źródło
1
Na końcu mojego pytania wspominam o tego rodzaju rozwiązaniu. Ale szukam systematycznego rozwiązania, a nie twardego, które obejmuje tylko kilka przypadków. Na przykład, jeśli numpy doda więcej rodzajów nietypowych w przyszłości, Twoje rozwiązanie się zepsuje. Więc nie jestem zadowolony z tego rozwiązania.
conradlee
Liczba możliwych typów jest nieograniczona. Rozważ np.dtype('mint8')każdą dodatnią liczbę całkowitą m. Nie może być wyczerpującego mapowania. (Nie wierzę też, że istnieje wbudowana funkcja do wykonania tej konwersji dla ciebie. Mogę się mylić, ale nie sądzę :))
unutbu
2
Python mapuje typy numpy na typy python, nie jestem pewien jak, ale chciałbym użyć dowolnej metody, którą robią. Myślę, że to musi się zdarzyć, aby na przykład umożliwić mnożenie (i inne operacje) między typami numpy i typami python. Sądzę, że ich metoda nie wyczerpuje w wyczerpujący sposób wszystkich możliwych typów liczb, ale przynajmniej najczęstszych tam, gdzie ma to sens.
conradlee
Nie działa konsekwentnie: >>> 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]jak widać nie wszystkie wartości zostały poprawnie przekonwertowane.
Alex F,
po moim poprzednim komentarzu, co dziwne, ten działa, chociaż chciałbym, abyś musiał wprowadzić rundę na rodzimym typie Python zamiast rodzimym 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()jest bardziej ogólnym podejściem do osiągnięcia tego celu. Działa w każdym prymitywnym typie, a także w tablicach lub macierzach.

W rzeczywistości nie daje listy, jeśli jest wywoływana z typów pierwotnych:

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
źródło
8

Możesz także wywołać item()metodę obiektu, który chcesz przekonwertować:

>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>
Aryeh Leib Taurog
źródło
6

Myślę, że możesz po prostu napisać funkcję konwersji typu ogólnego tak:

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

Oznacza to, że nie ma ustalonych list, a Twój kod będzie skalowany wraz z większą liczbą typów.

Matt Alcock
źródło
Czy wiesz, gdzie znajduje się kod źródłowy części metody tolist (), która mapuje typy numpy na typy python? Rzuciłem okiem, ale nie mogłem go znaleźć.
conradlee
To trochę hack, co robię, to generowanie numpy.ndarrayz 1 zerem przy użyciu zeros()i wywołanie ndarrays tolist()funkcji w celu konwersji na typy rodzime. Raz w rodzimych typach proszę o typ i zwrot go. tolist()jest fucntion zndarray
Matt Alcocka
Tak, widzę, że --- to działa na to, czego chcę, więc zaakceptowałem twoje rozwiązanie. Zastanawiam się jednak, w jaki sposób tolist () wykonuje swoje zadanie, decydując, w jaki typ rzucić, i nie jestem pewien, jak znaleźć źródło.
conradlee
Numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 to miejsce, w którym funkcja jest udokumentowana. Myślałem, że inspekcja może pomóc znaleźć więcej informacji, ale bez radości. W następnym kroku próbowałem sklonować github.com/numpy/numpy.git i uruchomić grep -r 'tolist' numpy. (wciąż w toku, numpy jest ogromny!)
Matt Alcock
3

numpy przechowuje te informacje w mapowaniu typeDict, abyś mógł zrobić coś takiego:

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

Jeśli chcesz rzeczywistych typów python zamiast ich nazw, możesz:

>>> {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
źródło
3

Przepraszam, że spóźniłem się częściowo, ale patrzyłem na problem z przejściem tylko numpy.float64na zwykły Python float. Widziałem 3 sposoby na zrobienie tego:

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

Oto odpowiednie czasy z 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

Wygląda na to, float(npValue)że wydaje się znacznie szybszy.

gt6989b
źródło
1

Moje podejście jest nieco zdecydowane, ale wydaje się, że gra we wszystkich przypadkach:

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

Stosowanie:

>>> 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
źródło
Widzę, że jest to w zasadzie to samo co odpowiedź Matta Alcocka.
Simon Streicher
1

Dodatkowa uwaga na temat skalarów tablicowych dla tych, którzy nie potrzebują automatycznej konwersji i znają typ numpy wartości:

Skalary tablicowe różnią się od skalarów Pythona, ale w przeważającej części można ich używać zamiennie (głównym wyjątkiem są wersje Pythona starsze niż v2.x, w których skalary liczb całkowitych nie mogą działać jako wskaźniki dla list i krotek). Istnieją pewne wyjątki, na przykład gdy kod wymaga bardzo specyficznych atrybutów skalara lub gdy dokładnie sprawdza, czy wartość jest skalarem Pythona. Zasadniczo problemy można łatwo rozwiązać, jawnie konwertując skalary tablicowe na skalary Pythona, używając odpowiedniej funkcji typu Python (np. Int, float, complex, str, Unicode).

Źródło

Dlatego w większości przypadków konwersja może nie być wcale potrzebna, a skalar macierzy można zastosować bezpośrednio. Efekt powinien być identyczny z użyciem skalara Pythona:

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

Ale jeśli z jakiegoś powodu konieczna jest jawna konwersja, najlepszym rozwiązaniem jest użycie odpowiedniej wbudowanej funkcji Pythona. Jak pokazano w drugiej odpowiedzi, jest również szybszy niż item()metoda skalarna tablicowa .

wombatonfire
źródło
0

Przetłumacz cały ndarray zamiast jednego obiektu danych jednostki:

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

Jednak obsługa dużych ramek danych zajmuje kilka minut. Szukam również bardziej wydajnego rozwiązania. Mam nadzieję na lepszą odpowiedź.

Qinhong Ma
źródło