【问题标题】:Count values in overlapping sliding windows in python在python中计算重叠滑动窗口中的值
【发布时间】:2019-01-17 13:43:13
【问题描述】:

给定一个数组a排序值和一个范围数组bins,计算a中有多少值属于每个范围内的最有效方法是什么范围,rng,在bins

目前我正在做以下事情:

def sliding_count(a, end, window, start=0, step=1):
    bins = [(x, x + window) for x in range(start, (end + 1) - window, step)]
    counts = np.zeros(len(bins))
    for i, rng in enumerate(bins):
        count = len(a[np.where(np.logical_and(a>=rng[0], a<=rng[1]))])
        counts[i] = count
    return counts

a = np.array([1, 5, 8, 11, 14, 19])
end = 20
window = 10
sliding_count(a, end, window)

返回预期的数组

array([3., 4., 3., 3., 4., 4., 3., 3., 3., 3., 3.])

但我觉得必须有更有效的方法来做到这一点?

【问题讨论】:

  • 恕我直言,这非常有效。您是否在寻找更有效的方法?
  • 您是否知道range 返回一个对象,您可以在该对象上执行x in range(...) 并获得真/假?
  • @Nullman 是的,我做到了,但这意味着我必须为 a 中的每个元素迭代一次 bins
  • 窗口是否总是步长的倍数?

标签: python algorithm numpy


【解决方案1】:
import numpy as np

def alt(a, end, window, start=0, step=1):
    bin_starts = np.arange(start, end+1-window, step)
    bin_ends = bin_starts + window
    last_index = np.searchsorted(a, bin_ends, side='right')
    first_index = np.searchsorted(a, bin_starts, side='left')
    return  last_index - first_index

def sliding_count(a, end, window, start=0, step=1):
    bins = [(x, x + window) for x in range(start, (end + 1) - window, step)]
    counts = np.zeros(len(bins))
    for i, rng in enumerate(bins):
        count = len(a[np.where(np.logical_and(a>=rng[0], a<=rng[1]))])
        counts[i] = count
    return counts

a = np.array([1, 5, 8, 11, 14, 19])
end = 20
window = 10

print(sliding_count(a, end, window))
# [3. 4. 3. 3. 4. 4. 3. 3. 3. 3. 3.]

print(alt(a, end, window))
# [3 4 3 3 4 4 3 3 3 3 3]

alt 的工作原理:

生成 bin 的起始值和结束值:

In [73]: bin_starts = np.arange(start, end+1-window, step); bin_starts
Out[73]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

In [74]: bin_ends = bin_starts + window; bin_ends
Out[74]: array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])

由于a是有序的,你可以使用np.searchsorted来查找第一个和最后一个索引 在bin_startsbin_ends 中,a 中的每个值都适合:

In [75]: last_index = np.searchsorted(a, bin_ends, side='right'); last_index
Out[75]: array([3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6])

In [76]: first_index = np.searchsorted(a, bin_starts, side='left'); first_index
Out[76]: array([0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3])

count 只是索引的区别:

In [77]: last_index - first_index
Out[77]: array([3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3])

这是perfplot 比较altsliding_count 的性能作为a 长度的函数:

import perfplot

def make_array(N):
    a = np.random.randint(10, size=N)
    a = a.cumsum()
    return a

def using_sliding(a):
    return sliding_count(a, end, window)

def using_alt(a):
    return alt(a, end, window)

perfplot.show(
    setup=make_array,
    kernels=[using_sliding, using_alt],
    n_range=[2**k for k in range(22)],
    logx=True,
    logy=True,
    xlabel='len(a)')

Perfplot 还会检查 using_sliding 返回的值是否等于 using_alt 返回的值。

Matt Timmermans' idea, "subtract position_in_a from the count for that bin" 触发了这个解决方案。

【讨论】:

    【解决方案2】:

    bin 中的元素数 b 是元素数 &lt;= b.end 减去元素数 &lt; b.start

    因此,您可以创建一个按 start 排序的 bin 数组 starts 和一个按 end 排序的 bin 数组 ends。然后逐步遍历所有 3 个数组。当您前进超过 a 中的每个 x 时,从该 bin 的计数中前进超过 x &lt; b.start减去 position_in_a 的开头。然后使用x &lt;= b.end添加 position_in_a 到该垃圾箱的计数中。

    总复杂度为 O(N log N),主要是对开始和结束数组进行排序。遍历 3 个数组并调整计数是 O(N)。

    在您的代码中,您正在生成已排序的 bin 数组,因此如果可以这样做,则可以跳过排序步骤,总复杂度为 O(a.length+bin_count)。我什至不会费心生成该数组,因为您可以轻松地从索引中计算开始值和结束值。

    【讨论】:

      【解决方案3】:

      类似这样的东西(?):

      def sliding_count(a, nx0, nx1, window):
          bin0 = np.arange(nx0,nx1,1)
          bin1 = bin0 + window 
          count = np.zeros((nx1-nx0), dtype=int)
      
          for j in range(nx1-nx0):
              count[j] = np.sum(a<=bin1[j]) - np.sum(a<bin0[j])
          return count
      
      #---- main ---------------  
      nx0, nx1, window = 0, 11, 10
      a = np.array([1, 5, 8, 11, 14, 19])
      sliding_count(a, nx0, nx1, window)
      
      array([3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3])
      

      我没有检查 bin0 = np.arange(nx0,nx1,1)nx0>0step>1 的代码>。因此,对于这种情况,必须修改 for 循环的长度。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-16
        • 2020-09-19
        • 2023-01-29
        • 2017-07-06
        • 2018-08-27
        • 1970-01-01
        • 2015-02-21
        • 2018-02-11
        相关资源
        最近更新 更多