以矢量化方式连接给定开始,停止数字的范围数组 - NumPy

Posted

技术标签:

【中文标题】以矢量化方式连接给定开始,停止数字的范围数组 - NumPy【英文标题】:Concatenate range arrays given start, stop numbers in a vectorized way - NumPy 【发布时间】:2018-04-17 22:51:03 【问题描述】:

我有两个感兴趣的矩阵,第一个是“词袋”矩阵,有两列:文档 ID 和术语 ID。例如:

bow[0:10]

Out[1]:
    array([[ 0, 10],
           [ 0, 12],
           [ 0, 19],
           [ 0, 20],
           [ 1,  9],
           [ 1, 24],
           [ 2, 33],
           [ 2, 34],
           [ 2, 35],
           [ 3, 2]])

此外,我有一个“索引”矩阵,其中矩阵中的每一行都包含词袋矩阵中给定文档 ID 的第一行和最后一行的索引。例如:第 0 行是 doc id 0 的第一个和最后一个索引。例如:

index[0:4]

Out[2]:
    array([[ 0,  4],
           [ 4,  6],
           [ 6,  9],
           [ 9, 10]])

我想做的是随机抽取文档 ID 的样本,并获取这些文档 ID 的所有单词行包。词袋矩阵大约有 150M 行(~1.5Gb),所以使用 numpy.in1d() 太慢了。我们需要快速返回这些以供下游任务使用。

我想出的幼稚解决方案如下:

def get_rows(ids):
    indices = np.concatenate([np.arange(x1, x2) for x1,x2 in index[ids]])
    return bow[indices]

get_rows([4,10,3,5])

通用示例

提出问题的通用示例是这样的 -

indices = np.array([[ 4, 7],
                    [10,16],
                    [11,18]]

预期的输出将是 -

array([ 4,  5,  6, 10, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15, 16, 17])

【问题讨论】:

考虑到您尝试生成的输出是锯齿状的,因此不会有一个好的矢量化解决方案。 由于端点与下一组的开始相邻,因此连接的 o/p 将只是 range(a[0,0],a[-1,-1]),对吧? 在不经过锯齿状中间数组的情况下生成 concatenate 输出可能是矢量化的。 ...嘿,是的,范围都是这样连续的吗? Divakar 提出了一个很好的观点,如果他们是的话。 @Divark - 不一定,它可能是一个随机的索引列表,例如:arr[[5,100, 31, 123]]。不过你是对的,它们是锯齿状的。我目前的方法: sparse_rows = np.concatenate([np.arange(x1, x2) for x1,x2 in arr[idxs]]) 非常慢 【参考方案1】:

我想我终于用cumsum 技巧破解了它,用于矢量化解决方案 -

def create_ranges(a):
    l = a[:,1] - a[:,0]
    clens = l.cumsum()
    ids = np.ones(clens[-1],dtype=int)
    ids[0] = a[0,0]
    ids[clens[:-1]] = a[1:,0] - a[:-1,1]+1
    out = ids.cumsum()
    return out

示例运行 -

In [416]: a = np.array([[4,7],[10,16],[11,18]])

In [417]: create_ranges(a)
Out[417]: array([ 4,  5,  6, 10, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15, 16, 17])

In [425]: a = np.array([[-2,4],[-5,2],[11,12]])

In [426]: create_ranges(a)
Out[426]: array([-2, -1,  0,  1,  2,  3, -5, -4, -3, -2, -1,  0,  1, 11])

如果我们以两个1D 数组的形式给出开始和停止,我们只需要使用它们来代替第一列和第二列。为了完整起见,这是完整的代码-

def create_ranges(starts, ends):
    l = ends - starts
    clens = l.cumsum()
    ids = np.ones(clens[-1],dtype=int)
    ids[0] = starts[0]
    ids[clens[:-1]] = starts[1:] - ends[:-1]+1
    out = ids.cumsum()
    return out

【讨论】:

@yatu 是的,问题基本相同,因此关闭为 dup。也可以鼓励正在寻找被链接的性能的人们访问它。最好对相关问题进行一次问答。您可能还想在此处添加该答案帖子。 我见过人们这样做的通常的 dup 关闭是通过我在 SO 上的几年经验来保留最古老的。因此,遵循相同的。希望它是善意的:)

以上是关于以矢量化方式连接给定开始,停止数字的范围数组 - NumPy的主要内容,如果未能解决你的问题,请参考以下文章

如何以递减方式拉伸特定的numpy数组项?

2022-06-04:给定一个数字n,表示一开始有编号1~n的树木,列成一条直线, 给定一个有序数组arr,表示现在哪些树已经没了,arr[i]一定在[1,n]范围, 给定一个数字m,表示你可以补种多

显示范围内给定数字的倍数

显示范围内给定数字的倍数

找出能被两个给定参数和它们之间的连续数字整除的最小公倍数。 范围是两个数字构成的数组,两个数字不一定按数字顺序排序。

如何在给定范围内创建一个随机打乱数字的 int 数组 [重复]