numpy配列のすべての境界に0があるかどうかを確認します[終了]


13

多次元numpy配列がすべての側面で0を持っているかどうかを確認する最も速い方法は何でしょうか。

だから、簡単な2Dの例として、私は:

x = np.random.rand(5, 5)
assert np.sum(x[0:,  0]) == 0
assert np.sum(x[0,  0:]) == 0
assert np.sum(x[0:, -1]) == 0
assert np.sum(x[-1, 0:]) == 0

これは2Dの場合には問題ありませんが、より高い次元での記述は少し面倒であり、効率的で保守しやすくするためにここで使用できる巧妙で厄介なトリックがあるかどうか疑問に思っていました。


8
np.all (x[:, 0] == 0)合計よりも安全ではないでしょうか?合計検定は、すべての数値が正の場合にのみ正しいです。
Demi-Lune


1
@ Demi-Lume理にかなっています。私の場合、すべてが0以上になりますが、コメントは大歓迎です:)
Luca

1
3Dの場合、立方体の面(6つある)またはエッジ(12つある)を意味しますか?
Riccardo Bucco

@RiccardoBuccoうん、6面。しかし、私の問題は、それが3よりも高い次元に行くことができるということです
ルカ

回答:


7

方法は次のとおりです。

assert(all(np.all(np.take(x, index, axis=axis) == 0)
           for axis in range(x.ndim)
           for index in (0, -1)))

np.take 「ファンシー」なインデックス作成と同じことを行います。


1
@Luca:ドキュメントでは明確ではありませんnumpy.takeが、コピーを作成しています。これにより、ビューに基づくコードよりもパフォーマンスが低下する可能性があります。(タイミングを確認する必要があります
-NumPyの

1
@RiccardoBucco:len(x.shape)より簡単にと書くことができますx.ndim
user2357112は

1
@ user2357112supportsMonicaありがとう、修正しました:)
Riccardo Bucco

5
また、リスト内包表記を使用すると、all短絡を防ぐことができます。ブラケットを削除してジェネレータ式を使用するallと、単一のnumpy.all呼び出しが戻るとすぐに戻ることができますFalse
user2357112は11:09にMonica

1
@ user2357112supportsMonica True !!
Riccardo Bucco

5

これは、実際に関心のある配列の部分を調べ、配列全体のサイズのマスクを作成する時間を無駄にしない答えです。Pythonレベルのループがありますが、それは短く、反復は配列のサイズではなく次元数に比例します。

def all_borders_zero(array):
    if not array.ndim:
        raise ValueError("0-dimensional arrays not supported")
    for dim in range(array.ndim):
        view = numpy.moveaxis(array, dim, 0)
        if not (view[0] == 0).all():
            return False
        if not (view[-1] == 0).all():
            return False
    return True

not (view[0] == 0).all()同等ではない状況はありますview[0].any()か?
Paul Panzer

@PaulPanzer:私もうまくいくと思いview[0].any()ます。2つのオプションに含まれるキャスティングとバッファリングの効率への影響について完全には確信がありません。view[0].any()理論的には高速に実装できますが、以前に奇妙な結果を見たことがあり、関連するバッファリングを完全には理解していません。
user2357112は

view[0].view(bool).any()高速なソリューションになると思います。
ポールパンツァー

@PaulPanzer:argmax実際にanyはブール型ビューよりも優れている可能性があります。これは変になってしまいます。
user2357112は

(また、かどうかargmax、またはany、定期的にゼロに等しくないとして負のゼロを取り扱うブールビュー手段を使用して。)
user2357112支持モニカ

2

私はアレイの形状を変更し、それを繰り返しました。残念ながら、私の答えは、少なくとも3次元であり、通常の行列ではエラーになると想定しているため、1次元および2次元の形状配列に対して特別な句を追加する必要があります。さらに、これは遅くなるため、より良い解決策が存在する可能性があります。

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

xx = np.array(
        [
            [
                [0 , 0, 0, 0],
                [0 , 2, 3, 0],
                [0 , 0, 0, 0]
            ],
            [
                [0 , 0, 0, 0],
                [0 , 7, 8, 0],
                [0 , 0, 0, 0]
            ]
        ])

def check_edges(x):

    idx = x.shape
    chunk = np.prod(idx[:-2])
    x = x.reshape((chunk*idx[-2], idx[-1]))
    for block in range(chunk):
        z = x[block*idx[-2]:(block+1)*idx[-2], :]
        if not np.all(z[:, 0] == 0):
            return False
        if not np.all(z[:, -1] == 0):
            return False
        if not np.all(z[0, :] == 0):
            return False
        if not np.all(z[-1, :] == 0):
            return False

    return True

どちらが生成されます

>>> False
>>> True

基本的に私はすべての寸法を積み重ねてから、それらを調べてエッジをチェックします。


これは、配列の間違った部分を調べます。3次元配列の場合、各2次元サブ配列のエッジではなく、配列全体の面を調べます。
user2357112は

ああ、それはもっと理にかなっています。誤解
lwileczek

1

多分省略記号演算子はあなたが探しているものであり、多くの次元で機能します:

import numpy as np

# data
x = np.random.rand(2, 5, 5)
x[..., 0:, 0] = 0
x[..., 0, 0:] = 0
x[..., 0:, -1] = 0
x[..., -1, 0:] = 0

test = np.all(
    [
        np.all(x[..., 0:, 0] == 0),
        np.all(x[..., 0, 0:] == 0),
        np.all(x[..., 0:, -1] == 0),
        np.all(x[..., -1, 0:] == 0),
    ]
)

print(test)

これはすべての顔に色を付けるわけではありません。たとえば、(4、4、4)キューブで試してみます。
ルカ

顔を着色することの意味が
わかり

1

sliceブールマスキングを使用して、作業を完了することができます。

def get_borders(arr):
    s=tuple(slice(1,i-1) for i in a.shape)
    mask = np.ones(arr.shape, dtype=bool)
    mask[s] = False
    return(arr[mask])

この関数は、最初に配列の「コア」をタプルsに整形し、次にTrue境界点のみを示すマスクを作成します。ブールインデックスは、境界点を提供します。

作業例:

a = np.arange(16).reshape((4,4))

print(a)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

borders = get_borders(a)
print(borders)
array([ 0,  1,  2,  3,  4,  7,  8, 11, 12, 13, 14, 15])

次に、np.all(borders==0)必要な情報を提供します。


注:これは1次元配列では機能しませんが、私はそれらをエッジケースと見なしています。問題の2つの点を確認するだけの方が良いでしょう。


これには、境界だけでなく、配列内の要素の総数に比例して時間がかかります。また、1次元配列は無関係なエッジケースではありません。
user2357112は

1
また、np.arange(15)15が含まれていません
user2357112はモニカ・サポート

1次元配列の2つの関係点を確認するだけの方がいいと思いますが、「無関係」は強い表現です。15はタイプミス、良いキャッチです
Lukas Thaler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.