'and'(ブール値)と '&'(ビット単位)-リストとnumpy配列の動作に違いがあるのはなぜですか?


142

リストとNumPy配列のブール演算とビット演算の動作の違いを説明するものは何ですか?

次の例に示すように、Python で&vs andを適切に使用する方法について混乱しています。

mylist1 = [True,  True,  True, False,  True]
mylist2 = [False, True, False,  True, False]

>>> len(mylist1) == len(mylist2)
True

# ---- Example 1 ----
>>> mylist1 and mylist2
[False, True, False, True, False]
# I would have expected [False, True, False, False, False]

# ---- Example 2 ----
>>> mylist1 & mylist2
TypeError: unsupported operand type(s) for &: 'list' and 'list'
# Why not just like example 1?

>>> import numpy as np

# ---- Example 3 ----
>>> np.array(mylist1) and np.array(mylist2)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
# Why not just like Example 4?

# ---- Example 4 ----
>>> np.array(mylist1) & np.array(mylist2)
array([False,  True, False, False, False], dtype=bool)
# This is the output I was expecting!

この回答この答えは私がそれを理解する助けandブーリアン演算であるが、&ビット演算です。

概念をよりよく理解するためにビット演算について読みましたが、上記の4つの例を理解するためにその情報を使用するのに苦労しています。

例4では、希望する出力が得られたので問題ありませんが、andvs をいつ、どのように、なぜ使用する必要があるかについてはまだ混乱してい&ます。リストとNumPy配列がこれらの演算子で異なる動作をするのはなぜですか?

ブール演算とビット演算の違いを理解して、リストとNumPy配列を異なる方法で処理する理由を説明できる人はいますか?


2
Numpyには、混乱を避けるためにnp.bitwise_and()and np.logical_and()と友達がいます。
ディートリッヒ

1
例1では、delnanによって指摘された2番目のリストが返されるためmylist1 and mylist2、と同じ結果を出力しませんmylist2 and mylist1
user2015487 2016

回答:


113

and両方の式が論理的であるかどうかをテストTrueしながら&(と共に使用した場合True/ False両方がある場合、値)テストTrue

Pythonでは、空の組み込みオブジェクトは通常論理的に扱われますが、空でない組み込みオブジェクトは論理的に扱われFalseますTrue。これにより、リストが空の場合は何かを行い、リストが空でない場合は何かを行いたいという一般的な使用例が容易になります。これは、リスト[False]が論理的には次のことを意味することに注意してくださいTrue

>>> if [False]:
...    print 'True'
...
True

したがって、例1では、最初のリストは空ではないので論理的Trueになので、の真理値はand2番目のリストの真理値と同じです。(この場合、2番目のリストは空ではないため、論理的Trueにはですが、それを特定するには、不必要な計算ステップが必要になります。)

たとえば2のリストは、任意の異なる要素を含むことができるため、ビット単位で意味のある方法で組み合わせることができません。ビットごとに組み合わせることができるものには、真と偽、整数があります。

対照的に、NumPyオブジェクトはベクトル化された計算をサポートします。つまり、複数のデータに対して同じ操作を実行できます。

例3は、NumPy配列(長さが1より大きい)には真理値がないため、失敗します。これにより、ベクトルベースのロジックの混乱を防ぐことができます。

例4は、単にベクトル化されたビットand演算です。

ボトムライン

  • 配列を処理しておらず、整数の数学操作を実行していない場合は、おそらく必要ですand

  • 組み合わせたい真理値のベクトルがある場合は、で使用numpy&ます。


26

list

まず、すべてが続く非常に重要なポイントです(私はそう思います)。

通常のPythonでは、list特別なものではありません(ほとんどが歴史的な事故である、構築のためのかわいい構文があることを除いて)。リスト[3,2,6]が作成されると、それはすべての意図と目的のために、単なるnumber 3、set {3,7}、またはfunction などの通常のPythonオブジェクトlambda x: x+5です。

