使用pandas数据帧最好的方法是什么我想循环遍历一个数据帧,并找到最近的下一个索引,该索引的值差至少为+/-2例如:[100,99,102,98,103,103]将使用此[2,2,3,0,N/a]创建一个新列,0表示找不到。
我的解决方案性能是n*log(n)。有没有聪明人能给我看一个更好的性能解决方案?

最佳答案

当所有元素都是整数时,可以在线性时间内这样做。下面的解决方案很复杂,而且只对算法感兴趣(如果是这样的话)。因为它使用循环和数据结构,任何实际的实现都需要在C/C++ + Cython中(否则,常数将非常高,需要一个非常长的序列来开始看到一个改进,即使它是线性的)。
由于解决方案很复杂,我将首先进行一些简化假设,然后演示如何消除这些假设。最初的假设是:
我们需要的是找到下一个位置的指数是2或更大。
所有整数都是不同的。
考虑到这些假设,有可能使用一个众所周知的面试问题的变体(这很常见,我认为这是民间传说)这样做的目的是将数组的一堆位置放在尚未找到下一个位置的地方在元素和位置上循环时,保持循环不变量:
堆栈中的索引正在增加。
堆栈不包含位置i,j,使得a[i]+2不变量最初很容易满足,我将展示如何维护它们。
假设在迭代j中,堆栈的顶部是i:我将其标记为(堆栈向右)当a[j]>=a[i]+2时,我们可以弹出堆栈并将i的下一个位置设置为j。当这种情况发生时,我们可以弹出堆栈,直到条件失败。不过,在某一点上,堆栈可以是,而a[i]+2>a[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,一个检查第二个简单化的消除是更棘手的。问题是,如果不需要弹出堆栈的顶部,我们可能需要搜索许多项,看看是否有人需要弹出-这不一定是真的,这个潜在的项目正下方。如果堆栈顶部的元素相同,则可能发生这种情况。
处理这个问题是乏味的,但在概念上并没有那么困难。堆栈现在需要包含等价元素的连续非替换索引的整数列表。这意味着,当推送新索引时,需要检查它是否继续运行。如果是,则将其附加到顶部的列表中;如果不是,则创建一个新元组。现在,所有连续的等价未替换项都被组合在一起(类似于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]

10-08 09:18
查看更多