将numpy数据类型转换为本地Python类型

365
如果我有一个numpy数据类型,如何自动将它转换为最接近的Python数据类型?例如:
numpy.float32 -> "python float"
numpy.float64 -> "python float"
numpy.uint32  -> "python int"
numpy.int16   -> "python int"

我可以尝试列出所有这些情况的映射,但是numpy是否提供将其数据类型自动转换为最接近的本地Python类型的方法? 这个映射不需要详尽无遗,但它应该将具有紧密Python模拟的常见数据类型转换。 我认为这已经在numpy的某个地方发生了。

13个回答

516
使用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 'int'>
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) 在1.16版本中被弃用,并在1.23版本中被移除。
对于好奇的人,要为您的系统构建一个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等效类型,包括:clongdoubleclongfloatcomplex192complex256float128longcomplexlongdoublelongfloat。在使用.item()之前,需要将它们转换为最接近的NumPy等效类型。

我正在使用pandas(0.23.0)。至少对于那个版本,np.str没有.item()方法,所以我看到的唯一方法是将.item()包装在try块中。 - Robert Lugg
4
np.str不是Numpy类型,即np.str is str,因此它只是标准Python类型的别名。np.floatnp.intnp.boolnp.complexnp.object也是如此。Numpy类型有一个下划线后缀,例如np.str_ - Mike T
3
我明白了。问题是“希望”我能够执行:np.float64(0).item()np.float(0).item()。换句话说,在已知该如何处理的情况下,即使它只返回相同的值,也要支持 .item() 方法。这样,我就可以在更多的numpy标量上应用 .item() 而不需要特殊处理。目前看来,似乎并行概念由于底层实现而不同。我完全理解为什么要这样做。但对于库用户来说,这是一种麻烦。 - Robert Lugg
item() 看起来是一个出乎意料的直观名称,用于描述它所做的事情。有没有一种思考方式,让我能够理解它,以便更好地使用它? - Heberto Mayorquin
请注意,目前来看,item() 比 tolist() 慢得多。 - fury

64

我发现自己使用了混合的numpy类型和标准的Python类型。由于所有的numpy类型都派生自numpy.generic,以下是如何将一切转换为Python标准类型的方法:

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

2
asscalar方法自numpy v1.6版本以来已经被弃用。 - Eswar
7
你可以轻松地将答案替换为if isinstance(o, numpy.generic): return o.item() raise TypeError,这样它就变成了一个非废弃的答案 :D - Buggy

41
如果您想将(numpy.array、numpy scalar、native type 或 numpy.darray)转换为原生类型,您可以简单地执行以下操作:
converted_value = getattr(value, "tolist", lambda: value)()

tolist函数将标量或数组转换为Python原生类型。默认的lambda函数将处理值已经是本地类型的情况。

3
最干净的方法来处理混合类型(本地和非本地),做得好!对于那些想知道的人,是的,当你在单个值上调用它时,tolist只返回一个值(标量),而不是像你可能想象的那样返回一个列表。值得注意的是,编写lambda的更简单的方法是 lambda: value,因为我们不需要任何输入。 - fgblomqvist
2
getattr + tolist 的组合不仅是通用的,而且甚至是矢量化的!(与 .item() 不同) - mirekphd
1
这应该是一个被接受的答案,它干净且适用于所有情况。 - Itachi

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

好的建议!.tolist() 对于 np.float32 也适用。 - Davma

14

怎么样:

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
可能的数据类型数量是无限的。对于任何正整数m,请考虑使用np.dtype('mint8')。不能有详尽的映射。(我也不相信有内置函数可以为您执行此转换。我可能错了,但我不这么认为:)) - unutbu
2
Python将numpy数据类型映射到Python类型,我不确定它们是如何实现的,但我想使用他们所使用的方法。我认为这必须发生,以允许例如numpy数据类型和Python类型之间的乘法(和其他操作)。我猜测他们的方法并没有详尽地映射所有可能的numpy数据类型,但至少映射了最常见的一些,在有意义的情况下。 - 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] 如您所见,并非所有值都被正确转换。 - Alex F
根据我之前的评论,奇怪的是这个代码可以工作,尽管我认为你需要将 round 应用于 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
@AlexF 这是一致的,你只是碰巧遇到了浮点二进制算术问题。其中一些十进制数,如0.65,在二进制中是循环小数,因此无法准确存储。在以十进制显示时,看起来像是一个四舍五入误差。https://softwareengineering.stackexchange.com/a/101170/290646 - Davos

11

很抱歉我迟到了,但我一直在解决将 numpy.float64 转换为普通的 Python float 的问题。我看到有三种方法可以做到:

  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)更快。


10
您还可以调用要转换的对象的item()方法:
>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>

8
我认为您可以编写通用类型转换函数,例如:
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
这有点像是一个hack,我的做法是使用zeros()生成一个只有1个零的numpy.ndarray,然后调用ndarraystolist()函数将其转换为本地类型。一旦转换为本地类型,我会询问类型并返回它。tolist()ndarray的一个函数。 - Matt Alcock
是的,我看到了——它对我想要的东西起作用,所以我接受了你的解决方案。但我想知道 tolist() 如何决定要转换成什么类型,并且我不确定如何找到源代码。 - conradlee
http://numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 是文档化这个函数的地方。我认为检查工具可能能够帮助找到更多信息,但是没有结果。接下来,我尝试克隆 https://github.com/numpy/numpy.git 并运行 grep -r 'tolist' numpy。(仍在进行中,因为numpy非常庞大!) - Matt Alcock

5
numpy将该信息保存在一个映射中,该映射作为typeDict暴露出来,因此您可以执行以下操作:
>>> import __builtin__ as builtins  # if python2
>>> import builtins                 # if python3

然后:
>>> import numpy as np
>>> {v: k for k, v in np.typeDict.items() if k in dir(builtins)}
{numpy.object_: 'object',
 numpy.bool_: 'bool',
 numpy.string_: 'str',
 numpy.unicode_: 'unicode',
 numpy.int64: 'int',
 numpy.float64: 'float',
 numpy.complex128: 'complex'}

如果您想要实际的Python类型而不是它们的名称,可以执行以下操作:
>>> {v: getattr(builtins, k) for k, v in np.typeDict.items() if k in vars(builtins)}
{numpy.object_: object,
 numpy.bool_: bool,
 numpy.string_: str,
 numpy.unicode_: unicode,
 numpy.int64: int,
 numpy.float64: float,
 numpy.complex128: complex}

1
如果您有一个由numpy类型组成的数组列表list_numpy_numbers,请执行以下操作:
list_native_numbers = [i.item() for i in list_numpy_numbers]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接