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