熊猫数据框预测优化

时间:2016-09-19 04:18:19

标签: python algorithm

使用pandas dataframe执行此操作的最佳方法是什么?我想遍历一个数据帧,找到最接近+/- 2值差异的最近的下一个索引。例如:[100,99,102,98,103,103]将使用此[2,2,3,0,N / A]创建一个新列,0表示找不到。

我的解决方案性能是n * log(n)。任何才华横溢的人都可以向我展示更好的性能解决方案吗?

1 个答案:

答案 0 :(得分:1)

当所有元素都是整数时,可以在线性时间内完成。以下解决方案很复杂,并且只有算法兴趣(如果在那里)。由于它使用循环和数据结构,任何真正的实现都需要在C / C ++ / Cython中(否则,常量会如此之高,以至于需要非常长的序列来开始看到改进,即使它和#39 ; s线性)。

由于解决方案很复杂,我首先会做一些简化的假设,然后展示如何摆脱它们。最初的假设是:

  1. 需要的是找到下一个位置为2或更大的索引。

  2. 所有整数都是不同的。

  3. 考虑到这些假设,可以使用一个众所周知的面试问题的变体(它是如此常见,我认为它是民间传说)。我们的想法是保持数组的位置堆栈,其中尚未找到下一个位置。循环遍历元素和位置时,将保持循环不变量:

    • 堆栈中的索引正在增加。

    • 堆栈不包含 i j 的位置,以便 a [i] + 2< = a [j] i< Ĵ

    不变量最初是非常满意的,我将展示它们是如何维护的。

    在迭代 j 中说,堆栈的顶部是 i :我将此标记为< ...,i> ; (堆栈向右移动)。当 a [j]> = a [i] + 2 时,我们可以弹出堆栈并将 i 的下一个位置设置为 j 。当发生这种情况时,我们可以弹出堆栈直到条件失败。然而,有些人认为,堆栈可以是< ...,k,i> ,其中 a [i] + 2>一个[j]的。关于不变量的一些想法足以看出,在这种情况下,如果堆栈中有一个需要弹出的元素,它必须是 k (如果它存在)。这是唯一需要检查的项目 - 在最后一项之前的任何其他项目都不能是需要弹出的项目。所以,我们只需要检查 k ,并在必要时弹出它。在迭代结束时,我们只需要推送 j 本身。

    以下代码执行此操作:

    def plus2_detector(a, verbose=False):
        if verbose:
            print 'starting with', a
        remaining, out = [], [None] * len(a)
        for i, e in enumerate(a):
            if verbose:
                print 'it begin', i, e, remaining
            while remaining and e >= a[remaining[-1]] + 2:
                if verbose:
                    print 'setting', i, remaining[-1], a[remaining[-1]]
                out[remaining[-1]] = i 
                del remaining[-1]
            if len(remaining) > 1 and e >= a[remaining[-2]] + 2:
                if verbose:
                    print 'back setting', i, remaining[-2], a[remaining[-2]]
                out[remaining[-2]] = i 
                del remaining[-2]
            remaining.append(i) 
            if verbose:
                print 'it end', i, e, remaining
        return out
    

    您可以运行它,例如

    >>> plus2_detector([1, 2, 3, 5, 4, -1, -2, 10, 9, 8, 7, 11], False)
    [2, 3, 3, 7, 7, 7, 7, None, 11, 11, 11, None]
    

    为了直观地了解它的功能,您可以使用verbose=True在不同的(不同的整数!)上运行它,看看它的作用。

    现在摆脱简化。

    第一个简化设置很容易:运行此算法的两个副本:一个检查> = 2 ,另一个检查< = -2 ,以及结合结果。

    第二次简化谜语更加棘手。问题是如果堆栈的顶部不需要弹出,我们可能需要搜索许多项目以查看是否需要弹出任何人 - 这个潜在的项目不一定是真的在顶部。如果堆栈顶部的元素相同,则会发生这种情况。

    处理这个问题很乏味,但概念上并不困难。堆栈现在需要包含等效元素的连续欠盐指数的整数列表。这意味着当您推送新索引时,您需要检查它是否继续运行。如果是,请将其附加到顶部的列表中;如果没有,创建一个新的元组。现在所有连续等效的未定位项都被组合在一起(类似于itertools.groupby所做的)。

    存在技术上的复杂情况(当弹出倒数第二个列表时,我们可能需要结合顶部和新的倒数第二个元组),但这个想法是一样的。

    使用摊销分析的标准参数,复杂性是线性的(每个元素都插入并弹出一次,非弹出操作是常量)。

    以下是查找+2或以上索引的一般情况的代码,没有元素唯一的限制:

    def general_plus2_detector(a, verbose=False):
        if verbose:
            print 'starting with', a
        remaining, out = [], [None] * len(a)
        for i, e in enumerate(a):
            if verbose:
                print 'it begin', i, '(', e, ')', remaining
            while remaining and e >= a[remaining[-1][0]] + 2:
                for j in remaining[-1]:
                    if verbose:
                        print 'setting', j, '(', a[j], ') to', i, '(', a[i], ')'
                    out[j] = i
                del remaining[-1]
            if len(remaining) > 1 and e >= a[remaining[-2][0]] + 2:
                for j in remaining[-2]:
                    if verbose:
                        print 'back setting', j, '(', a[j], ') to', i, '(', a[i], ')'
                    out[j] = i
                del remaining[-2]
                if len(remaining) > 1 and a[remaining[-2][0]] == a[remaining[-1][0]]:
                    if verbose:
                        print 'joining', remaining[-2], remaining[-1]
                    remaining[-1].extend(remaining[-2])
                    del remaining[-2]
            if not remaining or a[remaining[-1][0]] != e:
                remaining.append([i]) 
            else:
                remaining[-1].append(i)
            if verbose:
                print 'it end', i, '(', e, ')', remaining
        return out
    

    运行它显示:

    a = [-1, -2, 3, 2, 2, 3, 2, 2, 4, 5, 4, 5, 2, 3, 4, 5, 5, 4, 4, 7]
    >>> general_plus2_detector(a, False)
    [2, 2, 9, 8, 8, 9, 8, 8, 19, 19, 19, 19, 14, 15, 19, 19, 19, 19, 19, None]