Преобразование numpy dtypes в нативные типы Python


238

Если у меня есть пустой тип dtype, как мне автоматически преобразовать его в ближайший тип данных python? Например,

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

Я мог бы попытаться придумать отображение всех этих случаев, но предоставляет ли numpy какой-то автоматический способ преобразования своих dtypes в наиболее близкие возможные нативные типы python? Это отображение не должно быть исчерпывающим, но оно должно преобразовывать общие dtypes, которые имеют близкий аналог python. Я думаю, что это уже происходит где-то в NumPy.

Ответы:


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 () внутри блока try.
Роберт Лугг

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

я обнаружил, что смешал набор типов numpy и стандартный python. как все numy-типы происходят из 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
Eswar

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

19

Если вы хотите преобразовать (numpy.array ИЛИ numpy скаляр ИЛИ собственный тип ИЛИ 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 добавит больше dtypes в будущем, ваше решение сломается. Так что я не доволен этим решением.
Конрадле

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

2
Python отображает numpy dtypes на типы python, я не уверен как, но я бы хотел использовать любой метод, который они делают. Я думаю, что это должно произойти, чтобы разрешить, например, умножение (и другие операции) между типами numpy dtypes и python. Я предполагаю, что их метод не исчерпывающе отображает все возможные типы numpy, но, по крайней мере, наиболее распространенные, где это имеет смысл.
Конрадле

Это не работает последовательно: >>> 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]как видите, не все значения были правильно преобразованы.
Алекс Ф

Следуя моему предыдущему комментарию, странно, что этот работает, хотя я хотел бы, чтобы вам нужно было ставить раунд на нативный тип 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()это более общий подход для достижения этой цели. Он работает в любом примитивном dtype, а также в массивах или матрицах.

Я на самом деле не выдает список, если вызывается из примитивных типов:

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


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? Я бросил быстрый взгляд, но не смог его найти.
Конрадле

Это что-то вроде хака: я генерирую a numpy.ndarrayс нулем в нем, используя функцию zeros()вызова ndarrays tolist()функции для преобразования в нативные типы. Однажды в нативных типах я прошу тип вернуть его. tolist()является функциейndarray
Мэтт Олкок

Да, я понимаю, что это работает для того, чего я хочу, и поэтому я принял ваше решение. Но мне интересно, как tolist () решает, к какому типу приписывать, и я не уверен, как найти источник.
Конрадле

numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 - то, где функция задокументирована. Я подумал, что осмотр может помочь найти больше информации, но не радости. Следующим шагом я попытался клонировать github.com/numpy/numpy.git и запустить grep -r 'tolist' numpy. (все еще в процессе, 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

Дополнительное замечание о скалярных массивах для тех, кто не нуждается в автоматическом преобразовании и знает numpy dtype значения:

Скалярные массивы отличаются от скалярных в 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.