Модуль 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 = ma.masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20)
Полное обсуждение методов создания маскированных массивов см. в разделе Создание маскированных массивов.
Использование numpy.ma
Создание маскированных массивов
Существует несколько способов создания маскированного массива.
- Первый вариант — непосредственно вызвать класс
MaskedArray. -
Второй вариант — использование двух конструкторов маскированных массивов,
arrayиmasked_array.array(data[, dtype, copy, order, mask, ...])Класс массива с возможностью маскирования значений.
Псевдоним для
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', '<i8'), ('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])Маскирование массива, где значения не равны заданному.
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,
dtype=int64)
Наконец, конкретные элементы могут быть замаскированы и/или размаскированы путём присваивания последовательности булевых значений маске:
>>> 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()
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)
>>> x.harden_mask()
masked_array(data=[1, 2, 5],
mask=[False, False, False],
fill_value=999999)
Для размаскирования всех замаскированных элементов массива (при условии, что маска не жёсткая), простейшим решением является присвоение константы 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 >>> 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]
(3, --)
При обращении к срезу результат — массив со значениями маски, чей атрибут 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, False, False, False, True])
>>> x.data
array([ 1, -1, 3, 4, 5])
Обращение к полю массива со структурированным типом данных возвращает MaskedArray.
Операции с массивами со значениями маски
Арифметические и сравнительные операции поддерживаются массивами со значениями маски. По возможности, недопустимые элементы массива со значениями маски не обрабатываются, что означает, что соответствующие элементы data должны быть одинаковыми до и после операции.
Предупреждение
Нужно подчеркнуть, что это поведение может не быть систематическим, данные со значениями маски могут быть затронуты операцией в некоторых случаях, поэтому пользователи не должны полагаться на то, что эти данные останутся неизменными.
Модуль numpy.ma имеет особую реализацию большинства ufunc. Унарные и бинарные функции, имеющие область определения (например, log или divide), возвращают константу masked всякий раз, когда вход замаскирован или выходит за пределы области определения:
>>> ma.log([-1, 0, 1, 2])
masked_array(data=[--, --, 0.0, 0.6931471805599453],
mask=[ True, True, False, False],
fill_value=1e+20)
Массивы со значениями маски также поддерживают стандартные ufunc numpy. Результат — массив со значениями маски. Результат унарной ufunc замаскирован там, где вход замаскирован. Результат бинарной ufunc замаскирован там, где любой из входов замаскирован. Если ufunc также возвращает необязательный выходной контекст (кортеж из 3 элементов, содержащий имя ufunc, её аргументы и её область определения), контекст обрабатывается, и элементы выходного массива со значениями маски замаскированы там, где соответствующие входы выходят за пределы области определения:
>>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1])
>>> np.log(x)
masked_array(data=[--, 0.0, --, 0.6931471805599453, --],
mask=[ True, False, True, 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.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(ma.sqrt(x/y)) [1.0 -- -- 1.0 -- --]
Четыре значения результата недопустимы: первое происходит от извлечения квадратного корня из отрицательного числа, второе — от деления на ноль, а последние два — где входы были замаскированы.
Игнорирование экстремальных значений
Рассмотрим массив d с плавающими числами от 0 до 1. Мы хотим вычислить среднее значение значений d, игнорируя любые данные вне диапазона [0.2, 0.9]:
>>> d = np.linspace(0, 1, 20) >>> print(d.mean() - ma.masked_outside(d, 0.2, 0.9).mean()) -0.05263157894736836
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/maskedarray.generic.html