Python 获取分割NumPy数组的索引

Python 获取分割NumPy数组的索引,python,numpy,Python,Numpy,假设我有一个NumPy数组: x = np.array([3, 9, 2, 1, 5, 4, 7, 7, 8, 6]) 如果我对这个数组求和,得到52。我需要的是一种从左到右将其划分为大致n块的方法,其中n由用户选择。本质上,分裂是以贪婪的方式发生的。因此,对于某些数量的块n,第一个n-1块的总和必须至少达到52/n,并且它们必须是从左到右的连续索引 因此,如果n=2,那么第一个块将由前7个元素组成: chunk[0] = x[:7] # [3, 9, 2, 1, 5, 4, 7], sum

假设我有一个NumPy数组:

x = np.array([3, 9, 2, 1, 5, 4, 7, 7, 8, 6])
如果我对这个数组求和,得到
52
。我需要的是一种从左到右将其划分为大致
n
块的方法,其中
n
由用户选择。本质上,分裂是以贪婪的方式发生的。因此,对于某些数量的块
n
,第一个
n-1
块的总和必须至少达到
52/n
,并且它们必须是从左到右的连续索引

因此,如果
n=2
,那么第一个块将由前7个元素组成:

chunk[0] = x[:7]  # [3, 9, 2, 1, 5, 4, 7], sum = 31
chunk[1] = x[7:]  # [7, 8, 6], sum = 21
请注意,第一个区块不会只包含前6个元素,因为总和将是
24
,小于
52/2=26
。另外,请注意,只要满足求和条件,每个块中的元素数量就可以改变。最后,最后一个块不能接近
52/2=26
,因为其他块可能需要更多的时间

但是,我需要的输出是一个两列数组,第一列中包含开始索引,第二列中包含(独占)停止索引:

[[0, 7],
 [7, 10]]
如果
n=4
,则前3个区块的总和至少应为
52/4=13
,如下所示:

chunk[0] = x[:3]  # [3, 9, 2], sum = 14
chunk[1] = x[3:7]  # [1, 5, 4], sum = 17
chunk[2] = x[7:9]  # [7, 8], sum = 15
chunk[3] = x[9:]  # [6], sum = 6
我需要的输出是:

