Spec-Zone.ru › NumPy 1.11

Модуль 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 Псевдоним 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  True False],
       fill_value = 999999)
>>> x.mask
array([False,  True, False, False,  True], dtype=bool)
>>> 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 ufuncs. Результатом является массив-маска. Результат унарной 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()

© 2008–2016 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.11.0/reference/maskedarray.generic.html

Spec-Zone.ru

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