创建numpy数组时dtype=object是什么意思?

dba5bblo  于 2023-06-23  发布在  其他
关注(0)|答案(2)|浏览(154)

我在实验numpy数组,并创建了一个numpy字符串数组:

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

正如我从他们的官方指南中读到的,numpy array上的操作被传播到单个元素。所以我这样做了:

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)

在创建数组时,我可以执行所有操作。
有人能告诉我为什么会这样吗?

v8wbuo2f

v8wbuo2f1#

NumPy数组存储为连续的内存块。它们通常具有单一的数据类型(例如整数、浮点数或定长字符串),然后将内存中的位解释为具有该数据类型的值。
使用dtype=object创建数组是不同的。数组占用的内存现在充满了指向Python对象的 * 指针 *,这些对象存储在内存中的 * 其他地方 *(很像Python list实际上只是指向对象的指针列表,而不是对象本身)。
算术运算符(如*)不能处理数据类型为string_的数组(如ar1)(有特殊的函数代替-见下文)。NumPy只是将内存中的位视为字符,*运算符在这里没有意义。然而,该行

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

因为现在数组是一个(指向)Python字符串的数组。*运算符是为这些Python字符串对象定义的。在内存中创建新的Python字符串,并返回一个新的object数组,其中包含对新字符串的引用。
如果你有一个string_unicode_ dtype的数组,并且想要重复每个字符串,你可以使用np.char.multiply

In [52]: np.char.multiply(ar1, 2)
Out[52]: array(['avinashavinash', 'jayjay'], 
      dtype='<U14')

NumPy还有很多其他的vectorised string methods

92vpleto

92vpleto2#

Numpy的str dtype及其操作没有优化,所以在使用Numpy处理字符串时,最好还是坚持使用object dtype。

strobject占用更多的内存

根据固定长度字符串的长度和数组的大小,该比率有所不同,但只要数组中最长的字符串长于2个字符,str就会消耗更多的内存(当数组中最长的字符串长度为2个字符时,它们相等)。例如,在下面的示例中,str消耗的内存几乎是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数组上操作通常更快。例如,在OP中每个字符都重复的例子中,简单的*(又名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

即使对于那些不能很容易地应用于数组的函数,代替str数组的向量化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

相关问题