[[0, 3],
 [3, 7],
 [7, 9],
 [9, 10]
因此,使用for循环的一种简单方法可能是:


ranges = np.zeros((n_chunks, 2), np.int64)
ranges_idx = 0
range_start_idx = start

sum = 0
for i in range(x.shape[0]):
    sum += x[i]
    if sum > x.sum() / n_chunks:
        ranges[ranges_idx, 0] = range_start_idx
        ranges[ranges_idx, 1] = min(
                i + 1, x.shape[0]
            )  # Exclusive stop index
        # Reset and Update
        range_start_idx = i + 1
        ranges_idx += 1
        sum = 0
# Handle final range outside of for loop
ranges[ranges_idx, 0] = range_start_idx
ranges[ranges_idx, 1] = x.shape[0]
if ranges_idx < n_chunks - 1:
    left[ranges_idx:] = x.shape[0]

return ranges

ranges=np.zero((n_块,2),np.int64)
范围_idx=0
范围\u开始\u idx=开始
总和=0
对于范围内的i(x.shape[0]):
总和+=x[i]
如果sum>x.sum()/n_块:
范围[ranges\u idx,0]=范围\u start\u idx
范围[ranges_idx,1]=min(
i+1,x.shape[0]
)#独家停止索引
#重置和更新
范围\u开始\u idx=i+1
范围_idx+=1
总和=0
#处理for循环之外的最终范围
范围[ranges\u idx,0]=范围\u start\u idx
范围[ranges_idx,1]=x.shape[0]
如果范围\u idx

我正在寻找一个更好的矢量化解决方案。

这里有一个不会迭代所有元素的解决方案:

chunk[0] = x[:7]  # [3, 9, 2, 1, 5, 4, 7], sum = 31
chunk[1] = x[7:]  # [7, 8, 6], sum = 21
def fun2(阵列,n): 最小和=np.和(数组)/n cumsum=np.cumsum(数组) i=-1 计数=最小和 out=[] 而i
对于这两个测试用例,它会产生您期望的结果。HTH

这里有一个解决方案,它不会迭代所有元素:

chunk[0] = x[:7]  # [3, 9, 2, 1, 5, 4, 7], sum = 31
chunk[1] = x[7:]  # [7, 8, 6], sum = 21
def fun2(阵列,n): 最小和=np.和(数组)/n cumsum=np.cumsum(数组) i=-1 计数=最小和 out=[] 而i 对于这两个测试用例,它会产生您期望的结果。HTH

我从一个

更新

为了涵盖病理病例,我们需要更精确一点,并采取如下措施:

def func(x, n, truncate=False):
    out = np.zeros((n_chunks, 2), np.int64)
    cum_arr = x.cumsum() / x.sum()
    idx = 1 + np.searchsorted(cum_arr, np.linspace(0, 1, n, endpoint=False)[1:])
    out[1:, 0] = idx  # Fill the first column with start indices
    out[:-1, 1] = idx  # Fill the second column with exclusive stop indices
    out[-1, 1] = x.shape[0]  # Handle the stop index for the final chunk

    # Handle pathological case
    diff_idx = np.diff(idx)
    if np.any(diff_idx == 0):
        row_truncation_idx = np.argmin(diff_idx) + 2
        out[row_truncation_idx:, 0] = x.shape[0]
        out[row_truncation_idx-1:, 1] = x.shape[0]
        if truncate:
            out = out[:row_truncation_idx]

    return out
我从一本书中得到了灵感:

更新

为了涵盖病理病例,我们需要更精确一点,并采取如下措施:

def func(x, n, truncate=False):
    out = np.zeros((n_chunks, 2), np.int64)
    cum_arr = x.cumsum() / x.sum()
    idx = 1 + np.searchsorted(cum_arr, np.linspace(0, 1, n, endpoint=False)[1:])
    out[1:, 0] = idx  # Fill the first column with start indices
    out[:-1, 1] = idx  # Fill the second column with exclusive stop indices
    out[-1, 1] = x.shape[0]  # Handle the stop index for the final chunk

    # Handle pathological case
    diff_idx = np.diff(idx)
    if np.any(diff_idx == 0):
        row_truncation_idx = np.argmin(diff_idx) + 2
        out[row_truncation_idx:, 0] = x.shape[0]
        out[row_truncation_idx-1:, 1] = x.shape[0]
        if truncate:
            out = out[:row_truncation_idx]

    return out

解决这个问题的编码尝试在哪里?您已经描述了算法——第一次实现尝试通常落在海报上(即您)。如果列表在到达N个块之前耗尽,会发生什么?例如,N=6,x=[3,3,3,11,11]。你得到了[3,3,3],[11],[11],[11]的块,然后你就没有剩下最后两个块的列表了。@Prune-Oops,我忘记了最重要的部分。我已经用代码更新了这个问题。在筋疲力尽的情况下,你可以立即结束。但在实际情况中,列表总是足够长,足以填满所有的块。好吧,现在这是一个非常不同的问题。我不确定在这种情况下你所说的“矢量化”是什么意思。可能使用
np.cumsum()
@rpoleski“矢量化”函数,就像在NumPy矢量化函数中一样,它允许我们避免显式for loops这里有解决这个问题的编码尝试吗?您已经描述了算法——第一次实现尝试通常落在海报上(即您)。如果列表在到达N个块之前耗尽,会发生什么?例如,N=6,x=[3,3,3,11,11]。你得到了[3,3,3],[11],[11],[11]的块,然后你就没有剩下最后两个块的列表了。@Prune-Oops,我忘记了最重要的部分。我已经用代码更新了这个问题。在筋疲力尽的情况下,你可以立即结束。但在实际情况中,列表总是足够长,足以填满所有的块。好吧,现在这是一个非常不同的问题。我不确定在这种情况下你所说的“矢量化”是什么意思。可能使用
np.cumsum()
@rpoleski“矢量化”,就像在NumPy矢量化函数中一样,它允许我们避免显式for loops注意到@Prune指出的病理情况,该代码比我的代码多返回2行。公平点。我已经更新了我的答案,希望能涵盖病理病例注意,对于@Prune指出的病理病例,这段代码比我的代码多返回2行。公平点。我已经更新了我的答案,希望能涵盖病理病例