(はい、それはその要素の変更をサポートし、反復やその他多くのものをサポートしますが、それはタイプが何であるかです:それはいくつかの操作をサポートしますが、他のいくつかをサポートしません。intは累乗をサポートしますが、それはしませんラムダは呼び出しをサポートしますが、それはそれを非常に特別にしません-それは結局ラムダが何のためにあるのかです:)。

and

andは演算子ではありません(「演算子」と呼ぶこともできますが、演算子の「for」と呼ぶこともできます)。Pythonの演算子は、あるタイプのオブジェクトに対して呼び出される(実装される)メソッドであり、通常はそのタイプの一部として記述されます。メソッドがそのオペランドの一部の評価を保持する方法はありませんが、それを実行andできます(また、実行する必要があります)。

その結果、andオーバーロードforできないように、オーバーロードできません。それは完全に一般的であり、指定されたプロトコルを介して通信します。あなたができることはプロトコルのあなたの部分をカスタマイズすることですが、それはあなたがand完全に振る舞いを変えることができることを意味しません。プロトコルは次のとおりです。

Pythonが「aとb」を解釈することを想像してみてください(これは文字通りこの方法では発生しませんが、理解に役立ちます)。「and」に関しては、評価したばかりのオブジェクト(a)を見て、それを尋ねます。本当ですか?(NOT:あなたTrueですか?)あなたがaのクラスの作成者である場合は、この回答をカスタマイズできます。場合aの答えは「いいえ」、 and(完全にBスキップし、それがすべてで評価し、されていない)と言う:a私の結果である(NOT:Falseが私の結果ではありません)。

a答えない場合は、andそれを尋ねます:あなたの長さは?(再度、これをaのクラスの作成者としてカスタマイズできます)。a0と答えた場合and、上記と同じ-偽(NOT偽)と見なし、bをスキップしaて結果を返します。

a2番目の質問(「あなたの長さ」)に0以外の何かに答える場合、またはまったく答えない場合、または最初の質問に「はい」と答える(「あなたは本当ですか」)、andbを評価し、言う:b私の結果です。それはないことに注意してくださいしないで尋ねるb質問を。

これをすべて言い換えるもう1つの方法は、aが1回だけ評価さa and bれることb if a else aを除いて、とほぼ同じです。

次に、ペンと紙を使って数分間座って、{a、b}が{True、False}のサブセットである場合、ブール演算子と同じように機能することを理解してください。しかし、私はあなたがそれがはるかに一般的であることがあなたが確信していることを望みます。

これら2つを組み合わせる

ここで、例1を理解してandいただければ幸いです。mylist1が数値、リスト、ラムダ、またはArgmhblクラスのオブジェクトであるかどうかは問題ではありません。プロトコルの質問に対するmylist1の回答を気にするだけです。そしてもちろん、mylist1は長さに関する質問に5と答え、mylist2を返します。以上です。mylist1とmylist2の要素とは何の関係もありません-それらはどこにも画像を入力しません。

2番目の例:&オンlist

一方、&は、他のオペレーターと同様に+、例えばです。そのクラスで特別なメソッドを定義することにより、型に対して定義できます。intこれはビット単位の "and"として定義され、boolは論理的な "and"として定義されますが、これは1つのオプションにすぎません。たとえば、セットやdictキービューなどの他のオブジェクトは、それをセットの交差として定義します。listおそらくそれを定義していません。おそらくギドがそれを定義する明白な方法を考えていなかったからでしょう。

派手な

もう一方の脚:-D、numpy配列特別です、または少なくともそれらはしようとしています。もちろん、numpy.arrayは単なるクラスであり、決してオーバーライドすることはできないandため、次の最善のことを行います。「本当ですか?」真実の見方はあなたのモデルに適合しません。」(ValueErrorメッセージは話さないことに注意してくださいand-numpy.arrayはが質問しているのかわからないので、それは真実について話しています。)

