Spec-Zone.ru › NumPy 1.17

Модуль 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 имеет особую реализацию большинства 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)

Массивы-маски также поддерживают стандартные 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.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.17.0/reference/maskedarray.generic.html

Spec-Zone.ru

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