Spec-Zone.ru › NumPy 1.20

Модуль 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, …])

    Класс массива с возможными замаскированными значениями.

    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, 1.e+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])

    Маскирование массива, где значения равны заданному.

    ...

    ...

Доступ к данным

К данным базового массива можно получить доступ несколькими способами:

  • через атрибут data. Результат — представление массива как numpy.ndarray или одного из его подклассов, в зависимости от типа базовых данных при создании маскированного массива.
  • через метод __array__. Результат — numpy.ndarray.
  • путем непосредственного взятия представления маскированного массива как numpy.ndarray или одного из его подклассов (что фактически и делает атрибут data).
  • с помощью функции getdata.

Ни один из этих методов не является полностью удовлетворительным, если некоторые записи были помечены как недопустимые. В общем случае, когда требуется представление массива без замаскированных записей, рекомендуется заполнить массив методом filled.

Доступ к маске

К маске маскированного массива можно получить доступ через атрибут mask. Следует помнить, что запись True в маске указывает на недопустимые данные.

END_OF_DOCUMENT_MARKER

Ещё одним вариантом является использование функций 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, если ни одно из полей не замаскировано, или нульмерный массив с пропусками с таким же типом данных, что и исходный массив, если хотя бы одно из полей замаскировано.

>>> 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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/maskedarray.generic.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API