Python 获取分割NumPy数组的索引
假设我有一个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
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=[]
而idef 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行。公平点。我已经更新了我的答案,希望能涵盖病理病例