Модуль 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 имеет собственную реализацию большинства ufuncs. Унарные и бинарные функции, имеющие область допустимых значений (например, 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–2016 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.10.1/reference/maskedarray.generic.html