根据输入偏移值拆分数组,但在同一块中保留重复

Posted

技术标签:

【中文标题】根据输入偏移值拆分数组,但在同一块中保留重复【英文标题】:Splitting an array according input offset values, but leaving duplicated in a same chunk 【发布时间】:2022-01-21 02:24:57 【问题描述】:

给定一个索引列表(偏移值),根据该列表拆分一个 numpy 数组,我想对其进行调整,以便在重复值上不会发生拆分。 这意味着重复值将仅在一个块中。

我已经编写了以下代码,它给出了结果,但我并不为此感到非常自豪。我想留在 numpy 世界中,并尽可能多地使用向量化的 numpy 函数。 但要检查索引(偏移值),我使用for 循环,并将结果存储在列表中。

你知道如何矢量化第二部分吗?

如果这有帮助,ar 是一个有序数组。 (我没有在下面的代码中使用此信息)。

import numpy as np
import vaex as vx

ar = np.array([8,8,8,10,11,11,13,14,15,15,18,19,20,21,22,22,22,22,22,22])
offsets = np.array([0,2,4,9,11,13,15,len(ar)])

_, unique_ind = np.unique(ar, return_index=True, return_inverse=False)
dup_ind = np.diff(unique_ind, append=len(ar))
dup_start = unique_ind[dup_ind > 1]
dup_end = dup_start + dup_ind[dup_ind > 1]
print(f'initial offsets: offsets')
#print(f'dup start: dup_start')
#print(f'dup end: dup_end')

temp = []
for off in offsets:
    for ind in range(len(dup_start)):
        if off > dup_start[ind] and off < dup_end[ind]:
            off = dup_start[ind]
            break
    temp.append(off)

# Remove duplicates
offsets = list(dict.fromkeys(temp))
print(f'modified offsets: offsets')

结果

initial offsets: [ 0  2  4  9 11 13 15 20]
modified offsets: [0, 4, 8, 11, 13, 14, 20]

【问题讨论】:

【参考方案1】:

根据@richardec 的回答,这是一个可能的解决方案,以防它可以帮助其他人。

import numpy as np

def adjust_offsets(values, offsets) -> np.ndarray:
    # Creates bins describing when duplicates start and when they end.
    _, indexes, counts = np.unique(values, return_index=True, return_counts=True)
    duplicates = counts>1
    dup_starts = indexes[duplicates]
    dup_ends = dup_starts + counts[duplicates]
    # Zipping starts and ends.
    # Bins of duplicates are interlayed with bins of unique values.
    bins = np.dstack((dup_starts,dup_ends)).flatten()
    # Set start of bin for offsets falling in a bin.
    off_binned = np.digitize(offsets, bins, right=True)
    return np.unique(np.where((off_binned%2)==0,offsets,bins[off_binned-1]))

# Test setup 1 / starting with no duplicates
# idx:         0,1,2,3,4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21
ar = np.array([4,5,8,8,8,10,11,11,13,14,15,15,18,19,20,21,22,22,22,22,22,22])
offsets = np.array([0,2,4,9,11,14,18,len(ar)-1])
off_adjusted = adjust_offsets(ar, offsets)
ref = np.array([ 0,  2,  9, 10, 14, 16])
assert np.array_equal(off_adjusted, ref)

# Test setup 2 / starting with duplicates
# idx:         0,1,2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19
ar = np.array([8,8,8,10,11,11,13,14,15,15,18,19,20,21,22,22,22,22,22,22])
offsets = np.array([0,2,4,9,11,13,15,len(ar)-1])
off_adjusted = adjust_offsets(ar, offsets)
ref = np.array([ 0,  4,  8, 11, 13, 14])
assert np.array_equal(off_adjusted, ref)

我使用了 2 个测试,因为如果数组不是以重复开头,我想出的以前的版本将无法工作。

2句话,算法遵循以下逻辑:

如果偏移值(索引)落在由 [start of duplicate, end of duplicate] 定义的 bin 中,则将其重置为“start of duplicate”。 如果不是,则未修改

可能更快,正如我从这个相关的SO answer 中看到的那样,np.diffnp.unique 更快,我认为可以使用np.diff 获得相同类型的信息。我开始评估这种可能性,但它对我来说太复杂了。

【讨论】:

【参考方案2】:

您可以使用np.digitize 将偏移量限制在 bin 中:

ar = np.array([8,8,8,10,11,11,13,14,15,15,18,22,22,22,22,22,22])
offsets = np.array([0,2,4,9,13,15,len(ar)])

_, unique_ind = np.unique(ar, return_index=True, return_inverse=False)
dup_ind = np.diff(unique_ind, append=len(ar))
dup = np.append(unique_ind[dup_ind > 1], len(ar))

offsets = dup[np.digitize(offsets, dup) - 1]

输出:

>>> offsets
array([ 0,  0,  4,  8, 11, 11, 17])

>>> np.unique(offsets)
array([ 0,  4,  8, 11, 17])

它很可能被压缩成更少的代码(可能是 1 或 2 行),但我认为你真的只是希望消除循环,所以我想提交我发现的内容。

【讨论】:

谢谢!不,这段代码不太对,而且我的例子也不完整。当偏移量不落在重复值上时,我已经完成了它以包含“正常”情况。在这种情况下,我们只是保留它。我尝试使用您的代码,但没有保留值。

以上是关于根据输入偏移值拆分数组,但在同一块中保留重复的主要内容,如果未能解决你的问题,请参考以下文章

Java - 正则表达式拆分输入文本但保留分隔符[重复]

SQL Query 根据 ID 将多行合并为一行,同时将其他值保留在同一行中?

如何拆分字符串但在java中保留分隔符? [复制]

保留一个值或根据关联进行拆分以声明 no 和 cpt 代码

如何对数组进行排序但保留C中重复元素的位置?

Laravel(Livewire)中的组件拆分和保留参数