numpy 配列内の同一値のシーケンスの長さを見つける (ランレングスエンコーディング)

StackOverflow https://stackoverflow.com/questions/1066758

質問

pylab プログラム (おそらく matlab プログラムでもよい) には、距離を表す数値の配列があります。 d[t] それは 距離 当時の t (そして、私のデータのタイムスパンは len(d) 時間単位)。

私が関心のあるイベントは、距離が特定のしきい値を下回ったときであり、これらのイベントの継続時間を計算したいと考えています。ブール値の配列を取得するのは簡単です b = d<threshold, 、そして問題は、結局のところ、次の True-only 単語の長さのシーケンスを計算することになります。 b. 。しかし、それを効率的に行う方法がわかりません(つまり、numpy プリミティブを使用)、配列を調べて手動で変更検出を行うことにしました (つまり、値が False から True になるとカウンタを初期化し、値が True である限りカウンタを増加させ、値が False に戻るとシーケンスにカウンタを出力します)。しかし、これは非常に遅いです。

numpy 配列内のその種のシーケンスを効率的に検出するにはどうすればよいでしょうか?

以下は私の問題を説明するPythonコードです。4 番目のドットが表示されるまでに非常に長い時間がかかります (表示されない場合は、配列のサイズを増やしてください)

from pylab import *

threshold = 7

print '.'
d = 10*rand(10000000)

print '.'

b = d<threshold

print '.'

durations=[]
for i in xrange(len(b)):
    if b[i] and (i==0 or not b[i-1]):
        counter=1
    if  i>0 and b[i-1] and b[i]:
        counter+=1
    if (b[i-1] and not b[i]) or i==len(b)-1:
        durations.append(counter)

print '.'
役に立ちましたか?

解決

プリミティブをnumpyないが、itertools機能は、多くの場合、非常に高速なので、この1を試してみて(そしてもちろん、この1を含む様々なソリューションのための時間を測定)します。

def runs_of_ones(bits):
  for bit, group in itertools.groupby(bits):
    if bit: yield sum(group)

あなただけもちろん、リスト(runs_of_ones(ビット))を使用することができ、リスト内の値を必要がある場合。多分リストの内包は、まだわずかに速いかもしれません。

def runs_of_ones_list(bits):
  return [sum(g) for b, g in itertools.groupby(bits) if b]

何について "numpyのネイティブ" の可能性、に移動します

def runs_of_ones_array(bits):
  # make sure all runs of ones are well-bounded
  bounded = numpy.hstack(([0], bits, [0]))
  # get 1 at run starts and -1 at run ends
  difs = numpy.diff(bounded)
  run_starts, = numpy.where(difs > 0)
  run_ends, = numpy.where(difs < 0)
  return run_ends - run_starts

アゲイン!リアルなため、あなたの例では、お互いに対するベンチマークのソリューションに必ず

他のヒント

任意の配列(あまりにも文字列、ブール値などで動作する)のための

完全numpyのベクトル化し、一般的なRLEます。

出力ランレングスのタプル、開始位置、および値。

import numpy as np

def rle(inarray):
        """ run length encoding. Partial credit to R rle function. 
            Multi datatype arrays catered for including non Numpy
            returns: tuple (runlengths, startpositions, values) """
        ia = np.asarray(inarray)                  # force numpy
        n = len(ia)
        if n == 0: 
            return (None, None, None)
        else:
            y = np.array(ia[1:] != ia[:-1])     # pairwise unequal (string safe)
            i = np.append(np.where(y), n - 1)   # must include last element posi
            z = np.diff(np.append(-1, i))       # run lengths
            p = np.cumsum(np.append(0, z))[:-1] # positions
            return(z, p, ia[i])

かなり速い(I7):

xx = np.random.randint(0, 5, 1000000)
%timeit yy = rle(xx)
100 loops, best of 3: 18.6 ms per loop

複数のデータタイプ:

rle([True, True, True, False, True, False, False])
Out[8]: 
(array([3, 1, 1, 2]),
 array([0, 3, 4, 5]),
 array([ True, False,  True, False], dtype=bool))

rle(np.array([5, 4, 4, 4, 4, 0, 0]))
Out[9]: (array([1, 4, 2]), array([0, 1, 5]), array([5, 4, 0]))

rle(["hello", "hello", "my", "friend", "okay", "okay", "bye"])
Out[10]: 
(array([2, 1, 1, 2, 1]),
 array([0, 2, 3, 4, 6]),
 array(['hello', 'my', 'friend', 'okay', 'bye'], 
       dtype='|S6'))

上記のアレックスマルテッリと同じ結果になります。

xx = np.random.randint(0, 2, 20)

xx
Out[60]: array([1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1])

am = runs_of_ones_array(xx)

tb = rle(xx)

am
Out[63]: array([4, 5, 2, 5])

tb[0][tb[2] == 1]
Out[64]: array([4, 5, 2, 5])

%timeit runs_of_ones_array(xx)
10000 loops, best of 3: 28.5 µs per loop

%timeit rle(xx)
10000 loops, best of 3: 38.2 µs per loop

アレックス(まだ非常に速い)よりもわずかに遅く、そしてより柔軟ます。

ここのみアレイを用いた溶液である:それはboolsの配列を含む配列を取得し、遷移の長さをカウント

>>> from numpy import array, arange
>>> b = array([0,0,0,1,1,1,0,0,0,1,1,1,1,0,0], dtype=bool)
>>> sw = (b[:-1] ^ b[1:]); print sw
[False False  True False False  True False False  True False False False
  True False]
>>> isw = arange(len(sw))[sw]; print isw
[ 2  5  8 12]
>>> lens = isw[1::2] - isw[::2]; print lens
[3 4]

swは、スイッチがあり、iswはインデックスでそれらを変換します真が含まれています。 ISWの項目は、その後lensでペアワイズを減算されます。

シーケンスが1で開始した場合、それが0の配列の長さをカウントすることを注意してください。これはレンズを計算するために、インデックス内に固定することができます。また、私は長さ1のコーナーケースこのような配列をテストしていません。

<時間> 全てTrue-サブアレイの開始位置と長さを返す

フル機能。

import numpy as np

def count_adjacent_true(arr):
    assert len(arr.shape) == 1
    assert arr.dtype == np.bool
    if arr.size == 0:
        return np.empty(0, dtype=int), np.empty(0, dtype=int)
    sw = np.insert(arr[1:] ^ arr[:-1], [0, arr.shape[0]-1], values=True)
    swi = np.arange(sw.shape[0])[sw]
    offset = 0 if arr[0] else 1
    lengths = swi[offset+1::2] - swi[offset:-1:2]
    return swi[offset:-1:2], lengths

異なるブール1D-アレイの試験済み

(空の配列、単一/複数の要素、偶数/奇数の長さは、唯一True / False要素と; True / Falseで開始しました)。

念のために誰もが好奇心である(そしてあなたが合格にMATLABを述べたので)、ここではMATLABでそれを解決する一つの方法です。

threshold = 7;
d = 10*rand(1,100000);  % Sample data
b = diff([false (d < threshold) false]);
durations = find(b == -1)-find(b == 1);

私は、Pythonとあまりにも慣れていないんだけど、多分これはあなたにいくつかのアイデアを与えるのを助けることができます。 =)

durations = []
counter   = 0

for bool in b:
    if bool:
        counter += 1
    elif counter > 0:
        durations.append(counter)
        counter = 0

if counter > 0:
    durations.append(counter)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top