Numpy:索引不同长度的子串

问题描述 投票:0回答:1

我有一个很大的“S1”类型的数组,它是通过连接许多名字、姓氏对获得的。例如:

>>> names
array([b'F', b'r', b'e', b'd', b'B', b'l', b'o', b'g', b's', b'A', b'n',
       b'n', b'S', b'm', b'i', b't', b'h', b'J', b'i', b'm', b'J', b'o',
       b'n', b'e', b's'], dtype='|S1')

虽然大得多。你可以更容易地看到名字:

>>> names.tobytes().decode()
'FredBlogsAnnSmithJimJones'

我有一个单独的整数数组,告诉我每个名字的位置:

>>> idx
array([[ 4,  9],
       [12, 17],
       [20, 25]])

所以我们可以找到第n个人的名字和姓氏:

>>> idx[1]
array([12, 17])
>>> idx[0,1]
9
>>> 

所以这让我可以恢复第 n 个姓氏对:

>>> names[9:12].tobytes().decode(), names[12:17].tobytes().decode()
('Ann', 'Smith')

问题是:给定一组人的索引以任意顺序找到对应的名字。所以从例如从

[2,0]
(作为一个numpy数组)恢复
[["Jim", "Jones"], ["Fred", Blogs"]]
。例如:

>>> get_names(names, np.array[2,0])
array([['Jim', 'Jones'],
       ['Fred', 'Blogs']], dtype='<U5')

虽然这里的 dtype 可以是带有 python 字符串的对象。

我可以通过在 python 中生成索引来做到这一点,但实际上我可能有一长串索引并且想知道是否有一种方法可以在 numpy 中生成索引以提高性能。

将原始数据作为一个大的连续数组(使用共享内存共享数据)是有原因的,因此涉及将源数据转换为包含对象 dtype 的任何内容的解决方案将不起作用。

虽然我已经针对我的特定用例提出了问题,但如何在名称数组中获取适当索引的核心问题并不真正取决于名称的数据类型。

numpy numpy-ndarray
1个回答
1
投票

您可以将

S1
的数组转换为bytestring:

In [35]: astr = b''.join(arr)
In [36]: astr
Out[36]: b'FredBlogsAnnSmithJimJones'

如果你想解码也没关系。

字符串可以像数组一样被切片:

In [37]: (astr[9:12],astr[12:17])
Out[37]: (b'Ann', b'Smith')

但是数组可以用数组索引,其中字符串(和列表不允许):

例如,一个 numpy 辅助函数可以将两个切片转换成一个数组:

In [38]: np.r_[9:12,12:17]
Out[38]: array([ 9, 10, 11, 12, 13, 14, 15, 16])
In [39]: arr[_]
Out[39]: array([b'A', b'n', b'n', b'S', b'm', b'i', b't', b'h'], dtype='|S1')

所以选择了相关的字符,但松散了名字和姓氏之间的分割。

所以即使使用数组,我也无法“快速”选择一组“随机”的名字/姓氏。

我认为最好的办法是提前将整个数组拆分为名称,然后再进行“随机”选择。从平面 runon 数组(或字符串)中即时选择不会很快。

例如使用

array_split
将数组分成数组列表,每个名称一个。这实际上对扁平
idx
中的每对数字进行切片。

In [45]: np.array_split(arr,idx.ravel())
Out[45]:
[array([b'F', b'r', b'e', b'd'], dtype='|S1'),
 array([b'B', b'l', b'o', b'g', b's'], dtype='|S1'),
 array([b'A', b'n', b'n'], dtype='|S1'),
 array([b'S', b'm', b'i', b't', b'h'], dtype='|S1'),
 array([b'J', b'i', b'm'], dtype='|S1'),
 array([b'J', b'o', b'n', b'e', b's'], dtype='|S1'),
 array([], dtype='|S1')]
In [46]: alist = np.array_split(arr,idx.ravel())

然后将其转换为字符串列表:

In [47]: blist = [b''.join(x) for x in alist]
In [48]: blist
Out[48]: [b'Fred', b'Blogs', b'Ann', b'Smith', b'Jim', b'Jones', b'']

可以从列表中索引对,但为了方便起见,让我们将其转换为 2 列数组:

In [49]: barr = np.array(blist[:-1]).reshape(-1,2)
In [50]: barr
Out[50]:
array([[b'Fred', b'Blogs'],
       [b'Ann', b'Smith'],
       [b'Jim', b'Jones']], dtype='|S5')
In [51]: barr[[0,2]]
Out[51]:
array([[b'Fred', b'Blogs'],
       [b'Jim', b'Jones']], dtype='|S5')

array_split
有效地做:

In [54]: [b''.join(arr[i:j]) for i,j in [[0,4],[4,9],[9,12],[12,17],[17,20],[20,25]]]
Out[54]: [b'Fred', b'Blogs', b'Ann', b'Smith', b'Jim', b'Jones']

我使用了列表的显式列表,因为我不想花时间从

idx
中推导出等价物。

编辑

比较两个数组的大小:

In [59]: arr.nbytes, barr.nbytes
Out[59]: (25, 30)

In [60]: arr.tobytes()
Out[60]: b'FredBlogsAnnSmithJimJones'
In [61]: barr.tobytes()
Out[61]: b'Fred\x00BlogsAnn\x00\x00SmithJim\x00\x00Jones'
© www.soinside.com 2019 - 2024. All rights reserved.