繁体   English   中英

在字符串之间转换代码点的numpy数组

[英]Converting numpy arrays of code points to and from strings

我有一个长的unicode字符串:

alphabet = range(0x0FFF)
mystr = ''.join(chr(random.choice(alphabet)) for _ in range(100))
mystr = re.sub('\W', '', mystr)

我想将其视为一系列代码点,因此目前,我正在执行以下操作:

arr = np.array(list(mystr), dtype='U1')

我希望能够将字符串作为数字来处理,并最终获得一些不同的代码点。 现在,我想反转转换:

mystr = ''.join(arr.tolist())

这些转换相当快且可逆,但是在list中介中占用了不必要的空间。

有没有一种方法可以将numpy的Unicode字符数组与Python字符串进行相互转换而无需先转换为列表?

后记

我可以让arr以单个字符串的形式出现,例如

buf = arr.view(dtype='U' + str(arr.size))

这将导致包含整个原始图元的1元素数组。 逆也是可能的:

buf.view(dtype='U1')

唯一的问题是结果的类型是np.str_ ,而不是str

我发现将字符串转换为数组的最快方法是

arr = np.array([mystr]).view(dtype='U1')

根据@Daniel Mesejo的评论 ,将字符串转换为Unicode代码点数组的另一种(较慢的)方法:

arr = np.fromiter(mystr, dtype='U1', count=len(mystr))

查看fromiter的源代码表明,将count参数设置为字符串的长度将导致立即分配整个数组,而不是执行多个重新分配。

转换回字符串:

str(arr.view(dtype=f'U{arr.size}')[0])

在大多数情况下,最终转化到Python str ,因为没有必要np.str_是的子类str

arr.view(dtype=f'U{arr.size}')[0]

附录: frombuffer vs array

100

mystr = ''.join(chr(random.choice(range(1, 0x1000))) for _ in range(100))

%timeit np.array([mystr]).view(dtype='U1')
1.43 µs ± 27.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.frombuffer(bytearray(mystr, 'utf-32-le'), dtype='U1')
1.2 µs ± 9.06 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

10000

mystr = ''.join(chr(random.choice(range(1, 0x1000))) for _ in range(10000))

%timeit np.array([mystr]).view(dtype='U1')
4.33 µs ± 13.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit np.frombuffer(bytearray(mystr, 'utf-32-le'), dtype='U1')
10.9 µs ± 29.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

百万

mystr = ''.join(chr(random.choice(range(1, 0x1000))) for _ in range(1000000))

%timeit np.array([mystr]).view(dtype='U1')
672 µs ± 1.64 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit np.frombuffer(bytearray(mystr, 'utf-32-le'), dtype='U1')
732 µs ± 5.22 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

fromiter可以工作,但是速度很慢,因为它要通过迭代器协议。 将您的数据编码为UTF-32(以系统字节顺序)并使用numpy.frombuffer

In [56]: x = ''.join(chr(random.randrange(0x0fff)) for i in range(1000))

In [57]: codec = 'utf-32-le' if sys.byteorder == 'little' else 'utf-32-be'

In [58]: %timeit numpy.frombuffer(bytearray(x, codec), dtype='U1')
2.79 µs ± 47 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [59]: %timeit numpy.fromiter(x, dtype='U1', count=len(x))
122 µs ± 3.82 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [60]: numpy.array_equal(numpy.fromiter(x, dtype='U1', count=len(x)), numpy.fr
    ...: ombuffer(bytearray(x, codec), dtype='U1'))
Out[60]: True

我已经使用sys.byteorder确定是使用utf-32-le还是utf-32-be进行编码。 此外,使用bytearray而不是encode得到一个可变的bytearray代替不可变字节的对象,所以所得到的阵列是可写的。


至于反向转换, arr.view(dtype=f'U{arr.size}')[0]可以工作,但是使用item()会更快一些,并且会生成一个普通的字符串对象,避免了numpy.str_可能出现的奇怪边缘情况numpy.str_行为并不像str

In [72]: a = numpy.frombuffer(bytearray(x, codec), dtype='U1')

In [73]: type(a.view(dtype=f'U{a.size}')[0])
Out[73]: numpy.str_

In [74]: type(a.view(dtype=f'U{a.size}').item())
Out[74]: str

In [75]: %timeit a.view(dtype=f'U{a.size}')[0]
3.63 µs ± 34 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [76]: %timeit a.view(dtype=f'U{a.size}').item()
2.14 µs ± 23.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

最后,请注意,NumPy不像常规Python字符串对象那样处理null。 NumPy不能区分'asdf\\x00\\x00\\x00''asdf' ,因此,如果您的数据可能包含空代码点,则将NumPy数组用于字符串操作并不安全。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM