Перетворення numpy-типів в нативні типи python


238

Якщо я маю numty dtype, як я автоматично конвертую його в найближчий тип даних python? Наприклад,

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

Я міг би спробувати придумати картування всіх цих випадків, але чи дає numpy якийсь автоматичний спосіб перетворення своїх dtypes у найближчі можливі типи python? Це відображення не повинно бути вичерпним, але воно повинно перетворити загальні типи, що мають близький аналог пітона. Я думаю, що це вже відбувається десь у нутрії.

Відповіді:


325

Використовуйте val.item()для перетворення більшості значень NumPy в початковий тип Python:

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

(Інший метод є np.asscalar(val), проте він застарілий з часу NumPy 1.16).


Для допитливих скласти таблицю перетворень скалярів масивів NumPy для вашої системи:

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

Є кілька типів Numpy , які не мають рідної еквівалента Python на деяких системах, в тому числі: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoubleі longfloat. Перед використанням їх потрібно перетворити в найближчий еквівалент NumPy .item().


Я використовую панди (0,23,0). Принаймні для цієї версії, np.str не має методу .item (), тому єдиним способом, який я бачив, було загортання .item () у спробу блоку.
Роберт Лугг

3
@RobertLugg np.str- це не тип Numpy, тобто np.str is str, це лише псевдонім до стандартного типу Python. Те ж саме з np.float, np.int, np.bool, np.complex, і np.object. Типи Numpy мають кінцеві _, наприклад np.str_.
Майк Т

Я розумію. Отже, питання "було б добре, якби" я міг би зробити: np.float64(0).item()і також np.float(0).item(). Іншими словами, у випадках, коли відомо, що робити, підтримуйте .item()метод, навіть якщо він просто повертає те саме значення. Таким чином я міг подати заявки .item()на набагато більш товсті скаляри без спеціального корпусу. Насправді, здавалося б, паралельні поняття різняться внаслідок основної реалізації. Я повністю розумію, чому це було зроблено. Але це прикро для користувача бібліотеки.
Роберт Лугг

45

я опинився зі змішаним набором nummy типів та стандартним python. як походять усі типи numpy numpy.generic, ось як ви можете конвертувати все в стандартні типи python:

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

5
Як зазначається у прийнятій відповіді , метод NumPy 1.16 застаріло np.asscalar(). Чому? Напевно, без поважної причини. Незважаючи на десятирічну відносну стабільність, API NumPy тепер є нестабільною рухомою ціллю, що вимагає постійного обслуговування з подальших програм. Принаймні, вони залишили нам item()метод ... поки що.
Сесіль Карі

asscalar метод амортизації , оскільки v1.6 з NumPy
Есвар

Ви можете легко замінити відповідь, if isinstance(o, numpy.generic): return o.item() raise TypeErrorі вона знову перетворюється на непридатну відповідь: D
Баггі

19

Якщо ви хочете перетворити (numpy.array АБО numpy скалярний чи OR нативний тип АБО numpy.darray) в початковий тип, ви можете просто зробити:

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

tolist перетворить ваш скаляр або масив у рідний тип python. Функція лямбда за замовчуванням піклується про випадок, коли значення вже є власним.


2
Найпростіший підхід для змішаних типів (рідного та неродного), молодець! А для тих, хто дивується, так, tolist просто повертає єдине значення (скалярне), коли ви називаєте це одним значенням, а не списком, як ви могли подумати. Варто зазначити, що найпростіший спосіб написання лямбда - це те, що lambda: valueми не хочемо ніяких вводів.
fgblomqvist

getattr+ tolistкомбо не тільки універсальний, але навіть векторизований! (unlinke .item ())
mirekphd

11

Як щодо:

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
Я згадую такий тип рішення як можливість наприкінці мого питання. Але я шукаю системне рішення, а не жорстке кодування, яке охоплює лише декілька випадків. Наприклад, якщо numpy в майбутньому додасть більше типів, ваше рішення буде порушено. Тож я не задоволений таким рішенням.
conradlee

Кількість можливих типів не обмежено. Розглянемо np.dtype('mint8')будь-яке додатне ціле число m. Не може бути вичерпного відображення. (Я також не вірю, що є вбудована функція, щоб зробити це перетворення для вас. Я можу помилитися, але я не думаю, що так :))
unutbu

