【问题标题】:Python/Numpy - Fill gaps between non-consecutive points?Python/Numpy - 填补非连续点之间的空白?
【发布时间】:2014-11-21 10:59:53
【问题描述】:

我正在尝试找到一种矢量化/快速/numpy 友好的方式来将 A 列中的以下值转换为 B 列:

ID  A   B
1   0   0
2   0   0
3   1   0
4   1   1
5   0   1
6   0   1
7   -1  1
8   0   0
9   1   0
10  0   1
11  0   1
12  1   1
13  0   1
14  -1  1
15  0   0

定义列“B”的算法将使用值 1 填充 1 和 -1 组之间的所有间隙,跳过每对中的第一行。也就是说,对于 ID4-ID7,B 列用 1 填充(给定 A 列中的初始 1 @ ID3)。接下来,从 ID10-ID14 填充一个(因为列 A @ ID9 =1)。

虽然使用 for 循环很容易做到这一点,但我想知道是否存在非循环解决方案?下面是一个基于 O(n) 循环的解决方案:

import numpy as np
import pandas as pd
x = np.array([ 0, 0, 1, 1, 0 ,0, -1, 0, 1, 0 , 0, 1, 0, -1, 0])


def make_y(x,showminus=False):
    y = x * 0
    state = 0 # are we in 1 or 0 or -1
    for i,n in enumerate(x):
        if n == 1 and n != state:
            state = n
            if i < len(y)-1:
                y[i+1] = state
        elif n == -1 and n != state:
            y[i] = state
            if showminus:
                state = -1
            else:
                state = 0
        else:
            y[i] = state
    return y

y = make_y(x)
print pd.DataFrame([x,y]).T

上述函数在我的机器上产生以下性能:

%timeit y = make_y(x)
10000 loops, best of 3: 28 µs per loop

我猜肯定有某种方法可以让整个事情变得更快,因为我最终需要处理超过 1000 万个元素长的数组...

【问题讨论】:

  • 模式是否总是如果 A 为 1,则下一行 B 为 1,直到 -1 出现在 A 中。即 1 和 -1 标记连续 1 的开始和结束(但不包括1 出现在 A) 中的行
  • @EdChum - 没错。但是,您可能已经注意到在make_y 循环函数中还有一个参数也可以跟踪-1 区域。为了简化事情(最初),我把那部分排除在外。
  • 这很棘手,我想不出没有迭代的方法,你可以使用mask = df.loc[(df['A'].shift() == 1) | (df['A']==-1)] 之类的东西获取这些标记的索引,然后使用mask.loc[(mask['A'] == -1) | (mask['A'].shift(-1) != -1)] 再次折叠它应该然后显示开始和结束索引,然后迭代或将索引拉入元组对列表中,其中该对已经开始,结束并将这些设置为 1。

标签: python algorithm numpy pandas vectorization


【解决方案1】:

一个可能的矢量化解决方案如下

idx_1s, = np.where(x == -1)  # find the positions of the -1's
idx1s, = np.where(x == 1)  # find the positions of the 1's

找出哪些 1 应该变成 0 并标记一个 1 块的开始:

idx0s = np.concatenate(([0], np.searchsorted(idx1s, idx_1s[:-1])))
idx0s = idx1s[idx0s]

我们现在有两个长度相等的数组,idx0sidx_1s,标记每个块的第一项和最后一项的位置,所以我们现在可以这样做:

y = x.copy()
y[idx0s] = 0
idx0s += 1
idx_1s += 1
mask = np.zeros_like(y, dtype=np.bool)
mask[idx0s] = True
mask[idx_1s] = True
mask = np.logical_xor.accumulate(mask)
y[mask] = 1

产生期望的结果:

>>> y
array([0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])

格式错误的输入可能有点脆弱,我认为它不会优雅地处理尾随的 -1。但唯一的非 O(n) 操作是对 searchsorted 的调用,但 searchsorted 进行了优化,可以更快地搜索排序的键,因此可能不会被注意到。

如果我在你的 x 上计时,它不会胜过循环版本,但对于更大的数组,它可能会。

【讨论】:

  • 这很漂亮,谢谢。我在 2600 多个元素的数组上为您的解决方案计时。最初的 for 循环大约需要 500 毫秒。 Cython 优化版本将其降低到 2 毫秒。该解决方案将其降低到 113µs。干得好 Jaime,再次感谢!
【解决方案2】:

这很好用,

A=[0,0,1,1,0,0,-1,0,1,0,0,1,0,-1,0]
B=[]
#initializing column with same number of zeros 
for j in range(len(A)):
    B.append(0)
print A
for i in range(len(A)):
    #retrieve the indices of pair (1 to -1)
    try:
            one_index=A.index(1)
            neg_one_index=A.index(-1)
    except:
            pass 
    one_index=one_index+1
    #replacing the zeros in column B by 1 at correct locations
    while one_index<=neg_one_index:
            B[one_index]=1
            A[one_index-1]=0
            A[one_index]=0
            one_index=one_index+1
print B
#output->[0,0,0,1,1,1,1,0,0,1,1,1,1,1,0] (i.e correct)

【讨论】:

  • 抱歉,这并不比 OP 的尝试快,OP 正在寻找矢量化解决方案。
猜你喜欢
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 2019-08-14
  • 1970-01-01
  • 1970-01-01
  • 2021-03-01
  • 2022-11-30
  • 1970-01-01
相关资源
最近更新 更多