Spec-Zone.ru › NumPy 1.16

Модуль 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.

END_OF_DOCUMENT_MARKER

Получение только допустимых записей

Для получения только допустимых записей можно использовать инверсию маски в качестве индекса. Инверсию маски можно вычислить с помощью функции 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 имеет специфическую реализацию большинства ufunc. Унарные и бинарные функции, имеющие область определения (такие как 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 ufunc. Результатом является массив с маской. Результат унарной 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.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.16.1/reference/maskedarray.generic.html

Spec-Zone.ru

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