Python'da x [x <2] = 0 ne anlama geliyor?


85

Satırına benzer bir kodla karşılaştım

x[x<2]=0

Varyasyonlarla uğraşırken, hala bu sözdiziminin ne yaptığına bağlı kaldım.

Örnekler:

>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]

7
bunu bir liste ile yapmak asla mantıklı gelmez.
dbliss

12
Bu yalnızca, deneylerinizdeki davranıştan veya her iki cevapta açıklanan liste tabanlı davranıştan tamamen farklı davranan NumPy dizileri veya benzer nesneler için mantıklıdır.
user2357112 Monica'yı

11
Bu Python 3'te çalışmaz. Türler yalnızca karşılaştırma mantıklı olduğunda karşılaştırılabilir. Python 3'te bu örnek fırlatır TypeError: unorderable types: list() < int().
Morgan Thrapp

2
Çok az bilgi. Dizinin uyuşmuş bir dizi olduğunu söylemeliydim.
lmaooooo

3
Bunun çok fazla olumlu oy almasına şaşırdım (gerçi bu SO formatı için gerçekten iyi bir soru).
PascalVKooten

Yanıtlar:


120

Bu sadece NumPy dizileriyle mantıklıdır . Listelerle ilgili davranış işe yaramaz ve Python 2'ye özgüdür (Python 3'e değil). Orijinal nesnenin gerçekten bir NumPy dizisi olup olmadığını (aşağıya bakınız) ve bir liste olmadığını iki kez kontrol etmek isteyebilirsiniz.

Ancak buradaki kodunuzda, x basit bir listedir.

Dan beri

x < 2

False yani 0, dolayısıyla

x[x<2] dır-dir x[0]

x[0] değişiyor.

Tersine, x[x>2]bir x[True]veyax[1]

Yani x[1]değişiyor.

Bu neden oluyor?

Karşılaştırma kuralları:

  1. İki dizi veya iki sayısal tür sipariş ettiğinizde, sıralama beklenen şekilde yapılır (dizge için sözlüksel sıralama, tam sayılar için sayısal sıralama).

  2. Sayısal ve sayısal olmayan bir tür sipariş ettiğinizde, sayısal tür önce gelir.

  3. İkisinin de sayısal olmadığı iki uyumsuz türü sipariş ettiğinizde, tür adlarının alfabetik sırasına göre sıralanırlar:

Yani aşağıdaki siparişimiz var

sayısal <liste <dize <tuple

Python string ve int'i nasıl karşılaştırır? Sorusunun kabul edilen cevabına bakın. .

Eğer x bir NumPy dizisiyse , sözdizimi boolean dizi indekslemesi nedeniyle daha anlamlıdır . Bu durumda, x < 2hiç de boole değildir; bu her bir elemanı olup temsil Boolean bir dizi var xaz 2. fazla oldu x[x < 2] = 0sonra unsurları seçer xbu 2 den aşağı ve 0 görmek için bu hücreleri harekete Endeksleme .

>>> x = np.array([1., -1., -2., 3])
>>> x < 0
array([False,  True,  True, False], dtype=bool)
>>> x[x < 0] += 20   # All elements < 0 get increased by 20
>>> x
array([  1.,  19.,  18.,   3.]) # Only elements < 0 are affected

11
OP'nin özellikle "Bunun gibi bir kodla karşılaştım ..." dediği göz önüne alındığında, uyuşuk boole endekslemesini açıklayan cevabınızın çok yararlı olduğunu düşünüyorum - OP baktığı kodu yukarı kaydırırsa, bunu belirtmeye değer olabilir. Neredeyse kesinlikle bir importuyuşuk göreceğim .
J Richard Snape

2
Yine de bunu yapmanın aşırı akıllıca bir yolu, kesinlikle? (Karşılaştırıldığında, diyelim [0 if i < 2 else i for i in x].) Yoksa bu Numpy'de teşvik edilen tarz mı?
Tim Pederick

6
@TimPederick: NumPy ile liste anlamalarını kullanmak oldukça kötü bir fikir. Düzinelerce yüzlerce kat daha yavaştır, keyfi boyutlu dizilerle çalışmaz, öğe türlerini bozmak daha kolaydır ve bir dizi yerine bir liste oluşturur. Boolean dizi indekslemesi tamamen normaldir ve NumPy'de beklenir.
user2357112 Monica'yı destekliyor

