Модуль numpy.ma
Описание
Маскированные массивы — это массивы, которые могут содержать пропущенные или недопустимые элементы. Модуль numpy.ma предоставляет практически идентичную замену numpy, поддерживающую массивы данных с масками.
Что такое маскированный массив?
В многих ситуациях наборы данных могут быть неполными или содержать недопустимые данные. Например, датчик может не записать данные или записать недопустимое значение. Модуль numpy.ma предоставляет удобный способ решения этой проблемы, введя маскированные массивы.
Маскированный массив — это комбинация стандартного numpy.ndarray и маски. Маска может быть nomask, указывая, что ни одно значение связанного массива не является недопустимым, или массивом булевых значений, определяющим для каждого элемента связанного массива, является ли значение допустимым или нет. Когда элемент маски False, соответствующий элемент связанного массива допустим и считается не замаскированным. Когда элемент маски True, соответствующий элемент связанного массива считается замаскированным (недопустимым).
Пакет гарантирует, что маскированные элементы не используются в вычислениях.
В качестве иллюстрации рассмотрим следующий набор данных:
>>> import numpy as np >>> import numpy.ma as ma >>> x = np.array([1, 2, 3, -1, 5])
Мы хотим пометить четвертый элемент как недопустимый. Самый простой способ — создать маскированный массив:
>>> mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0])
Теперь мы можем вычислить среднее значение набора данных, не учитывая недопустимые данные:
>>> mx.mean() 2.75
Модуль numpy.ma
Основная функция модуля numpy.ma — класс MaskedArray, который является подклассом numpy.ndarray. Класс, его атрибуты и методы более подробно описаны в разделе Класс MaskedArray.
Модуль numpy.ma может использоваться как дополнение к numpy:
>>> import numpy as np >>> import numpy.ma as ma
Чтобы создать массив со вторым элементом недопустимым, мы выполним следующие действия:
>>> y = ma.array([1, 2, 3], mask = [0, 1, 0])
Чтобы создать маскированный массив, где все значения, близкие к 1.e20, являются недопустимыми, мы выполним следующие действия:
>>> z = masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20)
Полное описание методов создания маскированных массивов см. в разделе Создание маскированных массивов.
Использование numpy.ma
Создание маскированных массивов
Существует несколько способов создания маскированного массива.
- Первое — это непосредственное обращение к классу
MaskedArray. -
Второй вариант — использование двух конструкторов маскированных массивов,
arrayиmasked_array.array(data[, dtype, copy, order, mask, …])Класс массива с потенциально замаскированными значениями. masked_arrayПсевдоним для numpy.ma.core.MaskedArray -
Третий вариант — использование представления существующего массива. В этом случае маска представления устанавливается в
nomask, если массив не имеет именованных полей, или массив булевых значений с такой же структурой, как и массив в противном случае.>>> x = np.array([1, 2, 3]) >>> x.view(ma.MaskedArray) masked_array(data = [1 2 3], mask = False, fill_value = 999999) >>> x = np.array([(1, 1.), (2, 2.)], dtype=[('a',int), ('b', float)]) >>> x.view(ma.MaskedArray) masked_array(data = [(1, 1.0) (2, 2.0)], mask = [(False, False) (False, False)], fill_value = (999999, 1e+20), dtype = [('a', '<i4'), ('b', '<f8')]) -
Еще один вариант — использование одной из следующих функций:
asarray(a[, dtype, order])Преобразовать входные данные в маскированный массив заданного типа данных. asanyarray(a[, dtype])Преобразовать входные данные в маскированный массив, сохраняя подклассы. fix_invalid(a[, mask, copy, fill_value])Возвращает входные данные с замаскированными недопустимыми данными, замененными значением заполнения. masked_equal(x, value[, copy])Маскирование массива, где значение равно заданному значению. masked_greater(x, value[, copy])Маскирование массива, где значение больше заданного значения. masked_greater_equal(x, value[, copy])Маскирование массива, где значение больше или равно заданному значению. masked_inside(x, v1, v2[, copy])Маскирование массива внутри заданного интервала. masked_invalid(a[, copy])Маскирование массива, где встречаются недопустимые значения (NaN или inf). masked_less(x, value[, copy])Маскирование массива, где значение меньше заданного значения. masked_less_equal(x, value[, copy])Маскирование массива, где значение меньше или равно заданному значению. masked_not_equal(x, value[, copy])Маскирование массива, где notне равно заданному значению.masked_object(x, value[, copy, shrink])Маскирование массива xтам, где данные точно равны значению.masked_outside(x, v1, v2[, copy])Маскирование массива вне заданного интервала. masked_values(x, value[, rtol, atol, copy, …])Маскирование с использованием точного равенства с плавающей запятой. masked_where(condition, a[, copy])Маскирование массива, где выполняется условие.
Доступ к данным
К данным базового массива можно получить доступ несколькими способами:
- через атрибут
data. Результат — представление массива в видеnumpy.ndarrayили одного из его подклассов, в зависимости от типа базовых данных при создании маскированного массива. - через метод
__array__. Результат —numpy.ndarray. - путём непосредственного получения представления маскированного массива как
numpy.ndarrayили одного из его подклассов (фактически то, что делает атрибутdata). - используя функцию
getdata.
Ни один из этих методов не является полностью удовлетворительным, если некоторые записи были помечены как недопустимые. В общем случае, если требуется представление массива без маскированных записей, рекомендуется использовать метод filled для заполнения массива.
Доступ к маске
К маске маскированного массива можно получить доступ через атрибут mask. Следует помнить, что запись True в маске указывает на недопустимые данные.
Ещё один вариант — использовать функции getmask и getmaskarray. getmask(x) выводит маску x если x является массивной, и специальное значение nomask в противном случае. getmaskarray(x) выводит маску x если x является массивной. Если x не содержит недопустимых значений или не является массивной, функция выводит булеву массив False из x элементов.
Получение только допустимых значений
Чтобы получить только допустимые значения, можно использовать инверсию маски в качестве индекса. Инверсию маски можно рассчитать с помощью функции numpy.logical_not или просто с помощью оператора ~.
>>> x = ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]])
>>> x[~x.mask]
masked_array(data = [1 4],
mask = [False False],
fill_value = 999999)
Другой способ получения допустимых данных — использование метода compressed, который возвращает одномерный ndarray (или один из его подклассов, в зависимости от значения атрибута baseclass):
>>> x.compressed() array([1, 4])
Обратите внимание, что вывод compressed всегда одномерный.
Изменение маски
Маскирование элемента
Рекомендуемый способ маркировки одного или нескольких конкретных элементов массива с маской как недопустимых — присвоить им специальное значение masked:
>>> x = ma.array([1, 2, 3])
>>> x[0] = ma.masked
>>> x
masked_array(data = [-- 2 3],
mask = [ True False False],
fill_value = 999999)
>>> y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> y[(0, 1, 2), (1, 2, 0)] = ma.masked
>>> y
masked_array(data =
[[1 -- 3]
[4 5 --]
[-- 8 9]],
mask =
[[False True False]
[False False True]
[ True False False]],
fill_value = 999999)
>>> z = ma.array([1, 2, 3, 4])
>>> z[:-2] = ma.masked
>>> z
masked_array(data = [-- -- 3 4],
mask = [ True True False False],
fill_value = 999999)
Второй вариант — непосредственно изменить mask, но это использование не рекомендуется.
Примечание
При создании нового массива с маской с простым, неструктурированным типом данных, маска изначально устанавливается в специальное значение nomask, которое примерно соответствует булеву False. Попытка установить элемент nomask завершится с ошибкой TypeError, так как булевы значения не поддерживают присваивание элементов.
Все элементы массива могут быть замаскированы одновременно, присвоив True маске:
>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x.mask = True
>>> x
masked_array(data = [-- -- --],
mask = [ True True True],
fill_value = 999999)
Наконец, отдельные элементы могут быть замаскированы и/или размаскированы, присвоив маске последовательность булевых значений:
>>> x = ma.array([1, 2, 3])
>>> x.mask = [0, 1, 0]
>>> x
masked_array(data = [1 -- 3],
mask = [False True False],
fill_value = 999999)
Размаскирование элемента
Чтобы размаскировать один или несколько конкретных элементов, достаточно просто присвоить им новые допустимые значения:
>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data = [1 2 --],
mask = [False False True],
fill_value = 999999)
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 5],
mask = [False False False],
fill_value = 999999)
Примечание
Размаскирование элемента прямым присваиванием будет молча проигнорировано, если у массива с маской установлена *жесткая* маска, как показано атрибутом hardmask. Эта функция была введена для предотвращения перезаписи маски. Чтобы принудительно размаскировать элемент, где у массива установлена жесткая маска, маска должна быть сначала размягчена с помощью метода soften_mask перед выделением. Она может быть снова затвержена с помощью harden_mask:
>>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True)
>>> x
masked_array(data = [1 2 --],
mask = [False False True],
fill_value = 999999)
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 --],
mask = [False False True],
fill_value = 999999)
>>> x.soften_mask()
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 5],
mask = [False False False],
fill_value = 999999)
>>> x.harden_mask()
Чтобы размаскировать все замаскированные элементы массива с маской (при условии, что маска не является жесткой), самое простое решение — присвоить константу nomask маске:
>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data = [1 2 --],
mask = [False False True],
fill_value = 999999)
>>> x.mask = ma.nomask
>>> x
masked_array(data = [1 2 3],
mask = [False False False],
fill_value = 999999)
Индексирование и срезы
Так как MaskedArray является подклассом numpy.ndarray, он наследует его механизмы индексирования и срезов.
При обращении к отдельному элементу массива с маской без именованных полей результат — это скаляр (если соответствующий элемент маски — False) или специальное значение masked (если соответствующий элемент маски — True):
>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x[0]
1
>>> x[-1]
masked_array(data = --,
mask = True,
fill_value = 1e+20)
>>> x[-1] is ma.masked
True
Если массив с маской имеет именованные поля, обращение к отдельному элементу возвращает объект numpy.void, если ни одно из полей не замаскировано, или 0-мерный массив с маской с тем же типом данных, что и исходный массив, если хотя бы одно из полей замаскировано.
>>> y = ma.masked_array([(1,2), (3, 4)],
... mask=[(0, 0), (0, 1)],
... dtype=[('a', int), ('b', int)])
>>> y[0]
(1, 2)
>>> y[-1]
masked_array(data = (3, --),
mask = (False, True),
fill_value = (999999, 999999),
dtype = [('a', '<i4'), ('b', '<i4')])
При обращении к срезу результат — это массив с маской, чьим атрибутом data является представление исходных данных, а маской является либо nomask (если в исходном массиве не было недопустимых значений), либо представлением соответствующего среза исходной маски. Представление необходимо для обеспечения распространения любых изменений маски на исходный массив.
>>> x = ma.array([1, 2, 3, 4, 5], mask=[0, 1, 0, 0, 1])
>>> mx = x[:3]
>>> mx
masked_array(data = [1 -- 3],
mask = [False True False],
fill_value = 999999)
>>> mx[1] = -1
>>> mx
masked_array(data = [1 -1 3],
mask = [False False False],
fill_value = 999999)
>>> x.mask
array([False, True, False, False, True])
>>> x.data
array([ 1, -1, 3, 4, 5])
Обращение к полю массива с маской со структурированным типом данных возвращает MaskedArray.
Операции с массивами с маской
Арифметические и сравнительные операции поддерживаются массивами с маской. По возможности, недопустимые значения массива с маской не обрабатываются, что означает, что соответствующие data элементы *должны* быть одинаковыми до и после операции.
Предупреждение
Необходимо подчеркнуть, что это поведение может не быть систематическим, что замаскированные данные могут быть затронуты операцией в некоторых случаях, и поэтому пользователи не должны полагаться на то, что эти данные останутся неизменными.
Модуль numpy.ma содержит специальную реализацию большинства ufuncs. Унарные и бинарные функции, имеющие область допустимости (такие как log или divide) возвращают константу masked всякий раз, когда вход замаскирован или выходит за пределы области допустимости:
>>> ma.log([-1, 0, 1, 2])
masked_array(data = [-- -- 0.0 0.69314718056],
mask = [ True True False False],
fill_value = 1e+20)
Массивы с маской также поддерживают стандартные numpy ufuncs. Результат — массив с маской. Результат унарной ufunc замаскирован, где вход замаскирован. Результат бинарной ufunc замаскирован, где любой из входов замаскирован. Если ufunc также возвращает контекстный вывод (тройку, содержащую имя ufunc, ее аргументы и область), контекст обрабатывается, и элементы выходного массива с маской маскируются, когда соответствующий вход выходит за пределы области допустимости:
>>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1])
>>> np.log(x)
masked_array(data = [-- -- 0.0 0.69314718056 --],
mask = [ True True False False True],
fill_value = 1e+20)
Примеры
Данные со значением, представляющим пропущенные данные
Рассмотрим список элементов, x, где значения -9999 представляют пропущенные данные. Мы хотим вычислить среднее значение данных и вектор аномалий (отклонения от среднего):
>>> import numpy.ma as ma >>> x = [0.,1.,-9999.,3.,4.] >>> mx = ma.masked_values (x, -9999.) >>> print mx.mean() 2.0 >>> print mx - mx.mean() [-2.0 -1.0 -- 1.0 2.0] >>> print mx.anom() [-2.0 -1.0 -- 1.0 2.0]
Заполнение пропущенных данных
Предположим теперь, что мы хотим вывести эти же данные, но с заменой пропущенных значений на среднее значение.
>>> print mx.filled(mx.mean()) [ 0. 1. 2. 3. 4.]
Числовые операции
Числовые операции можно легко выполнять, не беспокоясь о пропущенных значениях, делении на ноль, квадратных корнях из отрицательных чисел и т. д.:
>>> import numpy as np, numpy.ma as ma >>> x = ma.array([1., -1., 3., 4., 5., 6.], mask=[0,0,0,0,1,0]) >>> y = ma.array([1., 2., 0., 4., 5., 6.], mask=[0,0,0,0,0,1]) >>> print np.sqrt(x/y) [1.0 -- -- 1.0 -- --]
Четыре значения в выводе недопустимы: первое — из извлечения квадратного корня из отрицательного числа, второе — из деления на ноль, а последние два — где входы были замаскированы.
Игнорирование крайних значений
Рассмотрим массив d случайных чисел с плавающей точкой между 0 и 1. Мы хотим вычислить среднее значение значений d , игнорируя любые данные вне диапазона [0.1, 0.9]:
>>> print ma.masked_outside(d, 0.1, 0.9).mean()
© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.14.5/reference/maskedarray.generic.html