交错两个numpy索引数组,每个数组一个项目

时间:2012-08-06 11:08:12

标签: python arrays numpy vectorization

我有两个有序的numpy数组,我想交错它们,以便我从第一个数组中取出一个项目,然后从第二个数据中取出另一个项目,然后返回到第一个数据 - 获取大于我刚才的下一个项目从第二个开始,依此类推。 这些实际上是其他数组的索引数组,只要操作是向量化的,我就可以对原始数组进行操作(当然,作为向量操作工作在索引数组上会非常棒)。 / p>

示例(可以假设数组的交集是空的)

a = array([1,2,3,4,7,8,9,10,17])
b = array([5,6,13,14,15,19,21,23])

我想得到[1,5,7,13,17,19]

5 个答案:

答案 0 :(得分:13)

矢量化解决方案(教学风格,易于理解)

我们可以通过使用鉴别器索引扩充数组来对此进行矢量化,以便a被标记为0b被标记为1

a_t = np.vstack((a, np.zeros_like(a)))
b_t = np.vstack((b, np.ones_like(b)))

现在,让我们结合并排序:

c = np.hstack((a_t, b_t))[:, np.argsort(np.hstack((a, b)))]
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 13, 14, 15, 17, 19, 21, 23],
       [ 0,  0,  0,  0,  1,  1,  0,  0,  0,  0,  1,  1,  1,  0,  1,  1,  1]])

您现在可以看到元素按顺序排列但保留了标记,因此我们可以看到哪些元素来自ab

因此,让我们选择第一个元素以及标记从0(对于a)更改为1(对于b)并再次返回的每个元素:< / p>

c[:, np.concatenate(([True], c[1, 1:] != c[1, :-1]))][0]
array([ 1,  5,  7, 13, 17, 19])

高效的载体化溶液

通过将项目及其标记保存在单独(但并行)的数组中,您可以更有效地执行此操作:

ab = np.hstack((a, b))
s = np.argsort(ab)
t = np.hstack((np.zeros_like(a), np.ones_like(b)))[s]
ab[s][np.concatenate(([True], t[1:] != t[:-1]))]
array([ 1,  5,  7, 13, 17, 19])

这比上述解决方案稍微有效;虽然您的条件可能会有所不同,但平均为45而不是90微秒。

答案 1 :(得分:3)

您可以将问题分成两部分:

  1. 生成ab迭代器,只有在它们生成时才会生成值 大于某些threshold
  2. 使用itertools(基本上是George Sakkis的roundrobin配方)在两个迭代器之间交替。

  3. import itertools
    import numpy as np
    
    a = np.array([1,2,3,4,7,8,9,10,17])
    b = np.array([5,6,13,14,15,19,21,23])
    
    def takelarger(iterable):
        for x in iterable:
            if x > takelarger.threshold:
                takelarger.threshold = x
                yield x
    takelarger.threshold = 0
    
    def alternate(*iterables):
        # Modified version of George Sakkis' roundrobin recipe
        # http://docs.python.org/library/itertools.html#recipes
        pending = len(iterables)
        nexts = itertools.cycle(iter(it).next for it in iterables)
        while pending:
            try:
                for n in nexts:
                    yield n()
            except StopIteration:
                # Unlike roundrobin, stop when any iterable has been consumed
                break
    
    def interleave(a, b):
        takelarger.threshold = 0
        return list(alternate(takelarger(a),takelarger(b)))
    
    print(interleave(a,b))
    

    产量

    [1, 5, 7, 13, 17, 19]
    

答案 2 :(得分:1)

a = [1,2,3,4,7,8,9,10,17]
b = [5,6,13,14,15,19,21,23]
c=[]
c.append(a[0])  #add the first element to c
i=True          #breaking condition
loop=2          #initially we to choose b
while i:
    if loop==2:
        val=c[-1]
        temp_lis=d([x-val for x in b])  #find the difference between every
                                        # element and the last element of c and
                                        # pick the smallest positive value.

        for k,y in enumerate(temp_lis):
            if y>0:
                c.append(b[k])
                break
        else:
            i=False        #use for-else loop for determining the breaking condition


        loop=1   #change the loop to 1
    elif loop==1:
        val=c[-1]
        temp_lis=[x-val for x in a]
        for k,y in enumerate(temp_lis):
            if y>0:
                c.append(a[k])
                break
        else:
            i=False


        loop=2

