从numpy对象解码非ascii字符

4c8rllxm  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(84)
import numpy as np

sentence = ['世界']

a = np.array(sentence, dtype=object).reshape(-1)

b = [sentence for sentence in np.char.decode(a.astype(np.bytes_), 'UTF-8')]

字符串
我试图将numpy.object转换回字符串,但它不适用于非ascii字符。错误为

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

lp0sw83n

lp0sw83n1#

我想你误解了np.object是什么。它只是意味着“其他python东西”(与其他数据类型相反,这些数据类型在numpy中转换,没有内部python Package :只有数组被 Package 在python对象中,而不是它的所有内容。所以,一个1000个short int的数组,在内存中只使用2000个字节。并且可以通过numpy的内部C代码高效迭代,而不必费心python内部表示数据)
看出区别了吗

T=np.array([1,2,3], dtype=np.uint32)
Tb=T.tobytes()
Tb
# b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
len(Tb)
# 12
np.frombuffer(Tb, dtype=np.uint32)
# [1,2,3]

字符串
当然,最后一行可能看起来很愚蠢。但它证明了numpy的记忆里储存的东西:3 int 32的值。通过观看Tb也可以看到:我们看到12个字节,1,0,0,0(所以1,在32位上,小端-至少在我的机器上,可能也在你的机器上),2,0,0,0(所以2),3,0,0,0。
您也可以使用struct进行解码

import struct
struct.unpack('iii', Tb)


所以,这里没有什么令人惊讶的:当我们在3 int 32上创建一个数组时,它包含12个字节,这12个字节表示这些int 32的值。
现在,把那个和这个比较一下

U=np.array([1,2,3], dtype=object)
Ub=U.tobytes()
Ub 
# b' n\x95\x00\x00\x00\x00\x00@n\x95\x00\x00\x00\x00\x00`n\x95\x00\x00\x00\x00\x00'
len(Ub)
U.dtype.itemsize # or U.strides[-1]
# 8


我们看到每个数字用8个字节编码,因此总共24个字节。但是看Ub,这似乎不仅仅是1 2 3的int 64编码。让我们检查一下

struct.unpack('lll', Ub)
# or
np.frombuffer(Ub, dtype=np.uint64)
# (9793056, 9793088, 9793120)


这与(1,2,3)无关。或者

id(1)
# 9793056
id(2)
# 9793088
id(3)
# 9793120


对于您自己的数组也是如此

struct.unpack('l', a.tobytes())
# (140450860495472,)
id(sentence[0])
# 140450860495472


所以,这就是从numpy的Angular 来看object的意思:存储python对象ID。一个指向那些python对象的指针,如果你愿意的话。这意味着这对numpy来说是不透明的。它无法理解object数组中的内容。它不能对它执行任何操作。它不被编码,也不需要以任何方式解码。它只是对象的id,只有python(不是numpy)可以理解。numpy唯一能做的就是把它还给你,就像你给它的那样。
当然,它的优点是它可以存储任何东西。缺点是你基本上什么也做不了。
但是,好吧,现在我弄清楚了什么是那种类型,没有解码要做。你的字符串(一个python字符串,它是一个指向包含该字符串的python对象的“指针”)被存储起来,这对于numpy来说是不可理解的,但它是这样的。当您取回它时(例如使用简单的a[0]),它仍然是python的相同字符串。

a[0]
# '世界'

Unicode和utf8/decode

因为你标记了问题unicodedecode,...这可能值得一看的utf8编码,和unicode在这里。
我也可以不使用基于int的示例,而是与<U2类型进行比较,但这会增加复杂性。

V=np.array(sentence)
Vb=V.tobytes()
V.dtype.itemsize
# 4
np.frombuffer(Vb, dtype=np.uint32)
# array([19990, 30028], dtype=uint32)


不出所料,19990是ref here的unicode
那么19990和30028是什么呢

utf8 = np.frombuffer(sentence[0].encode(), dtype=np.uint8)
utf8
# array([228, 184, 150, 231, 149, 140], dtype=uint8)
[bin(x) for x in utf8]
# ['0b11100100', '0b10111000', '0b10010110', '0b11100111', '0b10010101', '0b10001100']


基于utf8标准,我们看到第一个字节以3 111开头,后面是0。这是一个3字节的代码。接下来的2个字节确实以10开始,继续3个字节的代码。总的来说,16位是第1字节的最后4位,以及随后2个字节的最后6位。所以0100111000010110
哪个是号码

int('0100111000010110', 2)
# 19990


同样,接下来的3个字节当然是编号为30028的utf8编码。
因此,sentence[0].encode()是字符串的utf-8编码,即unicode值的utf-8编码。当使用<U dtype时,numpy数组中存储的是unicode值(没有utf8编码,即具有固定的32位大小)
因此,换句话说,当使用<U2 dtype时,字符串被编码(以uint 32数字表示,表示字符串字符的unicode代码)。Numpy确实理解它的含义,并且可以采取行动。不多,因为numpy不太适合字符串处理。正如它的名字num所暗示的那样,numpy的角色更多的是关于数字,但是,字符串数据被编码在数组中,而不仅仅是指向opaque python引用的指针,该python引用碰巧是一个字符串,但是只有python知道。这就是object的情况
长话短说,当你在object中编码东西(数字,字符串,无论什么)时,你不能对它们使用任何numpy函数。但你不需要做任何事情来解码它们。它们只是对python对象的原始引用。当你使用另一个dtype时,事情会以一种对numpy有意义的方式编码,numpy可以在这种方式上工作。但是需要从python的内部表示和numpy的内部表示来回转换(通常由numpy的构造函数和getter处理)。

相关问题