|
Булевы операции и функции, значения inf и nan
Продолжаем
знакомство с операциями над массивами и посмотрим как они ведут себя с булевыми
операциями. Предположим, имеется одномерный массив:
a = np.array([1, 2, 3, 10, 20, 30])
и мы хотим
определить все числа, которые больше 5. Мы с вами уже выполняли такую операцию
и для этого сначала формировали булевый массив, а затем, выделяли элементы, у
индексов которых стоит значение True:
На выходе
получим массив из трех элементов, которым соответствуют позиции True:
array([10, 20,
30])
Видите, как это
может быть удобно: выделить нужные элементы, не используя ни одного оператора
цикла языка Python. А, значит,
такая конструкция будет работать достаточно быстро (так как внутри реализована
на языках Си и Fortran).
Конечно, эту
запись можно еще упростить и записать в виде:
Результат будет
тем же. По аналогии работают и другие булевы операторы:
Оператор
|
Описание
|
a == b
|
Проверка
на равенство
|
a != b
|
Проверка
на неравенство
|
a > b
|
Проверка,
что a больше b
|
a < b
|
Проверка,
что a меньше b
|
a >= b
|
Проверка,
что a больше или
равно b
|
a <= b
|
Проверка,
что a меньше или
равно b
|
Здесь в качестве
операндов a и b могут выступать
как числа, так и массивы NumPy. Например, добавим еще один массив:
b = np.array([1, 2, 3, 4, 5, 6])
Тогда можно
использовать сравнения:
a == b # array([ True, True, True, False, False, False])
a >= b # array([ True, True, True, True, True, True])
a <= b # array([ True, True, True, False, False, False])
a != b # array([False, False, False, True, True, True])
Функции greater, less, equal
Вместо записи
операторов в NumPy имеются функции
сравнения: greater(), less() и equal(). Их названия говорят сами за себя:
-
greater(a, b) – выполняет
сравнение a > b;
-
less(a, b) – выполняет
сравнение a < b;
-
equal(a, b) – выполняет
сравнение a == b.
Использование их
вполне очевидно, например:
np.greater(a, b) # array([False, False, True, False])
np.less(a, b) # array([ True, True, False, False])
np.equal(a, b) # array([False, False, False, True])
Но, чаще всего
на практике вместо них записывают булевы операторы: >, <, ==.
Функции array_equal, all и any
Но использовать
результат сравнения в условных операторах нельзя. Следующая строчка приведет к
ошибке:
if(a == b): print("a == b")
Для такого
сравнения массивов необходимо получать только одно значение True или False, а не объект array. Для этого в
пакете NumPy существуют
специальная функция np.array_equal(), которую можно применить так:
if np.array_equal(a ,b):
print("a == b")
Это условие
сработает, если оба массива a и b содержат
одинаковые значения элементов и равны по длине.
Если нам нужно
определить, что хотя бы один элемент массива удовлетворяет указанному условию,
то можно воспользоваться функцией any(), например:
# для массива a = array([ 1, 2, 3, 10, 20, 30])
np.any(a > 5) # True
np.any(a == 5) # False
np.any(a == b) # True
Если же нужно
узнать, все ли элемента массива удовлетворяют условию, то используется функция all():
np.all(a > 5) # False
np.all(a > 0) # True
np.all(a == b) # False
Значения -inf, inf и nan
Пакет NumPy реализован
максимально дружественным способом и там, где можно избежать ошибок и
продолжить вычисления, он это делает. Например, давайте разделим все значения
массива a на 0. Из
математики мы знаем, что на 0 делить нельзя, но, тем не менее, критической
ошибки не возникнет, а все элементы примут значение inf:
с результатом:
<input>:1: RuntimeWarning: divide
by zero encountered in true_divide
array([[inf, inf],
[inf, inf],
[inf,
inf]])
Здесь NumPy нас лишь
предупредил, что встретилось деление на ноль, но расчеты были завершены и все
элементы равны inf.
Что это за
значение inf? Это сокращение
от английского слова infinity – бесконечность.
Действительно, при делении на 0 получаем бесконечность. Именно это и указано в
значениях элементов массива. Благодаря использованию этого специального
значения, NumPy избежал ошибки
деления на 0. Причем, inf – это полноценный элемент массивов. Его
можно непосредственно задать при определении:
b = np.array([1, 2, np.inf])
И, далее, он
может участвовать в вычислениях. Например, умножим b на ноль и
посмотрим, что получится:
b*0 # array([ 0., 0., nan])
Последний
элемент превратился в nan. Это еще одно сокращение от
английского:
not a
number (не число)
То есть,
значение nan указывает, что
в результате арифметической операции третий элемент перестал быть каким-либо
числовым значением. Причем, это определение оказывается «прилипчивым».
Например, сложим все элементы массива:
получим:
То есть, любые
арифметические операции с nan приводят к nan.
Функции isnan и isinf
Так как элементы
inf и nan не относятся к
числам, то для их идентификации, проверки, что текущий элемент массива
принимает одно из этих значений, существуют функции isnan() и isinf(). Они
возвращают True, если элемент
равен nan и inf и Flase – в противном
случае. Посмотрим как можно их использовать в программе. Пусть имеется массив:
b = np.array([1, 2, np.nan, np.inf, -np.inf])
к которому
применим эти две функции:
np.isinf(b) # array([False, False, False, True, True])
np.isnan(b) # array([False, False, True, False, False])
На выходе имеем
массив с булевыми значениями и True стоит на местах inf (при вызове isinf) и nan (при вызове isnan). Далее,
используя этот массив можно исключить нечисловые элементы из массива, например,
так:
indx = np.isinf(b)
b[~indx] # array([ 1., 2., nan])
Здесь
исключаются все элементы inf, а операция ~indx инвертирует
булевы значения. Аналогично можно отфильтровать значения nan.
Дополнительные функции: isfinite, iscomplex, isreal
Часто, при
работе с массивами требуется определить: являются ли его элементы конечными
числами. Для этого используется еще одна функция – isfinit():
# для массива b = np.array([1, 2, np.nan, np.inf, -np.inf])
np.isfinite(b) # array([ True, True, False, False, False])
Соответственно,
все не числовые элементы помечены как False, а числовые –
как True.
Далее, мы можем
уточнять тип числа: комплексное или действительное, с помощью функций iscompex() и isreal(). Например:
a = np.array([1+2j, 3-4j, 5]) # array([1.+2.j, 3.-4.j, 5.+0.j])
np.iscomplex(a) # array([ True, True, False])
Обратите
внимание, несмотря на то, что тип данных у всех элементов массива complex128 (посмотреть
можно через a.dtype), последний
элемент функция iscomplex() пометила как False, так как мнимая
часть равна нулю.
Аналогично работает функция isreal():
np.isreal(a) # array([False, False, True])
Только теперь True помечены
действительные числа, а False – все остальные.
Но,
применяя эту функцию к массиву b:
np.isreal(b) # array([ True, True, True, True, True])
получим все
значения True. То есть,
специальные значения nan и inf отмечаются как
действительные.
Функции logical_and, logical_or, logical_not и logical_xor
В NumPy можно выполнять
стандартные булевы операции И, ИЛИ, НЕ, исключающее ИЛИ, применительно к данным
массивов. Например, зададим два массива так, чтобы попарно элементы
образовывали все возможные комбинации:
X = np.array([True, False, True, False])
Y = np.array([True, True, False, False])
И, затем,
применим к ним логические операции:
np.logical_and(X, Y) # логическое И: array([ True, False, False, False])
np.logical_or(X, Y) # логическое ИЛИ: array([False, True, False, True])
np.logical_not(X) # логическое НЕ: array([False, True, False, True])
np.logical_xor(X, Y) # XOR: array([ True, True, True, True])
Получили вполне
ожидаемые результаты в соответствии с таблицами истинности этих операций.
Все те же
операции можно проводить и с числовыми значениями, полагая, что 0 – это False, а любое другое
число – True. Например, два
таких массива:
a = np.array([1, 0, 2, 0])
b = np.array([3, 4, 0, 0])
Будут вести себя
идентично массивам X, Y при булевых операциях:
np.logical_and(a, b) # array([ True, False, False, False])
|