2016-12-17 3 views
3

私はn×nの配列を持ち、アウトライン値を受け取りたいです。例えば、すべてのアレイエッジを取得する方法は?

[4,5,6,7]

[2、2,6、3]

[4、4,9、4 ]

[8,1,6,1]

このことから

、私はこの

[4,5,6,7,3,4,1,6,1,8,4,2] 

(ここで、太字を参照)

になるだろうので、本質的に、のエッジの周りに起こったすべての値の1次元配列を得るための最も効率的なな方法は何ですか2Dアレイ? ループで手動で行うのではなく、これに役立つnumPy関数があると仮定していますか?あなたのリストを想定し

+1

は、注文事項をしていますか? –

+0

いいえ、運良く私の場合はありません:) –

答えて

2
In [1]: arr=np.arange(16).reshape(4,4) 
In [2]: arr 
Out[2]: 
array([[ 0, 1, 2, 3], 
     [ 4, 5, 6, 7], 
     [ 8, 9, 10, 11], 
     [12, 13, 14, 15]]) 

これを行うための比較的単純な方法 - 時計回りの順では、次のとおりです。意味で

In [5]: alist=[arr[0,:-1], arr[:-1,-1], arr[-1,::-1], arr[-2:0:-1,0]] 
In [6]: alist 
Out[6]: [array([0, 1, 2]), array([ 3, 7, 11]), array([15, 14, 13, 12]), array([8, 4])] 
In [7]: np.concatenate(alist) 
Out[7]: array([ 0, 1, 2, 3, 7, 11, 15, 14, 13, 12, 8, 4]) 

それは私が4つのスライスを構築する必要があり、その中で、ループのです。しかし、4がnに比べて小さい場合は、それは小さな値段です。それはあるレベルで連結しなければなりません。

注文が問題でない場合は、スライスを簡略化することができます(逆順を忘れるなど)。

alist=[arr[0,:], arr[1:,-1], arr[-1,:-1], arr[1:-1,0]] 

私は順序を気にしませんでした、または角をカウントダブル私が使用することができた場合:

np.array([arr[[0,n],:], arr[:,[0,n]].T]).ravel() 

をあなたが使用することもでき

In [18]: np.concatenate((arr[[0,n],:].ravel(), arr[1:-1,[0,n]].ravel())) 
Out[18]: array([ 0, 1, 2, 3, 12, 13, 14, 15, 4, 7, 8, 11]) 
1

は、次の形式になります。

l = [ 
    [4, 5, 6, 7], 
    [2, 2, 6, 3], 
    [4, 4, 9, 4], 
    [8, 1, 6, 1] 
    ] 

あなたはリストの内包表記を使用して、あなたはこのクイックワンライナーで欲しいものを達成することができます

out = list(l[0]) + # [4, 5, 6, 7] 
     list([i[-1] for i in l[1:-1]]) + # [3, 4] 
     list(reversed(l[-1])) + # [1, 6, 1, 8] 
     list(reversed([i[0] for i in l[1:-1]])) # [4, 2] 

print(out) # gives [4, 5, 6, 7, 3, 4, 1, 6, 1, 8, 4, 2] 

これはあなたが持っているかどうか動作します単純なpythonリストまたはnumpy配列。

効率に関しては、0120060x20000マトリックスで%timeitを使用すると、このメソッドは16.4msとなりました。

このタスクを実行するためのより効率的な方法があると確信していますが、これは1ライナーのソリューションには非常に適していると思います。

1

重複の角をなくしitertools.groupbylist comprehension以下の例のように:

a = [ 
     [4,5,6,7], 
     [2,2,6,3], 
     [4,4,9,4], 
     [8,1,6,1], 
    ] 

from itertools import groupby 

def edges(a = list): 
    final, i = [], [] 
    for k, _ in groupby(a[1:-1], lambda x : [x[0], x[-1]]): 
     i += k 

    return a[0] + [k for n in range(1,len(i), 2) for k in i[n:n+1]] + a[-1][::-1] + [k for n in range(0, len(i), 2) for k in i[n:n+1] ][::-1] 

出力:

print(edges(a)) 
>>> [4, 5, 6, 7, 3, 4, 1, 6, 1, 8, 4, 2] 

timeitを使用してテスト:

