2016-02-19 9 views
12

がこの問題の解決策を探していましたが、何も見つかりませんでした。numpy配列を2つの値の間に設定すると、高速の

例えば、私は

[ 0, 0, 2, 3, 2, 4, 3, 4, 0, 0, -2, -1, -4, -2, -1, -3, -4, 0, 2, 3, -2, -1, 0] 

のnumpyの配列を持っている私が達成したいことのはここ2と-2の間で言わせて、数字のペアの間の要素を示すために、別の配列を生成しています。だから私はこのような配列を取得したいと思います。

[ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0] 

(2、-2)のペアの間の2または-2は無視されます。簡単なアプローチは、forループを使って各要素を繰り返し処理し、2の最初の出現を識別し、-2を打ち、次の2を再び探し始めるまで、それ以降のすべてを1に設定します。

しかし、私はnumpyの配列に1000以上の要素があるので、このプロセスがより高速であることを望みます。このプロセスは何度も行う必要があります。あなたはこれを解決するためのエレガントな方法を知っていますか?前もって感謝します!

+0

ここで、範囲は[[2、2、-2] 'で始まりますか? –

+2

最初の '2'は、例から明らかです。' 2、3、2、... ' –

+1

ベクトル化された操作を使用するものの作業。まだそれを持っていないが、これをチェックしてください:http://stackoverflow.com/questions/28563711/make-a-numpy-array-monotonic-without-a-python-loop –

答えて

4

かなり問題があります。この記事には、ベクトル化された解決策が掲載されています(うまくいけば、その背後にある論理を説明するのに役立つはずです)。私は、T1T2を開始トリガと停止トリガとして入力配列としてAと仮定しています。

def setones_between_triggers(A,T1,T2):  

    # Get start and stop indices corresponding to rising and falling triggers 
    start = np.where(A==T1)[0] 
    stop = np.where(A==T2)[0] 

    # Take care of boundary conditions for np.searchsorted to work 
    if (stop[-1] < start[-1]) & (start[-1] != A.size-1): 
     stop = np.append(stop,A.size-1) 

    # This is where the magic happens. 
    # Validate (filter out) the triggers based on the set conditions : 
    # 1. See if there are more than one stop indices between two start indices. 
    # If so, use the first one and rejecting all others in that in-between space. 
    # 2. Repeat the same check for start, but use the validated start indices. 

    # First off, take care of out-of-bound cases for proper indexing 
    stop_valid_idx = np.unique(np.searchsorted(stop,start,'right')) 
    stop_valid_idx = stop_valid_idx[stop_valid_idx < stop.size] 

    stop_valid = stop[stop_valid_idx] 
    _,idx = np.unique(np.searchsorted(stop_valid,start,'left'),return_index=True) 
    start_valid = start[idx] 

    # Create shifts array (array filled with zeros, unless triggered by T1 and T2 
    # for which we have +1 and -1 as triggers). 
    shifts = np.zeros(A.size,dtype=int) 
    shifts[start_valid] = 1 
    shifts[stop_valid] = -1 

    # Perform cumm. summation that would almost give us the desired output 
    out = shifts.cumsum() 

    # For a worst case when we have two groups of (T1,T2) adjacent to each other, 
    # set the negative trigger position as 1 as well 
    out[stop_valid] = 1  
    return out 

サンプルが実行

オリジナルサンプルの場合:

In [1589]: A 
Out[1589]: 
array([ 0, 0, 2, 3, 2, 4, 3, 4, 0, 0, -2, -1, -4, -2, -1, -3, -4, 
     0, 2, 3, -2, -1, 0]) 

In [1590]: setones_between_triggers(A,2,-2) 
Out[1590]: array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0]) 

最悪のケース#1(隣接(2,-2)グループ):

In [1595]: A 
Out[1595]: 
array([-2, 2, 0, 2, -2, 2, 2, 2, 4, -2, 0, -2, -2, -4, -2, -1, 2, 
     -4, 0, 2, 3, -2, -2, 0]) 

In [1596]: setones_between_triggers(A,2,-2) 
Out[1596]: 
array([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 
     0], dtype=int32) 

最悪のケース#2(2無し任意-2まで):

In [1603]: A 
Out[1603]: 
array([-2, 2, 0, 2, -2, 2, 2, 2, 4, -2, 0, -2, -2, -4, -2, -1, -2, 
     -4, 0, 2, 3, 5, 6, 0]) 