については&、まったく別の話です。numpy.arrayは必要に応じてそれを定義でき&、他の演算子と一貫して定義します。だから、あなたは最終的にあなたが望むものを手に入れます。

HTH、


23

新しい言語機能を導入したり、ショートサーキットを犠牲にしたりせずにこれを行うには満足のいく方法がないためand、ショートサーキットブール演算子(、or)はオーバーライドできません。ご存じかもしれませんが、最初のオペランドの真理値を評価し、その値に応じて、2番目の引数を評価して返すか、2番目の引数を評価せずに最初の引数を返します。

something_true and x -> x
something_false and x -> something_false
something_true or x -> something_true
something_false or x -> x

実際のオペランド(の評価結果)が返され、その真理値は返されないことに注意してください。

それらの動作をカスタマイズする唯一の方法はオーバーライドすることです__nonzero____bool__Python 3では名前が変更されています)。そのため、返されるオペランドに影響を与えることができますが、別のオペランドを返すことはできません。リスト(およびその他のコレクション)は、何かが含まれている場合は「真実」であり、空の場合は「偽」であると定義されています。

NumPy配列はその概念を拒否します。それらが目指すユースケースでは、2つの異なる真実の概念が一般的です:(1)要素が真かどうか、(2)すべての要素が真かどうか。これら2つは完全に(そして黙って)互換性がなく、どちらも明らかに正確でも一般的でもないため、NumPyは推測を拒否し、.any()またはを明示的に使用するように要求します.all()

&また、|(そしてnot、ところで)短絡しないため、完全にオーバーライドできます。オーバーライドされると、何でも返すことができ、NumPyは、他のスカラー操作と同様に、要素単位の操作を行うためにそれをうまく利用します。一方、リストは要素全体に操作をブロードキャストしません。同じようにmylist1 - mylist2平均何もしていませんmylist1 + mylist2完全に異なる手段の何かを、何もありません&リストのオペレータが。


3
これが生成できる特に興味深い例の1つは、に[False] or [True]評価され[False]、に[False] and [True]評価され[True]ます。
Rob Watts

16

例1:

これがand演算子の働きです。

xおよびy => xがfalseの場合はx、それ以外の場合はy

したがって、言い換えると、mylist1isではないのでFalse、式の結果はになりますmylist2。(空のリストのみが評価されFalseます。)

例2:

&オペレータは、あなたが言及として、ビット単位のためであると。ビットごとの演算は数値に対してのみ機能します。結果&bが両方とも1であるビットに1Sからなる数であり、AおよびB。例えば:

>>> 3 & 1
1

バイナリリテラル(上記と同じ番号)を使用すると、何が起こっているのかを簡単に確認できます。

>>> 0b0011 & 0b0001
0b0001

ビット演算は、概念的にはブール(真理)演算と似ていますが、ビットに対してのみ機能します。

だから、私の車についてのいくつかの声明を与えられて

  1. 私の車は赤い
  2. 私の車には車輪があります

これら2つのステートメントの論理的な「and」は次のとおりです。

(私の車は赤ですか?)および(車に車輪がありますか?)=> false値の論理true

どちらも、少なくとも私の車には当てはまります。したがって、ステートメント全体の値は論理的に真です。

これら2つのステートメントのビット単位の「and」は、もう少し曖昧です。

(「私の車は赤です」というステートメントの数値)&(「私の車は車輪があります」というステートメントの数値)=>数値

pythonがステートメントを数値に変換する方法を知っている場合は、それを実行して2つの値のビット単位のANDを計算します。これは、&と交換可能であると思われるかもしれませんandが、上記の例と同様に、それらは異なるものです。また、変換できないオブジェクトの場合は、を取得しTypeErrorます。

例3および4:

Numpy は配列の算術演算を実装します:

ndarrayの算術演算と比較演算は要素ごとの演算として定義され、一般に結果としてndarrayオブジェクトを生成します。

ただし、配列演算子は論理演算を実装していません。Pythonでは論理演算子をオーバーロードできないためです。そのため、例3は機能しませんが、例4は機能します。

したがって、あなたのandvsの&質問に答えるには、を使用してくださいand

ビット演算は、数値の構造(どのビットが設定され、どのビットが設定されていないか)を調べるために使用されます。この種の情報は、主に低レベルのオペレーティングシステムインターフェイス(たとえば、UNIXの許可ビット)で使用されます。ほとんどのpythonプログラムはそれを知る必要はありません。

論理演算は(andornot)、しかし、すべての時間に使用されます。


14
  1. Pythonでの表現X and Yを返すYことを考えると、bool(X) == TrueまたはのいずれかXまたはYFalseに評価し、例えば:

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
  2. ビット演算子はリストには定義されていません。しかし、それは整数に対して定義されています-数値のバイナリ表現で動作します。16(01000)と31(11111)を検討してください。

    16 & 31
    >>> 16
  3. NumPyは精神的なものではありません。たとえば、論理式で[False, False]等しいと見なすべきかどうかはわかりませんTrue。「任意の空のコレクションを:このでは、ある標準のPythonの挙動、上書きされますlen(collection) == 0ですFalse」。

  4. おそらく、NumPyの配列の&演算子の予想される動作です。


Falseおよび20はFalseを返します
Rahul 2017

4

最初の例では、djangoのドキュメント
に基づいています。常に2番目のリストを返します。実際、空でないリストはPythonのTrue値として表示されるため、Pythonは「最後の」True値を返すため、2番目のリスト

In [74]: mylist1 = [False]
In [75]: mylist2 = [False, True, False,  True, False]
In [76]: mylist1 and mylist2
Out[76]: [False, True, False, True, False]
In [77]: mylist2 and mylist1
Out[77]: [False]

4

Pythonリストを使用した操作は、リストを操作しますlist1 and list2かどうかをチェックしますlist1空で、返すlist1ことがあれば、そしてlist2それがない場合。list1 + list2がに追加さlist2れるlist1ため、len(list1) + len(list2)要素を含む新しいリストを取得します。

要素ごとの操作は要素をループしないとサポートされない&ため、などの要素ごとに適用した場合にのみ意味のある演算子はを発生さTypeErrorせます。

Numpy配列は要素ごとの演算をサポートしていますarray1 & array2ビット単位の計算または各対応する要素のためであろうarray1array2。およびのarray1 + array2対応する各要素の合計を計算します。array1array2

これは動作しませんandor

array1 and array2 基本的に、次のコードの省略形です。

if bool(array1):
    return array2
else:
    return array1

このためには、の適切な定義が必要ですbool(array1)。Pythonリストで使用されるようなグローバル操作のbool(list) == True場合、定義はif listが空でない場合と空の場合ですFalse。numpyの要素ごとの演算では、要素がに評価されるTrueか、すべての要素がに評価されるかを確認するかどうかにあいまいさがいくつかありTrueます。どちらも間違いなく正しいので、numpyは推測せず、(間接的に)配列に対してValueErrorwhen bool()が呼び出されます。


0

良い質問。論理的なandビット&演算子について、例1と4(または1と4と言うべきか)についての観察と同様に、演算子について経験しましたsum。numpy sumとpyのsum動作も異なります。例えば:

「マット」が次のような5x5の2次元配列であるとします。

array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20],
       [21, 22, 23, 24, 25]])

次に、numpy.sum(mat)は、行列全体の合計を与えます。一方、sum(mat)などのPythonの組み込み合計は、軸に沿ってのみ合計されます。下記参照:

np.sum(mat)  ## --> gives 325
sum(mat)     ## --> gives array([55, 60, 65, 70, 75])
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.