a = [ 
     [4,5,6,7], 
     [2,2,6,3], 
     [4,4,9,4], 
     [8,1,6,1], 
    ] 

from itertools import groupby 

def edges(): 
    final, i = [], [] 
    for k, _ in groupby(a[1:-1], lambda x : [x[0], x[-1]]): 
     i += k 

    return a[0] + [k for n in range(1,len(i), 2) for k in i[n:n+1]] + a[-1][::-1] + [k for n in range(0, len(i), 2) for k in i[n:n+1] ][::-1] 


if __name__ == '__main__': 
    import timeit 
    print(timeit.timeit("edges()", setup="from __main__ import edges", number = 100)) 

ベストタイムはあった(それはマスクを作成していますので、それは他の回答で述べた代替案よりも、おそらく遅くなります0.0006266489999688929

1

ました私のユースケースの場合)、あなたのケースで使用することができます:

def mask_borders(arr, num=1): 
    mask = np.zeros(arr.shape, bool) 
    for dim in range(arr.ndim): 
     mask[tuple(slice(0, num) if idx == dim else slice(None) for idx in range(arr.ndim))] = True 
     mask[tuple(slice(-num, None) if idx == dim else slice(None) for idx in range(arr.ndim))] = True 
    return mask 

としては、すでに(True)これは国境がマスクされているmaskを作成し、返し言った:

>>> mask_borders(np.ones((5,5))) 
array([[ True, True, True, True, True], 
     [ True, False, False, False, True], 
     [ True, False, False, False, True], 
     [ True, False, False, False, True], 
     [ True, True, True, True, True]], dtype=bool) 

>>> # Besides supporting arbitary dimensional input it can mask multiple border rows/cols 
>>> mask_borders(np.ones((5,5)), 2) 
array([[ True, True, True, True, True], 
     [ True, True, True, True, True], 
     [ True, True, False, True, True], 
     [ True, True, True, True, True], 
     [ True, True, True, True, True]], dtype=bool) 

「国境」の値を取得するには、これはあなたの配列にboolean indexingを適用する必要があります

>>> arr = np.array([[4,5,6,7], [2,2,6,3], [4,4,9,4], [8,1,6,1]]) 

>>> arr[mask_borders(arr)] 
array([4, 5, 6, 7, 2, 3, 4, 4, 8, 1, 6, 1]) 
+0

ニース!私はベクトル化された方法で同様の考えを持っていました。 – Divakar

+0

@Divakar形状がすべての次元で同じでない場合、私はそれをベクトル化するのに問題がありました。私はあなたの答えを間違いなくチェックします! – MSeifert

+0

「a」の形が '(m、n)'なら、私の解は次のように変わります: 'r1 = np.minimum(np.arange(n)[:: - 1]、np.arange() n)) 'と' 'm'を使った' r2'と '' np.minimum(r2 [:、None]、r1)のブール値マスクについても同様です。 – Divakar

2

このようなエッジピクセル/要素のマスクを作成し、次にそれらを取得するために単に配列にインデックスを付けるベクトル化された方法が1つあります。

def border_elems(a, W): # Input array : a, Edgewidth : W 
    n = a.shape[0] 
    r = np.minimum(np.arange(n)[::-1], np.arange(n)) 
    return a[np.minimum(r[:,None],r)<W] 

また、これはパフォーマンスのためのものではありませんが、エッジ幅を変更したり、そのようなエッジ要素のマスクを作成したりする場合にはさらに重要です。マスクは最後のステップで作成されたnp.minimum(r[:,None],r)<Wとなります。

サンプル実行 -

In [89]: a 
Out[89]: 
array([[49, 49, 12, 90, 42], 
     [91, 58, 92, 16, 78], 
     [97, 19, 58, 84, 84], 
     [86, 31, 80, 78, 69], 
     [29, 95, 38, 51, 92]]) 

In [90]: border_elems(a,1) 
Out[90]: array([49, 49, 12, 90, 42, 91, 78, 97, 84, 86, 69, 29, 95, 38, 51, 92]) 

In [91]: border_elems(a,2) # Note this will select all but the center one : 58 
Out[91]: 
array([49, 49, 12, 90, 42, 91, 58, 92, 16, 78, 97, 19, 84, 84, 86, 31, 80, 
     78, 69, 29, 95, 38, 51, 92]) 
関連する問題