In [1604]: setones_between_triggers(A,2,-2) 
Out[1604]: 
array([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 
     1], dtype=int32) 
+0

Ahhh。私は 'searchsorted'について全く知らなかった。驚くばかり! –

+0

私は文字通りこれを行うために私自身のufuncを実装しようとしていました:-)。 –

+0

@MadPhysicistええ、searchsortedは最高のツールの一つです:) – Divakar

0

この時点でいくつか試してみましたが、開始/終了マーカーの状態を追跡する必要があるため、チェックとして使用したダムの反復アプローチ:

for _ in xrange(1000): 
    a = np.random.choice(np.arange(-5, 6), 2000) 
    found2 = False 
    l = [] 
    for el in a: 
     if el == 2: 
      found2 = True 
     l.append(1 if found2 else 0) 
     if el == -2: 
      found2 = False 
    l = np.array(l) 
1

本当に遅すぎますか?

def between_vals(x, val1, val2): 
    out = np.zeros(x.shape, dtype = int) 
    in_range = False 
    for i, v in enumerate(x): 
     if v == val1 and not in_range: 
      in_range = True 
     if in_range: 
      out[i] = 1 
     if v == val2 and in_range: 
      in_range = False 
    return out 

私は@Randy Cと同じボートです:私が試したものはこれより速いです。

+0

理想的には、ベクトル化アプローチを使用するとはるかに速く、有利です... – FiniteElement

2

あなたは巨大なデータセットを持っていると仮定して、2つの境界の初期検索のペアを行い、検証のためにこれらのインデックスのfor-loopを使用することを好みます。

def between_pairs(x, b1, b2): 
    # output vector 
    out = np.zeros_like(x) 

    # reversed list of indices for possible rising and trailing edges 
    rise_edges = list(np.argwhere(x==b1)[::-1,0]) 
    trail_edges = list(np.argwhere(x==b2)[::-1,0]) 

    # determine the rising trailing edge pairs 
    rt_pairs = [] 
    t = None 
    # look for the next rising edge after the previous trailing edge 
    while rise_edges: 
     r = rise_edges.pop() 
     if t is not None and r < t: 
      continue 

     # look for the next trailing edge after previous rising edge 
     while trail_edges: 
      t = trail_edges.pop() 
      if t > r: 
       rt_pairs.append((r, t)) 
       break 

    # use the rising, trailing pairs for updating d 
    for rt in rt_pairs: 
     out[rt[0]:rt[1]+1] = 1 
    return out 
# Example 
a = np.array([0, 0, 2, 3, 2, 4, 3, 4, 0, 0, -2, -1, -4, -2, -1, -3, -4, 
     0, 2, 3, -2, -1, 0]) 
d = between_pairs(a , 2, -2) 
print repr(d) 

## -- End pasted text -- 
array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0]) 

私は、次の

In [59]: a = np.random.choice(np.arange(-5, 6), 2000) 

In [60]: %timeit between_vals(a, 2, -2) 
1000 loops, best of 3: 681 µs per loop 

In [61]: %timeit between_pairs(a, 2, -2) 
1000 loops, best of 3: 182 µs per loop 

とはるかに小さいデータセットのために、

In [72]: a = np.random.choice(np.arange(-5, 6), 50) 

In [73]: %timeit between_vals(a, 2, -2) 
10000 loops, best of 3: 17 µs per loop 

In [74]: %timeit between_pairs(a, 2, -2) 
10000 loops, best of 3: 34.7 µs per loop 
を見つけ@CactusWoman

def between_vals(x, val1, val2): 
    out = np.zeros(x.shape, dtype = int) 
    in_range = False 
    for i, v in enumerate(x): 
     if v == val1 and not in_range: 
      in_range = True 
     if in_range: 
      out[i] = 1 
     if v == val2 and in_range: 
      in_range = False 
    return out 

によって与えられた選択肢の回答との速度比較をしました

したがってそれはすべてあなたのデータセットのサイズに依存します。

+0

私たちが見ているデータのサイズを考慮してください。これは単純なforループよりはるかに優れています。ブラケットエッジを使用するアイデアのためにお勧めします。 @FiniteElement。 – FiniteElement

+0

実際、ベクトル化された解が存在するため、この答えは時代遅れです。 –

+0

@Mad Physicist、この後、ベクターソリューションが投稿されました。 – FiniteElement

関連する問題