2012-02-23 9 views
2

これは私が今日持っていた以前の質問に基づく知識トピックです。これらは、私が目撃した気味の悪い行動のいくつかの奇妙な不一致です。Numpy Indexing - 奇妙な行動/不一致に関する質問

まず、あなたはこのコードを実行する場合:

A = ones((10,4)) 
view = A[:,1] 
view.fill(7) 
A 

この配列は0からインデックス化され、スライスは、単に同じ行列の図であるとして、すべての7Sへの第二の列を変更します。素晴らしいです、それはまさに私が起こりたいものです。今

、あなたはこの実行する場合:

A = ones((10,4)) 
view = A[:,1:2] 
view.fill(7) 
A 

をそれが最初の例と同様の効果があるでしょう。 a:bはaからb-1までの列を指定するのはなぜですか?これには具体的な理由がありますか?あなたはこの実行している場合ではない1と2

最後に、 - 私の列1、2を与える必要があります3、および3:

A = ones((10,4)) 
view = A[:,(1,2)] 
view.fill(7) 
A 

を一切側はありません私が入力が言うならば、1がいるようですタプルを使用してビューを作成した場合、オリジナルの行列にそれ以上の副作用が正しく反映されないようです。どんな洞察?

+3

非常に良いEdsger Dijkstraの読書:http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF –

答えて

3

なぜa:bがaからb-1までの列を指定するのですか?

これはPythonの規約です。普通のリストについても同じことが言え、range(a, b)は、aまでのb-1までの数字を含むリストを返すが、bではない。このコンベンションの利点は、a:bでスライスすると、abが数字であることは、より複雑なa-b+1の代わりにb-a要素/行/列を返します。

タプルを使用してビューを作成すると、元のマトリックスに何らかの副作用が正しく伝播されないようです。

これは、スライスに基づいてビューを作成することができるという事実によって引き起こされるナンシーの特異体である。タプルベースのスライスはできませんが、これらは効率的に実装できます。あなたは、Pythonのインデックス構文はボンネットの下に何を示しており、次のスニペットで、この動作をシミュレートすることができます

class FakeArray(object): 
    def __getitem__(self, idx): 
     return "You can't change the original FakeArray through me" 
    def __setitem__(self, idx, val): 
     print("We could set elements %r to %r here" % (idx, val)) 

A[1:2] = 'ham'A.__setitem__(slice(1, 2, None), 'ham')の省略形ですながらA.__getitem__(slice(1, 2, None))の省略形です

>>> A = FakeArray() 
>>> A[1:2] 
"You can't change the original FakeArray through me" 
>>> A[1:2] = 'ham' 
We could set elements slice(1, 2, None) to 'ham' 

のでA[1:2]を試してみてください。実際には2つの別個のメソッドが含まれているため、スライスの振る舞いは代入文の一部であるかどうかによって大きく異なる場合があります。 Numpyのケースでは、この違いとslicetupleオブジェクトの間の微妙な相互作用があります。

+1

大会は理にかなっているが、ビューで列とその隣人をつかむために、私はcol:col + 2をする必要があります。 最後の編集は、タプルの問題の点でより意味があります。タプルで必要な列を選択すると便利な場合があるので、これは無効な特質です。 – bjornsen

+0

@bjornsen:トレードオフです。 Pythonが閉じた間隔の規約を使用している場合は、 'i:len(x)'の代わりに 'i:len(x)-1'を最後に' i'をつかわなければなりません。 –

+0

*これはナンシーな独特のものです。*「ndarray」のようなタイプがどのように機能するかを知っているなら、それは本当に特有のものでも微妙なものでもありません。 –

1

半開きの間隔の使用は、NumPyに固有のものではなく、すべてのPythonで使用されます。リストスライシングは同じ方法で動作し、range()関数も同様に機能します。ハーフオープンの間隔で

  1. 閉じられた間隔では難しいだろう、空のスライスと範囲を、表現することができる:

    閉じ間隔でハーフオープンの間隔を使用するいくつかの利点があります。これはしばしば便利です。閉じた間隔の

  2. 半開区間[a, b)の長さを単にb - a + 1より自然思われる、b - aによって与えられる[a, b]

  3. 隣接間隔が発現しやすいです。リストak要素のチャンクで動作するアルゴリズムがあるとします。

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

    あなたはコードに登場する- 1がたくさんあるでしょう、そしてあなたは、2つの間隔がある財産を失う:実装は閉じintervalesと同じようにどのように見えるかとPythonの

    for i in range(0, len(a), k): 
        frobnicate(a[i:i + k]) 
    

    で実装を比較第1の値の右の値が第2の値の左の値と等しい場合に隣接する。

+0

あなたの例は、少し不公平です。 –

+0

@MikeGraham:クローズドインターバル大会はベース1と一緒に行くだろうが、それは、引数についてあまり変化しないことで合意しました。 1.と2.はベース0とベース1とは完全に無関係であり、3ではベース-1に対してはまだ ' - 1'があります。変更されるのは、最後の 'range()'呼び出しだけです。 –

5

なぜそれがあることである:bは、B-1にから列を指定しますか?

これはすべてのPythonの動作方法であり、プログラミングの多くで伝統です。一例として、スライスの長さをx[a:a + n]とし、x[:n]x[n:]xで2つに分けることができます。あなたはそれに慣れており、長期的には、ほとんどのプログラマーがそれを好む。

タプルを使用してビューを作成すると、元のマトリックスに何らかの副作用が正しく伝播されないようです。

A[:, (1, 2)]を実行すると、表示はなく新しい配列が表示されます。 A[:, 1:3]のようなスライスだけでは、配列の帯のために連続したメモリが残っているので、ビューを持つことは理にかなっています。あなたは、イテラブルを使って配列の一部をチェリーピックアップすると((0, 2)を使ったことをよりよく理解するために想像してください)、ビューのようなバーチャルを持つことは非効率的で扱いにくいでしょう。

+1

いいえ、タプルがビューを作成しない理由を説明するために+1します。私はまだOPの混乱を理解することができます。 。あなたが提案するよりも –

+1

ところで、それはさらに複雑だ: '[:、1:3]'必ずしもメモリ内の連続した配列ではありません。両方とも有効な 'A = np.ones((3、3))'とビュー 'A [:, 1:2]'と 'A [1:2、:] 'を考えてみましょう。 –

+0

私が言ったように、メモリはスワスに対して連続しています(必ずしも完全に連続しているわけではありません)。スワスがどこにあるのかを示す算術演算を簡単に行うこともできます。 –