【问题标题】:fill vector values between specified indices在指定索引之间填充向量值
【发布时间】:2019-09-16 23:25:59
【问题描述】:

从一个向量a 和一个索引列表ind 开始,我想将a 的元素设置为与ind 中的索引对应的原始向量的函数(在示例中,我将只将原始值加 10),并希望为所有后续元素保留与 ind 中每个索引对应的值,直到下一个索引。

例如,给定:

a = [10, 11, 12, 13, 14, 15]
ind = [0, 2, 4]

返回:

result = [20, 20, 22, 22, 24, 24]

您可以注意到a 的元素与ind 中的索引不对应是完全不相关的。此外,如果第一个元素的索引不在ind 中,则它的第一个元素存在不确定性,所以我希望能够为第一个元素设置一个默认值,如果0 不在ind 中,则使用完整的一组测试用例是:

#test
a = np.array([10, 11, 12, 13, 14, 15])

fill(a, [2, 4], -50)
#[-50, -50, 22, 22, 24, 24]
fill(a, [0, 2, 4], -50)
#[20, 20, 22, 22, 24, 24]
fill(a, [0, 2, 4, 5], -50)
#[20, 20, 22, 22, 24, 25]

我可以很容易地用循环来完成,但如果向量a 很大,这会很慢:

def fill(a, ind, startval):
    res = np.zeros(len(a) + 1)
    res[0] = startval
    for i in range(len(a)):
        if i in ind:
            res[i + 1] = a[i] + 10
        else:
            res[i + 1] = res[i]
    print(res[1:])

最有效的方法是什么?

【问题讨论】:

  • 感谢您的编辑。
  • 2 年后:不客气。经过这么多年,我终于想出了如何修复我的答案并取消删除它。

标签: python arrays numpy numpy-ndarray


【解决方案1】:

这是一种在索引上使用diff 来获取块大小,然后在值上使用repeat 来创建块的方法。

def fill(a,ind,f=lambda x:x+10,default=-50):
    sizes = np.diff(ind,prepend=0,append=len(a))
    values = np.concatenate([[default],f(a[ind])])
    return values.repeat(sizes)

【讨论】:

  • 感谢您向我展示prepend。如果之前看过关于它的讨论,但忘记了它是添加的。我的上一个项目现在是跳过箍来获得那些额外的框架元素。
  • 我看看能不能凑个 PR。我们可以将diff 使用的代码分解为实用函数。这几乎是微不足道的。
【解决方案2】:

np.repeat 显然是这里的方法,np.cumsum 也是一种选择。您唯一需要计算的是连续元素之间的差异。鉴于 np.diff 基本上是 np.cumsum 的倒数,并且零元素不会影响 cumsum,您可以执行以下操作:

def fill_cumsum(a, ind, f=lambda x:x + 10, default=-50):
    vals = np.diff(f(a[ind]))
    a = np.zeros_like(a)
    a[0] = default      # Do this first
    a[ind[0]] = a[ind[0] - np.sign(ind[0]) * default
    a[ind[1:]] = vals   # Overwrite zero automatically
    return a.cumsum()

如果您想就地执行相同的操作,只需将a = np.zeros_like(a) 更改为a[:] = 0 并将out=a 添加到返回cumsum

两个答案速度差不多:

a = np.random.randint(1000, size=10000)
ind = np.unique(np.random.randint(10000, size=100))
%timeit_repeat fill(a, ind)
43 µs ± 659 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit fill_cumsum(a, ind)
35.6 µs ± 367 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

a = np.random.randint(1000, size=100000)
ind = np.unique(np.random.randint(100000, size=100))
%timeit fill(a, ind)
237 µs ± 592 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit fill_mp(a, ind)
245 µs ± 521 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

这个答案对整数很有效,但np.repeat 引入的浮点舍入误差更少,因为它不调用np.diff

【讨论】:

    【解决方案3】:

    我接受了 Paul Panzer 的回答,这很聪明,而且效果很好。 但是,为了完整起见,我还想添加我自己的答案。

    此解决方案更灵活,因为它允许结果向量的值由先前的元素确定,但我不喜欢它,因为嵌套了 if

    我未能找到可以消除嵌套循环和 IF 的索引选择,但是从许多测试来看,这两种解决方案在执行时间方面是等效的(我最初的示例大约慢了 1000 倍):

    def fill(a,ind,startval):
        res=np.zeros(len(a))
        for i,it in enumerate(ind):
            if i == 0:
                if it == 0:
                    res[0]=a[0]+10
                else:
                    res[0:it]=startval        
            else:
                res[ind[i-1]:it]=res[ind[i-1]]
            res[it]=a[it]+10
        res[ind[-1]:]=res[ind[-1]]
        print (res) 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-19
      • 2018-07-28
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-21
      相关资源
      最近更新 更多