在创建一个numpy数组时,dtype=object是什么意思?

36

我在尝试使用numpy数组,并创建了一个字符串类型的numpy数组:

ar1 = np.array(['avinash', 'jay'])

根据其官方指南所述,对numpy数组的操作会传播到各个元素。因此我这样做:

ar1 * 2

但是我遇到了这个错误:

TypeError                                 Traceback (most recent call last)
<ipython-input-22-aaac6331c572> in <module>()
----> 1 ar1 * 2

TypeError: unsupported operand type(s) for *: 'numpy.ndarray' and 'int'

但当我使用 dtype=object

ar1 = np.array(['avinash', 'jay'], dtype=object)

在创建数组时,我能够执行所有操作。

有人可以告诉我为什么会发生这种情况吗?

2个回答

55
NumPy数组以连续的内存块存储。它们通常具有单一的数据类型(例如整数、浮点数或固定长度字符串),然后内存中的位被解释为具有该数据类型的值。
使用dtype=object创建数组是不同的。现在数组占用的内存被填充了指向Python对象的指针,这些对象被存储在内存的其他位置(就像Python的list实际上只是一个指向对象的指针列表,而不是对象本身)。
对于具有string_数据类型的数组(如ar1),算术运算符(如*)不起作用(这里有特殊的函数代替-请参见下文)。NumPy只是将内存中的位视为字符,并且在这里*运算符没有意义。然而,下面这行代码
np.array(['avinash','jay'], dtype=object) * 2

这个工作是因为现在数组是一个(指向)Python字符串的数组。对于这些Python字符串对象,*运算符是定义良好的。新的Python字符串被创建在内存中,并返回一个新的object数组,其中包含对新字符串的引用。
如果您有一个包含string_unicode_数据类型的数组,并且想要重复每个字符串,您可以使用np.char.multiply函数:
In [52]: np.char.multiply(ar1, 2)
Out[52]: array(['avinashavinash', 'jayjay'], 
      dtype='<U14')

NumPy还有许多其他向量化的字符串方法。

12
在对一个对象数组执行操作时,与尝试迭代Python列表相比,是否存在任何效率提升? - user2886057

1
Numpy的`str`数据类型及其操作并未经过优化,因此在使用numpy处理字符串时,最好还是坚持使用`object`数据类型。

`str`比`object`占用更多内存

根据固定长度字符串的长度和数组的大小,比例会有所不同,但只要数组中最长的字符串长度超过2个字符,`str`就会占用更多内存(当数组中最长的字符串长度为2个字符时,它们相等)。例如,在下面的示例中,`str`占用的内存几乎是`object`的8倍。
from pympler.asizeof import asizesof

ar1 = np.array(['this is a string', 'string']*1000, dtype=object)
ar2 = np.array(['this is a string', 'string']*1000, dtype=str)
asizeof(ar2) / asizeof(ar1)  # 7.944444444444445

strobject

Numpy的向量化字符串方法没有进行优化,因此在操作object数组时通常更快。例如,在原始问题中,每个字符重复的示例中,简单的*(也称为multiply())不仅更简洁,而且比char.multiply()快10倍以上。

import timeit
setup = "import numpy as np; from __main__ import ar1, ar2"
t1 = min(timeit.repeat("ar1*2", setup, number=1000))
t2 = min(timeit.repeat("np.char.multiply(ar2, 2)", setup, number=1000))
t2 / t1   # 10.650433758517027

即使对于不能直接应用于数组的函数,与向量化的`char`方法相比,循环遍历`object`数组并处理Python字符串的速度更快。
例如,迭代`object`数组并在每个Python字符串上调用`str.count()`比在`str`数组上使用向量化的`char.count()`快3倍以上。
f1 = lambda: np.array([s.count('i') for s in ar1])
f2 = lambda: np.char.count(ar2, 'i')

setup = "import numpy as np; from __main__ import ar1, ar2, f1, f2, f3"
t3 = min(timeit.repeat("f1()", setup, number=1000))
t4 = min(timeit.repeat("f2()", setup, number=1000))

t4 / t3   # 3.251369161574832

顺便说一下,如果涉及到显式循环,遍历列表比遍历numpy数组要快。因此,在前面的例子中,通过遍历列表可以进一步提高性能。
f3 = lambda: np.array([s.count('i') for s in ar1.tolist()])
#                                               ^^^^^^^^^  <--- convert to list here
t5 = min(timeit.repeat("f3()", setup, number=1000))
t3 / t5   # 1.2623498005294627

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