首页 > 解决方案 > 将 NumPy np.uint8 数组列表转换为 np.unicode_ 数组

问题描述

我有一个 NumPy 可变大小数组的列表dtype=np.uint8(这些代表 UTF-8 编码的字符串)。如何有效快速地将此列表转换为单个dtype=np.unicode_数组?

l = [np.frombuffer(b'asd', dtype = np.uint8), np.frombuffer(b'asdasdas', dtype = np.uint8)]

# The following will work, but will first create a temporary string which is inefficient. 
# I'm looking for a method that would directly allocate a target np.unicode_-typed array 
# and encode the data into it.
a = np.array([s.tostring().decode('utf-8') for s in l])

这些数组不仅是 ASCII 编码的,它们还包含其他字符:

s = b'8 \xd0\x93\xd0\xbe\xd1\x80\xd0\xbe\xd0\xb4 \xd0\x91\xd0\xb0\xd0\xb9\xd0\xba\xd0\xbe\xd0\xbd\xd1\x83\xd1\x80 (\xd0\xa0\xd0\xb5\xd1\x81\xd0\xbf\xd1\x83\xd0\xb1\xd0\xbb ...: \xd0\xb8\xd0\xba\xd0\xb0 \xd0\x9a\xd0\xb0\xd0\xb7\xd0\xb0\xd1\x85\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd)' 

s.decode('utf-8') # works

标签: pythonstringnumpyunicode

解决方案


更新

事实证明,Python utf-8 编解码器可用于直接解码 ndarray,而无需先将其内容复制到字节字符串.tostring():使用编解码器模块,可以检索将 utf-8 字节序列转换为 unicode 的可调用对象无需经过的字符串str.decode

lst = [np.frombuffer(b'asd', dtype = np.uint8), np.frombuffer(b'asdasdas', dtype = np.uint8)]

import codecs

decoder = codes.getdecoder("utf-8")    
data = np.array([decoder(item)[0] for item in lst], dtype="unicode")

这避免了转换的一个步骤 - 可以避免另一个步骤,因为这仍然会在调用最后一个.array构造函数之前创建内存中所有字符串的列表 - numpy 有一个.fromiter数组构造函数 - 但它不能创建一个数组任意 unicode 对象 - 它需要一个固定的字符宽度。这最终会消耗比目前更多的内存:

data = np.fromiter((decoder(item) for item in lst), count=len(lst), dtype="U120")  # For max-length of 120 characters.

原始答案(主要是罗马漫谈)

现代 Python 内部处理 Unicode 文本非常有效,内部 unicode 点表示取决于字符串中最宽的字符。

另一方面,Numpy 只为每个 unicode 字符存储一个 32 位的值——它没有业务“理解”utf-8。Python 语言在这方面做得很好——而且速度很快。尽管 Python 在将 utf-8 字节解码为文本时不会使用任何多线程、多核或硬件加速策略,但解码是在本机代码中进行的,并且与您在单个 CPU 内核中获得的速度一样快。

在我的系统中,使用纯 Python 将 4MB 大小的文本解码为 un​​icode 只需不到 30 毫秒。

换句话说:你开始担心错误的问题——除非你正在编码的任何东西都需要以持续的方式每秒转换大约 100 个圣经大小的文本语料库。

只需让 Python 进行 utf-8 解码,并将结果处理回 numpy(它将以 32 位格式再次对其进行编码) - 对于绝大多数现实世界的任务来说,这方面的花费可以忽略不计,这是例如,Pandas 库对数据执行几乎所有操作的方式:在每次操作后创建数据的新副本。


推荐阅读