Spec-Zone.ru › NumPy 1.13

Модуль 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]) Маскирование массива, где значения не равны заданному значению.
    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], 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)

Массивные массивы также поддерживают стандартные ufuncs 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()

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

Spec-Zone.ru

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