2016-04-13 3 views
83

я наткнулся на некоторый код с линией, аналогичнойЧто означает x [x <2] = 0 в Python?

x[x<2]=0 

играть с вариациями, я до сих пор застрял на том, что делает этот синтаксис.

Примеры:

>>> 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

Не имеет смысла делать это со списком. – dbliss

+12

Это имеет смысл только с массивами NumPy или подобными объектами, которые ведут себя совершенно иначе, чем поведение в ваших экспериментах, или поведение на основе списка, объясняемое в любом ответе. – user2357112

+11

Обратите внимание, что это не работает в Python 3. Типы могут сравниваться только тогда, когда сравнение имеет смысл. В Python 3 этот пример вызывает 'TypeError: unorderable types: list()

ответ

115

Это имеет смысл только с NumPy массивов. Поведение со списками бесполезно и специфично для Python 2 (а не Python 3). Вы можете дважды проверить, действительно ли исходный объект был массивом NumPy (см. Ниже), а не список.

Но в вашем коде здесь x представляет собой простой список.

С

x < 2 

является Ложные 0 т.е., следовательно,

x[x<2] является x[0]

x[0] получает изменилось.

Наоборот, x[x>2] является x[True] или x[1]

Итак, x[1] получает изменилось.

Почему это происходит?

Правила для сравнения являются:

  1. При заказе двух строк или двух числовых типов упорядочение выполняется ожидаемым образом (лексикографическое упорядочение строка, числовое упорядочение для целых чисел).

  2. Когда вы заказываете числовой и нецифровой тип, сначала вводится числовой тип.

  3. При заказе двух несовместимых типов, где ни числовой, они упорядочены по алфавиту их typenames:

Таким образом, мы имеем следующий порядок

числовая < список < строки < tuple

См. Принятый ответ для How does Python compare string and int?.

Если х массивы NumPy, то синтаксис имеет смысл из-за булева массива индексации.В этом случае x < 2 не является булевым; это массив булевых элементов, представляющий, был ли каждый элемент x меньше 2. x[x < 2] = 0 затем выбирает элементы x, которые были меньше 2, и устанавливает эти ячейки в 0. См. Indexing.

>>> 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

Учитывая, что ОП конкретно говорит: «Я натолкнулся на какой-то код, подобный этому ...», я думаю, что ваш ответ, описывающий буферизированное индексирование numpy, очень полезен - возможно, стоит отметить, что если OP прокручивает код, на который они смотрели, они почти наверняка увидим «импорт» для numpy. –

+2

Все еще слишком умный способ сделать это, конечно? (По сравнению с, скажем, '[0, если i <2 else i для i в x]'.) Или это поощряемый стиль в Numpy? –

+6

@TimPederick: Использование списков в NumPy - довольно плохая идея. Это в десятки раз в сотни раз медленнее, он не работает с произвольномерными массивами, проще получить типы элементов, которые были навинчены, и он создает список вместо массива. Индексирование булевых массивов вполне нормально и ожидается в NumPy. – user2357112

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

BOOL просто преобразуется в целое число. Показатель либо 0, либо 1.

+7