2
Python відображає numty-типи типів python, я не впевнений, як, але я хотів би використовувати будь-який метод, який вони роблять. Я думаю, що це повинно статися, щоб дозволити, наприклад, множення (та інші операції) між типовими dtypes та python типами. Я здогадуюсь, що їх метод не вичерпно відображає всі можливі типоподібні типи, але, принаймні, найбільш поширені, де це має сенс.
conradlee

Це не працює послідовно: >>> 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]Як бачите, не всі значення були правильно перетворені.
Алекс F

після мого попереднього коментаря, як не дивно, це працює, хоча я б хотів, хоча вам потрібно буде поставити раунд на рідний тип Python замість рідного типу 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()є більш загальним підходом до цього. Він працює в будь-якому примітивному типі, а також у масивах чи матрицях.

Я насправді не дає списку, якщо його викликають з примітивних типів:

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

Ви також можете викликати item()метод об'єкта, який ви хочете перетворити:

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

6

Я думаю, що ви можете просто написати загальну функцію перетворення типу типу:

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

Це означає, що немає фіксованих списків, і ваш код буде масштабуватися з більшістю типів.


Чи знаєте ви, де знаходиться вихідний код для частини методу tolist (), який відображає типи numpy на типи python? Я швидко подивився, але не зміг його знайти.
conradlee

Це трохи хак, що я роблю, це генерування в ньому numpy.ndarrayнуля 1, використовуючи zeros()функцію виклику, ndarrays tolist()щоб перетворити в натні типи. Опинившись в рідних типах, я прошу повернути його типу. tolist()- це fucntion of thendarray
Метт Алькок

Так, я бачу, що --- це працює для того, що я хочу, і тому я прийняв ваше рішення. Але мені цікаво, як tolist () виконує свою роботу, вирішуючи, який тип можна подати, і я не знаю, як знайти джерело.
conradlee

numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 - це те, де функція задокументована. Я думав, що огляд може допомогти знайти більше інформації, але ніякої радості. Наступним кроком я спробував клонувати github.com/numpy/numpy.git і запустити grep -r 'tolist' numpy. (все ще триває, нудота велика!)
Метт Алькок

3

numpy утримує цю інформацію в відображенні, typeDictтак як ви можете зробити щось подібне нижче:

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

Якщо вам потрібні фактичні типи python, а не їх імена, ви можете:

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

Вибачте, що почасти запізнився частково, але я розглядав проблему переходу лише numpy.float64до звичайного Python float. Я бачив 3 способи зробити це:

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

Ось відповідні таймінги від 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

Це здається, float(npValue)схоже, набагато швидше.


1

Мій підхід трохи сильний, але, здається, грає добре для всіх випадків:

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

Використання:

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

Я бачу, що це по суті те саме, що відповідь Метта Алькока.
Саймон

1

Побічна примітка про скалярні масиви для тих, хто не потребує автоматичного перетворення та знає нумерований тип значення:

Скалярні масиви відрізняються від скалярів Python, але здебільшого вони можуть використовуватися взаємозамінно (основний виняток - це версії Python, старші за v2.x, де цілочисельні скаляри масиву не можуть виступати в якості індексів для списків та кортежів). Є деякі винятки, наприклад, коли код вимагає дуже конкретних атрибутів скаляра або коли він спеціально перевіряє, чи є значення скаляром Python. Як правило, проблеми легко вирішуються шляхом явного перетворення скалярів масивів у скаляри Python, використовуючи відповідну функцію типу Python (наприклад, int, float, complex, str, unicode).

Джерело

Таким чином, для більшості випадків конверсія може взагалі не потрібна, і скаляр масиву може використовуватися безпосередньо. Ефект повинен бути ідентичним використанню скалярів 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

Але якщо з якихось причин потрібне явне перетворення, використання відповідної вбудованої функції Python - це шлях. Як показано в іншій відповіді, це також швидше, ніж скалярний item()метод масиву .


0

Перекладіть весь ndarray замість одного об'єкта даних одиниці:

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

Однак для обробки великих фреймів даних потрібно кілька хвилин. Я також шукаю більш ефективне рішення. Сподіваюся, що краща відповідь.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.