如何对数组中的一个范围值求和,其中每个值有一个不同的范围

2024-04-18 12:59:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我想计算数组中某个范围的和(简单)——但我不想这样做,而是n次,应该求和的范围来自第二个数组。你知道吗

我有一个带有0和1的2D数组:

count = np.array(\
[[0,1,0,0,1,0,1],
 [0,0,1,1,1,0,0]])

我构造了一个2D数组,该数组有一个字段,其中包含要在count数组上求和的范围。你知道吗

dtype=[..., ('ranges', 'u1', (2, 2)) , ...]

table['ranges']看起来像这样:

 [
 [[1, 3], [0, 4]],
 [[0, 0], [3, 4]],
 [[0, 0], [2 4]],
 [[0, 0], [3 4]],
 [[3, 7], [1 5]]]

(通常在20到几百行之间)。你知道吗

这个例子的结果应该是

[2, # = (1 +0) + (0 + 0 +1)
 1, # = ( ) + (1)
 2,  # = ( ) + (1 + 1)
 1, # = ( ) + (1)
 5] # = (0 + 1 +0 +1 ) + (0 + 1 + 1 + 1)

首先我从:

        result = np.zeros(table.size, dtype=np.int)

        for index, r in enumerate(table):
            for index, range in enumerate(r['ranges']):
                result[index] += np.sum(counts[index][range[0]:range[1]])

给出了正确的结果,但不是效率的例子。你知道吗

我还试图消除第二个循环,并将其放大一点:

        result = np.zeros(table.size, dtype=np.int)

        for index, (from1, to1, from2, to2) in \
                enumerate(np.nditer(table['ranges'], flags=['external_loop'])):
            counts[index] += np.sum(counts[0][from1:to1]) +\
                np.sum(counts[1][from2:to2])

但是这些代码行仍然是应用程序花费大部分时间的地方。这个应用程序比这个要大一点,但是根据profiler的说法,大约有一半的时间花在这些行上。你知道吗

所以基本上我正在寻找一种方法来摆脱循环,并在numpy中实现这一切。 我在找一些类似

counts=np.sum(counts[1][table['ranges'][0][0]:table['ranges'][0][1])+np.sum(counts[2][table['ranges'][1][0]:table['ranges'][1][1])

但到目前为止还没有找到一个好的方法。你知道吗

更新进行了一些时间比较:

import numpy as np
import timeit as ti

table = np.empty(5,
                 dtype=[('s1', np.int8),
                        ('ranges', 'u1', (2, 2)),
                        ('s2', np.int16)])

table["ranges"] = [((1, 3), (0, 4)),
                   ((0, 0), (3, 4)),
                   ((0, 0), (2, 4)),
                   ((0, 0), (3, 4)),
                   ((3, 7), (1, 5))]

results = np.zeros(table.size)

counts = np.array([[0, 1, 0, 0, 1, 0, 1],
                   [0, 0, 1, 1, 1, 0, 0]])


# version one
def rv1(table, counts, results):
    for row_index, r in enumerate(table):
        for index, crange in enumerate(r['ranges']):
            results[row_index] += np.sum(counts[index][crange[0]:crange[1]])


# version two
def rv2(table, counts, results):
    for rowindex, (f1, t1, f2, t2) in \
            enumerate(np.nditer(table['ranges'], flags=['external_loop'])):
        results[rowindex] += np.sum(counts[0][f1:t1]) +\
            np.sum(counts[1][f2:t2])


# version 3 (TomNash)
def rvTN(table, counts, results):
    ranges=table["ranges"]
    result=[
        sum(counts[0][slice(*ranges[i][0])]) + sum(counts[1][slice(*ranges[i][1])])
            for i in range(len(ranges))]
    results+=result


results = np.zeros(table.size)
rv1(table, counts, results)
print ("rv1 result" , results)


results = np.zeros(table.size)
rv2(table, counts, results)
print ("rv2 result", results)

results = np.zeros(table.size)
rvTN(table, counts, results)
print ("slice*(TN) result", results)



print ("double loop time " , ti.timeit(lambda : rv1(table, counts, results)))
print ("nditer time " ,  ti.timeit(lambda : rv2(table, counts, results)))
print ("slice* time " ,  ti.timeit(lambda : rv3(table, counts, results)))

我明白了

double loop result [3. 1. 2. 1. 5.]
nditer result [3. 1. 2. 1. 5.]
slice* result [3. 1. 2. 1. 5.]
double loop time  42.41987561201677
nditer time  36.45269059110433
slice* time  24.102186055853963

所以Tomnash版本的速度快了30%。不幸的是,这还是有点慢。你知道吗


Tags: inforsizeindexnptablezerosslice
1条回答
网友
1楼 · 发布于 2024-04-18 12:59:51

您可以使用slice*args来分解开始索引、停止索引和切片的列表。你知道吗

[sum(count[0][slice(*ranges[i][0])]) + sum(count[1][slice(*ranges[i][1])]) for i in range(len(ranges))]

我认为你的预期结果和你的指数有点出入,这就是我得到的。你知道吗

结果

[3, 1, 2, 1, 5]

相关问题 更多 >