print c  

<强>输出:

[1, 5, 7, 13, 17, 19]

答案 3 :(得分:1)

我认为您很难将numpy矢量化应用于此问题并保持线性性能。这需要存储相当多的状态:a中的当前索引,b中的当前索引和当前thresholdnumpy不提供许多有状态向量化函数。事实上,我能想到的唯一一个问题是ufunc.reduce,这个问题并不适合这个问题 - 当然它可能会被迫使用。

事实上,在我发布此消息后,我的浏览器更新了excellent vectorized solution。但该解决方案需要排序,即O(n log n);事实上,经过一些测试,我发现下面的纯python解决方案对所有输入都更快!

def interleave_monotonic(a, b):
    try:
        a = iter(a)
        threshold = next(a)
        yield threshold
        for current in b:
            if current > threshold:
                threshold = current
                yield threshold
                while current <= threshold:
                    current = next(a)
                threshold = current
                yield threshold
    except StopIteration:
        return

请注意,如果a为空,则返回空迭代,但如果b为空,则返回包含第一个a值的单项迭代。我认为与你的例子一致,但这是一个我不确定的边缘情况。此外,基于numpy的解决方案的行为略有不同,始终以a[0]b[0]中的较小者开头,而上述内容始终以a的第一个值开头,而不管。我修改了上面的内容以检查以下测试的内容,这些测试很清楚地表明纯python解决方案是最快的。

说明:

def interleave_monotonic_np(a, b):
    ab = np.hstack((a, b))
    s = np.argsort(ab)
    t = np.hstack((np.zeros_like(a), np.ones_like(b)))[s]
    return ab[s][np.concatenate(([True], t[1:] != t[:-1]))]

def interleave_monotonic(a, b):
    a, b = (a, b) if a[0] <= b[0] else (b, a)
    try:
        a = iter(a)
        threshold = next(a)
        yield threshold
        for current in b:
            if current > threshold:
                threshold = current
                yield threshold
                while current <= threshold:
                    current = next(a)
                threshold = current
                yield threshold
    except StopIteration:
        return

def interleave_monotonic_py(a, b):
    return numpy.fromiter(interleave_monotonic(a, b), dtype='int64')

def get_a_b(n):
    rnd = random.sample(xrange(10 ** 10), n * 2)
    return sorted(rnd[0::2]), sorted(rnd[1::2])

试验:

>>> for e in range(7):
...         a, b = get_a_b(10 ** e)
...         print (interleave_monotonic_np(a, b) == 
...                interleave_monotonic_py(a, b)).all()
...         %timeit interleave_monotonic_np(a, b)
...         %timeit interleave_monotonic_py(a, b)
...

True
10000 loops, best of 3: 85.6 us per loop
100000 loops, best of 3: 5.53 us per loop
True
10000 loops, best of 3: 91.7 us per loop
100000 loops, best of 3: 9.19 us per loop
True
10000 loops, best of 3: 144 us per loop
10000 loops, best of 3: 46.5 us per loop
True
1000 loops, best of 3: 711 us per loop
1000 loops, best of 3: 445 us per loop
True
100 loops, best of 3: 6.67 ms per loop
100 loops, best of 3: 4.42 ms per loop
True
10 loops, best of 3: 135 ms per loop
10 loops, best of 3: 55.7 ms per loop
True
1 loops, best of 3: 1.58 s per loop
1 loops, best of 3: 654 ms per loop

答案 4 :(得分:1)

受到其他帖子的启发,我对纯python代码的另一个想法是需要事先排序,但是它与a和b对称

def myIterator4(a, b):
    curr = iter(a)
    next = iter(b)
    try:
        max = curr.next()
        tmp = next.next() #  Empty iterator if b is empty
        while True:
            yield max
            while tmp<=max:
                tmp = next.next()           
            max = tmp
            curr, next  = next, curr
    except StopIteration:
        return  

如果a或b为空,则返回空迭代器。