Вы можете упомянуть, что 'x' и' 2' [* * упорядочены, но произвольно * "] (https://docs.python.org/2/library/stdtypes.html#comparisons) и что заказ может измениться в разных реализациях Python. –

+0

Yepp. python3: http://ideone.com/1Cw4gb и http://stackoverflow.com/questions/3270680/how-does-python-compare-string-and-int –

+2

Я бы также добавил, что это * умный * способ делать вещи и, на мой взгляд, избегать. Сделайте это явно - тот факт, что ОП должен был задать этот вопрос, подтверждает мою точку зрения. – kratenko

14

Исходный код в вашем вопросе работает только в Python 2. Если x в Python 2 list, сравнение x < y является False если y является int Эгер. Это связано с тем, что нет смысла сравнивать список с целым числом. Однако в Python 2, если операнды не сопоставимы, сравнение основано на CPython на alphabetical ordering of the names of the types; кроме того, все номера приходят сначала в сравнении смешанного типа. Это даже не описано в документации CPython 2, и различные реализации Python 2 могут дать разные результаты. То есть [1, 2, 3, 4, 5] < 2 оценивает False, потому что 2 является числом и, следовательно, «меньше», чем list в CPython. Это смешанное сравнение было в конечном итоге deemed to be too obscure a feature и было удалено в Python 3.0.


Теперь результат < является bool; и bool is a subclass of int:

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

Так в основном вы принимаете элемент 0 или 1 в зависимости от того, сравнение является истинным или ложным.


Если вы попытаетесь код выше в Python 3, вы получите TypeError: unorderable types: list() < int() из-за a change in Python 3.0:

Ordering Comparisons

Python 3.0 has simplified the rules for ordering comparisons:

The ordering comparison operators (< , <= , >= , >) raise a TypeError exception when the operands don’t have a meaningful natural ordering. Thus, expressions like 1 < '' , 0 > None or len <= len are no longer valid, and e.g. None < None raises TypeError instead of returning False . A corollary is that sorting a heterogeneous list no longer makes sense – all the elements must be comparable to each other. Note that this does not apply to the == and != operators: objects of different incomparable types always compare unequal to each other.


Есть много типов данных, которые перегрузки операторы сравнения, чтобы сделать что-то разные (dataframes from pandas, массивы numpy). Если код, который вы использовали, сделал что-то еще, это было потому, что x был не list, но экземпляр какого-либо другого класса с оператором < переопределен, чтобы вернуть значение, которое не является bool; и это значение затем обрабатывалось специально x[] (aka __getitem__/__setitem__)

+6

'+ False' Привет, Perl, эй, JavaScript, как вы делаете? – cat

+0

@cat в Javascript, Perl, он преобразует значение в число. В Python это код операции UNARY_POSITIVE, который вызывает '__pos__' –

+0

@Anti, который имеет тот же самый забавный результат, поэтому мой комментарий: P – cat

9

Это еще одно использование: код гольфа. Code golf - это искусство написания программ, которые решают некоторые проблемы в максимально возможном количестве байтов исходного кода.

return(a,b)[c<d] 

примерно эквивалентно

if c < d: 
    return b 
else: 
    return a 

за исключением того, что оба а и б оцениваются в первом варианте, но не во втором варианте.

c<d оценивается в True или False.
(a, b) является кортежем.
Индексирование на кортеже работает как индексирование по списку: (3,5)[1] == 5.
True равен 1 и False равен 0.

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

или для False:

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

Там хороший список на стеке сети обмена многих неприятных вещей, которые вы можете сделать, чтобы питон для того, чтобы сэкономить несколько байт. https://codegolf.stackexchange.com/questions/54/tips-for-golfing-in-python

Хотя в обычном коде это никогда не должно быть использовано, а в вашем случае это будет означать, что x действует как-то, что можно сравнить с целым числом и как контейнер, который поддерживает нарезку, которая является очень необычным сочетанием. Скорее всего, это код Numpy, как указывали другие.

+6

' Code Golf - это искусство написания программ': ') – cat

+1

Minor nitpick: bool не * cast * to int, это просто * is * one (см. другие ответы) – cat

+0

Хорошо, я пропустил, что True/False являются ints :) –

6

В целом это могло бы означать ничего. Было уже объяснено, что это означает, что x - это list или numpy.ndarray, но в целом это зависит только от того, как реализованы операторы сравнения (<, >, ...), а также как реализуются get/set-item ([...] -syntax) ,

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! 

Потому что:

  • x < value эквивалентно x.__lt__(value)
  • x[value] является (примерно) эквивалентно x.__getitem__(value)
  • x[value] = othervalue (также примерно), что эквивалентно x.__setitem__(value, othervalue).

Это может быть настроен, чтобы сделать что-нибудь вы хотите. Подобно тому, как пример (имитирует бит numpys-логическое индексирование):

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 

Итак, теперь давайте посмотрим, что произойдет, если вы используете его:

>>> 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]) 

Обратите внимание, это только одна возможность. Вы можете реализовать практически все, что хотите.

+0

Я бы сказал, используя ** что-либо ** действительно слишком общий для логически объяснимого поведения, такого как принятый ответ. – PascalVKooten

+0

@PascalvKooten Вы не согласны с «чем угодно» или с обобщенным ответом? Я думаю, что это важный момент, потому что большинство _logical behaviour_ в python только по соглашению. – MSeifert

Смежные вопросы