@TimPederick Performans vuruşuna ek olarak, kodu yazanın da uyuşuk bir dizi kullanmaya devam etmesi muhtemeldir. x[x<2]uyuşmuş bir dizi [0 if i<2 else i for i in x]döndürürken bir liste döndürür. Bunun nedeni x[x<2], bir indeksleme işlemidir (numpy / scipy / pandas'ta verileri maskeleme yeteneği nedeniyle bir dilimleme işlemi olarak anılır), oysa liste kavrama yeni bir nesne tanımıdır. NumPy indekslemesine
Michael Delgado

45
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2

Bool basitçe bir tam sayıya dönüştürülür. Dizin 0 veya 1'dir.


7
Bunu bahsedebilirim xve 2vardır " tutarlı ama keyfi sipariş " ve sipariş farklı Python uygulamalarda değişebileceğini.
Robᵩ

2
Bunun bir şeyleri yapmanın akıllıca bir yolu olduğunu ve bence bundan kaçınılması gerektiğini de ekleyeceğim . Açıkça yapın - OP'nin bu soruyu sorması gerektiği gerçeği benim açımdan desteklemektedir.
kratenko

11
daha fazla ayrıntı ekleyebilir misin, neden x<2 == false?
Iłya Bursov

15
booltam sayıya dönüştürülmez, boolPython'da a bir tam sayıdır
Antti Haapala

2
Sadece ortaya çıkınca başkası için AnttiHaapala açıklamaya @ netleştirmek için bool bir alt sınıfıdır arasında int.
porglezomp

14

Sorunuza orijinal kod ise sadece Python 2. çalışır xbir olan listPython 2'de, karşılaştırma x < yolup Falseolmadığını ybir olduğunu integer. Bunun nedeni, bir listeyi bir tamsayı ile karşılaştırmanın mantıklı olmamasıdır. Ancak Python 2'de, işlenenler karşılaştırılabilir değilse, karşılaştırma CPython'da türlerin adlarının alfabetik sıralanmasına dayanır ; ayrıca karışık tip karşılaştırmalarda tüm sayılar önce gelir . Bu, CPython 2'nin belgelerinde bile belirtilmemiştir ve farklı Python 2 uygulamaları farklı sonuçlar verebilir. Yani [1, 2, 3, 4, 5] < 2olarak değerlendirilirse False, çünkü 2bir dizi ve bir daha nedenle "küçük" olduğunu listCPython içinde. Bu karışık karşılaştırma sonundabir özelliği çok belirsiz olarak kabul edildi ve Python 3.0'da kaldırıldı.


Şimdi, sonucu <a bool; ve boolbir olan alt sınıf arasındaint :

>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6

Yani temelde karşılaştırmanın doğru veya yanlış olmasına bağlı olarak 0 veya 1 öğesini alıyorsunuz.


Yukarıdaki kodu Python 3'te denerseniz, Python 3.0'daki bir değişiklikTypeError: unorderable types: list() < int() nedeniyle alacaksınız :

Sıralama Karşılaştırmaları

Python 3.0, karşılaştırmaları sıralama kurallarını basitleştirdi:

Sipariş karşılaştırma operatörleri ( <, <=, >=, >) bir zam TypeErrorişlenenler anlamlı doğal sipariş olmadığı zamanlarda istisna. Böylece, ifadeler gibi 1 < '', 0 > Noneya da len <= lenartık geçerli ve örneğin None < Noneartırmalar TypeErrordöndürmek yerine False. Sonuç olarak, heterojen bir listeyi sıralamak artık mantıklı değildir - tüm öğeler birbiriyle karşılaştırılabilir olmalıdır. Bunun ==ve !=operatörleri için geçerli olmadığını unutmayın : farklı karşılaştırılamaz türdeki nesneler her zaman birbirleriyle eşit olmayanları karşılaştırır.


Karşılaştırma işleçlerini farklı bir şey yapmak için aşırı yükleyen birçok veri türü vardır (pandalardan gelen veri çerçeveleri, numpy dizileri). Kullandığınız kod başka bir şey yaptıysa, bunun nedeni a değil , başka bir sınıfın örneğidir, operatörün a olmayan bir değer döndürmek için geçersiz kılınması ; ve bu değer daha sonra (aka / ) tarafından özel olarak işlendixlist<boolx[]__getitem____setitem__


6
+FalseMerhaba Perl, hey JavaScript, nasılsınız?
kedi

@cat, Javascript, Perl, değeri sayı olarak dönüştürür. Python'daUNARY_POSITIVE__pos__
Antti Haapala'yı

Sanırım son bölümün __setitem__yerine demek istedin __getitem__. Ayrıca cevabımın cevabınızın bu kısmından ilham aldığını umarım.
MSeifert

Hayır, ben __getitem__de aynı şekilde __setitem__ve__delitem__
Antti Haapala

9

Bunun bir kullanımı daha var: kod golf. Kod golfü, bazı sorunları olabildiğince az kaynak kodu baytında çözen programlar yazma sanatıdır.

return(a,b)[c<d]

kabaca eşdeğerdir

if c < d:
    return b
else:
    return a

hem a hem de b'nin birinci sürümde değerlendirilmesinin dışında, ikinci sürümde değerlendirilmemesi dışında.

c<dTrueveya olarak değerlendirir False.
(a, b)bir gruptur.
Bir demet üzerinde indeksleme, bir listede indeksleme gibi çalışır: (3,5)[1]== 5.
Trueeşittir 1ve Falseeşittir 0.

  1. (a,b)[c<d]
  2. (a,b)[True]
  3. (a,b)[1]
  4. b

veya için False:

  1. (a,b)[c<d]
  2. (a,b)[False]
  3. (a,b)[0]
  4. a

Yığın değişim ağında, birkaç bayt tasarruf etmek için python'a yapabileceğiniz birçok kötü şeyin iyi bir listesi var. /codegolf/54/tips-for-golfing-in-python

Normal kodda bu asla kullanılmamalıdır ve sizin durumunuzda bu, xhem bir tamsayı ile karşılaştırılabilecek bir şey olarak hem de çok sıra dışı bir kombinasyon olan dilimlemeyi destekleyen bir kap olarak hareket ettiği anlamına gelir . Başkalarının da belirttiği gibi, muhtemelen geçersiz koddur.


6
Code Golf is the art of writing programs: ')
kedi

1
Minör nitpick: bool değildir döküm bir int, sadece olan bir (diğer cevaplar bakın)
cat

6

Genel olarak herhangi bir şey ifade edebilir . Zaten olsaydı ne anlama geldiğini açıkladı xbir olduğunu listya numpy.ndarrayama genel olarak sadece karşılaştırma operatörleri (bağlıdır <, >, ...) ve ayrıca get / set maddelik (nasıl [...]-syntax) uygulanmaktadır.

x.__getitem__(x.__lt__(2))      # this is what x[x < 2] means!
x.__setitem__(x.__lt__(2), 0)   # this is what x[x < 2] = 0 means!

Çünkü:

  • x < value eşdeğerdir x.__lt__(value)
  • x[value] (kabaca) eşdeğerdir x.__getitem__(value)
  • x[value] = othervalue(aynı zamanda kabaca) eşdeğerdir x.__setitem__(value, othervalue).

Bu, istediğiniz herhangi bir şeyi yapmak için özelleştirilebilir . Örnek olarak (biraz numpys-boolean indekslemeyi taklit eder):

class Test:
    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        # You could do anything in here. For example create a new list indicating if that 
        # element is less than the other value
        res = [item < other for item in self.value]
        return self.__class__(res)

    def __repr__(self):
        return '{0} ({1})'.format(self.__class__.__name__, self.value)

    def __getitem__(self, item):
        # If you index with an instance of this class use "boolean-indexing"
        if isinstance(item, Test):
            res = self.__class__([i for i, index in zip(self.value, item) if index])
            return res
        # Something else was given just try to use it on the value
        return self.value[item]

    def __setitem__(self, item, value):
        if isinstance(item, Test):
            self.value = [i if not index else value for i, index in zip(self.value, item)]
        else:
            self.value[item] = value

Şimdi onu kullanırsanız ne olacağını görelim:

>>> a = Test([1,2,3])
>>> a
Test ([1, 2, 3])
>>> a < 2  # calls __lt__
Test ([True, False, False])
>>> a[Test([True, False, False])] # calls __getitem__
Test ([1])
>>> a[a < 2] # or short form
Test ([1])

>>> a[a < 2] = 0  # calls __setitem__
>>> a
Test ([0, 2, 3])

Bunun sadece bir olasılık olduğuna dikkat edin. Neredeyse istediğiniz her şeyi uygulamakta özgürsünüz.


Herhangi bir şeyi kullanmanın , kabul edilen cevap gibi mantıksal olarak açıklanabilir davranış için gerçekten çok genel olduğunu söyleyebilirim .
PascalVKooten

@PascalvKooten "Herhangi bir şey" ya da genelleştirilmiş cevaba katılmıyor musunuz? Bence bu önemli bir nokta çünkü python'daki çoğu mantıksal davranış sadece gelenekseldir.
MSeifert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.