在Python列表中滑动窗口

时间:2017-06-06 07:10:54

标签: python pandas numpy

我试图使用numpy / pandas构建滑动窗口样式比较器。我有列表列表,每个列表的长度不同。我想将每个列表与另一个列表进行比较,如下所示:

lists = [[10,15,5],[5,10],[5]]

window_diff(l[1],l[0]) = 25

列表[0]和列表[1]的窗口差异将使用下图中显示的以下窗口滑动技术给出25。因为列表[1]是较短的路径,所以我们将其向右移动一次,从而产生2个比较窗口。如果您对下图中的最后一行求和,我们会使用两个比较窗口获得两个列表之间的总差异;在这种情况下共计25个。要注意我们正在采用绝对差异。enter image description here

该函数应聚合每个列表与其他列表之间的总window_diff,因此在这种情况下

tot = total_diffs(lists)
tot>>[40, 30, 20]

# where tot[0] represents the sum of lists[0] window_diff with all other lists. 

我想知道在熊猫或numpy中是否有快速通道。目前我正在使用一个非常漫长的循环过程来循环遍历每个列表,然后通过按照较长列表移动较短列表来逐位比较。

我的方法适用于短列表,但我的数据集长度为10,000个列表,其中一些列表包含60个左右的数据点,因此速度是此处的标准。我想知道numpy,熊猫是否对此有一些建议?感谢

示例问题数据

from random import randint
lists = [[random.randint(0,1000) for r in range(random.randint(0,60))] for x in range(100000)]

2 个答案:

答案 0 :(得分:2)

步骤:

  • 在列表输入列表的每对列表中,为较大的数组创建滑动窗口,然后获得该对中较小的一个的绝对差异。我们可以使用NumPy strides来获取那些滑动窗口。

  • 获得总和并将此总和存储为成对差异化。

  • 最后在上一步的2D数组的每一行和col上求和,它们的总和是最终输出。

因此,实现看起来像这样 -

import itertools

def strided_app(a, L, S=1 ):  # Window len = L, Stride len/stepsize = S
    a = np.asarray(a)
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))

N = len(lists)
pair_diff_sums = np.zeros((N,N),dtype=type(lists[0][0]))
for i, j in itertools.combinations(range(N), 2):
    A, B = lists[i], lists[j]
    if len(A)>len(B):
        pair_diff_sums[i,j] = np.abs(strided_app(A,L=len(B)) - B).sum()
    else:
        pair_diff_sums[i,j] = np.abs(strided_app(B,L=len(A)) - A).sum()

out = pair_diff_sums.sum(1) + pair_diff_sums.sum(0)

对于非常繁重的数据集,这里有一种方法,使用一个更多级别的循环 -

N = len(lists)
out = np.zeros((N),dtype=type(lists[0][0]))
for k,i in enumerate(lists):
    for j in lists:
        if len(i)>len(j):
            out[k] += np.abs(strided_app(i,L=len(j)) - j).sum()
        else:
            out[k] += np.abs(strided_app(j,L=len(i)) - i).sum()

strided_app的灵感来自here

示例输入,输出 -

In [77]: lists
Out[77]: [[10, 15, 5], [5, 10], [5]]

In [78]: pair_diff_sums
Out[78]: 
array([[ 0, 25, 15],
       [25,  0,  5],
       [15,  5,  0]])

In [79]: out
Out[79]: array([40, 30, 20])

答案 1 :(得分:2)

仅仅为了完美了解@Divakar的优秀答案及其在大型数据集中的应用:

import itertools

N = len(lists)
out = np.zeros(N, dtype=type(lists[0][0]))

for i, j in itertools.combinations(range(N), 2):
    A, B = lists[i], lists[j]

    if len(A)>len(B):
        diff = np.abs(strided_app(A,L=len(B)) - B).sum()
    else:
        diff = np.abs(strided_app(B,L=len(A)) - A).sum()

    out[i] += diff
    out[j] += diff

它不会创建不必要的大型数据集,只会在上三角数组上进行迭代时更新单个向量。

计算仍需要一段时间,因为在计算复杂性和大于ram的数据集之间存在权衡。大于ram数据集的解决方案通常依赖于迭代,而python并不是很好。在python中迭代大型数据集很慢,非常慢。

将上面的代码翻译成cython可能会加速一些事情。