Spec-Zone.ru › NumPy 1.18

Стандартные подклассы массивов

Примечание

Наследование от numpy.ndarray возможно, но если вашей целью является создание массива с изменённым поведением, например, массивов dask для распределённых вычислений или cupy-массивов для вычислений на GPU, наследование не рекомендуется. Вместо этого рекомендуется использовать механизм диспетчеризации NumPy.

Класс ndarray можно наследоваться (в Python или C), если это необходимо. Поэтому он может служить основой для многих полезных классов. Часто решение о наследовании от объекта массива или просто использовании базового компонента массива как внутренней части нового класса — это непростой выбор, который может быть связан просто с предпочтениями. NumPy предлагает несколько инструментов для упрощения взаимодействия вашего нового объекта с другими объектами массивов, поэтому окончательный выбор может оказаться не столь значимым. Один из способов упростить вопрос — спросить себя, можно ли заменить интересуемый объект одним массивом, или он действительно требует двух или более массивов в своей основе.

Обратите внимание, что asarray всегда возвращает базовый класс ndarray. Если вы уверены, что ваш способ использования объекта массива может обрабатывать любой подкласс ndarray, то asanyarray можно использовать, чтобы подклассы более чисто распространялись через вашу подпрограмму. В принципе, подкласс может переопределить любой аспект массива, и поэтому, в соответствии с строгими правилами, asanyarray будет редко полезным. Однако большинство подклассов объекта массива не будут переопределять некоторые аспекты объекта массива, такие как интерфейс буфера или атрибуты массива. Однако важный пример, почему ваша подпрограмма может не справиться с произвольным подклассом массива, заключается в том, что матрицы переопределяют оператор «*» для выполнения матричного умножения вместо поэлементного.

Специальные атрибуты и методы

См. также

Наследование от ndarray

NumPy предоставляет несколько крючков, которые классы могут настраивать:

class.__array_ufunc__(ufunc, method, *inputs, **kwargs)

Новое в версии 1.13.

Любой класс, подкласс ndarray или нет, может определить этот метод или установить его в None, чтобы переопределить поведение функций NumPy ufunc. Это работает очень похоже на __mul__ Python и другие бинарные операционные процедуры.

  • ufunc — это объект ufunc, который был вызван.
  • method — строка, указывающая, какой метод Ufunc был вызван (один из "__call__", "reduce", "reduceat", "accumulate", "outer", "inner").
  • inputs — кортеж входных аргументов для ufunc.
  • kwargs — словарь, содержащий необязательные входные аргументы ufunc. Если заданы, любые аргументы out, как позиционные, так и ключевые, передаются как tuple в kwargs. Подробнее см. обсуждение в Универсальные функции (ufunc).

Метод должен вернуть либо результат операции, либо NotImplemented, если запрошенная операция не реализована.

Если один из входных или выходных аргументов имеет метод __array_ufunc__, он выполняется вместо ufunc. Если более одного аргумента реализуют __array_ufunc__, они выполняются в порядке: подклассы перед суперклассами, входные данные перед выходными, в противном случае слева направо. Первая процедура, возвращающая значение отличное от NotImplemented, определяет результат. Если все операции __array_ufunc__ возвращают NotImplemented, возникает TypeError.

Примечание

Мы намерены повторно реализовать функции numpy в виде (обобщенных) Ufunc, в этом случае станет возможным их переопределение методом __array_ufunc__. Хорошим кандидатом является matmul, который в настоящее время не является Ufunc, но может быть относительно легко переписан как (набор) обобщенных Ufunc. То же самое может произойти с функциями, такими как median, amin и argsort.

Как и с другими специальными методами в Python, такими как __hash__ и __iter__, можно указать, что ваш класс не поддерживает ufuncs, установив __array_ufunc__ = None. Ufuncs всегда возбуждают TypeError, когда вызываются на объекте, который устанавливает __array_ufunc__ = None.

Наличие __array_ufunc__ также влияет на то, как ndarray обрабатывает бинарные операции, такие как arr + obj и arr < obj, когда arr — ndarray, а obj — экземпляр пользовательского класса. Есть два варианта. Если obj.__array_ufunc__ присутствует и не равно None, то ndarray.__add__ и аналогичные будут делегировать обработку ufunc, что означает, что arr + obj станет np.add(arr, obj), а затем add вызовет obj.__array_ufunc__. Это полезно, если вы хотите определить объект, который ведет себя как массив.

В качестве альтернативы, если obj.__array_ufunc__ установлено в None, то в качестве специального случая, специальные методы, такие как ndarray.__add__ обратят на это внимание и безоговорочно возбудят TypeError. Это полезно, если вы хотите создать объекты, взаимодействующие с массивами через бинарные операции, но сами по себе не являющиеся массивами. Например, система обработки единиц может иметь объект m, представляющий единицу «метры», и желает поддерживать синтаксис arr * m для представления того, что массив имеет единицы «метры», но не хочет иначе взаимодействовать с массивами через ufuncs или иначе. Это можно сделать, установив __array_ufunc__ = None и определив методы __mul__ и __rmul__. (Обратите внимание, что это означает, что написание __array_ufunc__ который всегда возвращает NotImplemented, не совсем то же самое, что установка __array_ufunc__ = None: в первом случае, arr + obj возбудит TypeError, а во втором случае возможно определить метод __radd__ для предотвращения этого.)

Вышесказанное не относится к операциям на месте, для которых ndarray никогда не возвращает NotImplemented. Следовательно, arr += obj всегда приведет к TypeError. Это связано с тем, что для массивов операции на месте не могут быть обобщенно заменены простой обратной операцией. (Например, по умолчанию arr += obj будет переведено в arr = arr + obj, т.е. arr будет заменено, вопреки тому, что ожидается для операций с массивами на месте.)

Примечание

Если вы определяете __array_ufunc__:

  • Если вы не являетесь подклассом ndarray, мы рекомендуем, чтобы ваш класс определял специальные методы, такие как __add__ и __lt__, которые делегируют ufunc так же, как это делает ndarray. Простым способом сделать это является наследование от NDArrayOperatorsMixin.
  • Если вы являетесь подклассом ndarray, мы рекомендуем разместить всю логику переопределения в __array_ufunc__ и не переопределять также специальные методы. Это гарантирует, что иерархия классов определяется только в одном месте, а не отдельно механизмом ufunc и правилами бинарных операций (которые отдают предпочтение специальным методам подклассов; альтернативный способ принудительного определения иерархии в одном месте, установка __array_ufunc__ в None, показалось бы очень неожиданным и, следовательно, запутанным, так как тогда подкласс вообще не будет работать с ufuncs).
  • ndarray определяет свой собственный __array_ufunc__, который оценивает ufunc, если у аргументов нет переопределений, и возвращает NotImplemented в противном случае. Это может быть полезно для подклассов, для которых __array_ufunc__ преобразует любые экземпляры собственного класса в ndarray: затем он может передать их своему суперклассу с помощью super().__array_ufunc__(*inputs, **kwargs), и, наконец, вернуть результаты после возможного обратного преобразования. Преимущество этой практики состоит в том, что она гарантирует возможность иметь иерархию подклассов, расширяющих поведение. Подробности см. в Подклассы ndarray.

Примечание

Если класс определяет метод __array_ufunc__, это отключает механизм __array_wrap__, __array_prepare__, __array_priority__, описанный ниже для ufuncs (который, возможно, в конечном итоге будет устаревшим).

class.__array_function__(func, types, args, kwargs)

Новое в версии 1.16.

Примечание

  • В NumPy 1.17 протокол включён по умолчанию, но его можно отключить с помощью NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0.
  • В NumPy 1.16 необходимо установить переменную окружения NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1 перед импортом NumPy, чтобы использовать переопределения функций NumPy.
  • В конечном итоге ожидается, что __array_function__ всегда будет включён.
  • func — это произвольная вызываемая функция, экспонируемая общедоступным API NumPy, которая была вызвана в форме func(*args, **kwargs).
  • types — это коллекция уникальных типов аргументов из исходного вызова функции NumPy, которые реализуют __array_function__.
  • Кортеж args и словарь kwargs передаются напрямую из исходного вызова.

Для удобства разработчиков __array_function__ types предоставляет все типы аргументов с атрибутом '__array_function__'. Это позволяет разработчикам быстро определять случаи, когда они должны использовать реализации __array_function__ для других аргументов. Реализации не должны полагаться на порядок итерации types.

Большинство реализаций __array_function__ начнут с двух проверок:

  1. Является ли данная функция той, которую мы знаем, как перегрузить?
  2. Являются ли все аргументы такого типа, который мы знаем, как обработать?

Если эти условия выполняются, __array_function__ должен вернуть результат вызова своей реализации для func(*args, **kwargs). В противном случае он должен вернуть значение-маяк NotImplemented, указывающее на то, что функция не реализована для этих типов.

Нет общих требований к возвращаемому значению от __array_function__, хотя большинство разумных реализаций, вероятно, должны возвращать массив(ы) с тем же типом, что и один из аргументов функции.

Также может быть удобно определить пользовательские декораторы (implements ниже) для регистрации реализаций __array_function__.

HANDLED_FUNCTIONS = {}

class MyArray:
    def __array_function__(self, func, types, args, kwargs):
        if func not in HANDLED_FUNCTIONS:
            return NotImplemented
        # Note: this allows subclasses that don't override
        # __array_function__ to handle MyArray objects
        if not all(issubclass(t, MyArray) for t in types):
            return NotImplemented
        return HANDLED_FUNCTIONS[func](*args, **kwargs)

def implements(numpy_function):
    """Register an __array_function__ implementation for MyArray objects."""
    def decorator(func):
        HANDLED_FUNCTIONS[numpy_function] = func
        return func
    return decorator

@implements(np.concatenate)
def concatenate(arrays, axis=0, out=None):
    ...  # implementation of concatenate for MyArray objects

@implements(np.broadcast_to)
def broadcast_to(array, shape):
    ...  # implementation of broadcast_to for MyArray objects

Обратите внимание, что реализации __array_function__ не обязаны включать все соответствующие необязательные аргументы функции NumPy (например, broadcast_to выше опускает нерелевантный аргумент subok). Необязательные аргументы передаются в __array_function__ только в том случае, если они были явно использованы в вызове функции NumPy.

Как и в случае с встроенными специальными методами, такими как __add__, правильно написанные методы __array_function__ всегда должны возвращать NotImplemented при обнаружении неизвестного типа. В противном случае будет невозможно правильно переопределить функции NumPy из другого объекта, если операция также включает один из ваших объектов.

В основном правила диспетчеризации с __array_function__ соответствуют правилам для __array_ufunc__. В частности:

  • NumPy соберет реализации __array_function__ от всех указанных входных данных и вызовет их в порядке: подклассы перед суперклассами, а в противном случае слева направо. Обратите внимание, что в некоторых крайних случаях, связанных с подклассами, это немного отличается от текущего поведения Python.
  • Реализации __array_function__ указывают, что они могут обработать операцию, вернув любое значение, отличное от NotImplemented.
  • Если все методы __array_function__ возвращают NotImplemented, NumPy выведет TypeError.

Если методы __array_function__ отсутствуют, NumPy по умолчанию вызовет собственную реализацию, предназначенную для использования с массивами NumPy. Такой случай возникает, например, когда все массивоподобные аргументы — это числа Python или списки. (Массивы NumPy имеют метод __array_function__, приведенный ниже, но он всегда возвращает NotImplemented если любой аргумент, кроме подкласса массива NumPy, реализует __array_function__.)

Одно отклонение от текущего поведения __array_ufunc__ заключается в том, что NumPy вызовет __array_function__ только для первого аргумента каждого уникального типа. Это соответствует правилу Python для вызова отражённых методов, и это гарантирует, что проверка перегрузок будет иметь приемлемую производительность даже при большом количестве перегруженных аргументов.

class.__array_finalize__(obj)

Этот метод вызывается всякий раз, когда система внутренне выделяет новый массив из obj, где obj — подкласс (подтип) ndarray. Его можно использовать для изменения атрибутов self после построения (например, для обеспечения матрицы 2-х мерности), или для обновления метаданных из «родителя». Подклассы наследуют реализацию этого метода по умолчанию, которая ничего не делает.

class.__array_prepare__(array, context=None)

В начале каждого ufunc этот метод вызывается на объекте ввода с наивысшим приоритетом массива или объекте вывода, если он был указан. Массив вывода передаётся, и всё, что возвращается, передаётся ufunc. Подклассы наследуют реализацию этого метода по умолчанию, которая просто возвращает массив вывода без изменений. Подклассы могут использовать этот метод для преобразования массива вывода в экземпляр подкласса и обновления метаданных перед возвращением массива в ufunc для вычисления.

Примечание

Для ufunc, ожидается, что в конечном итоге этот метод будет устаревать в пользу __array_ufunc__.

class.__array_wrap__(array, context=None)

В конце каждого ufunc этот метод вызывается на объекте ввода с наивысшим приоритетом массива или объекте вывода, если он был указан. Вычисленная ufunc массив передаётся, и всё, что возвращается, передаётся пользователю. Подклассы наследуют реализацию этого метода по умолчанию, которая преобразует массив в новый экземпляр класса объекта. Подклассы могут использовать этот метод для преобразования массива вывода в экземпляр подкласса и обновления метаданных перед возвращением массива пользователю.

Примечание

Для ufunc, ожидается, что в конечном итоге этот метод будет устаревать в пользу __array_ufunc__.

class.__array_priority__

Значение этого атрибута используется для определения типа объекта, который должен быть возвращён в ситуациях, когда существует более одной возможности для Python-типа возвращаемого объекта. Подклассы наследуют значение по умолчанию 0.0 для этого атрибута.

Примечание

Для ufunc, ожидается, что в конечном итоге этот метод будет устаревать в пользу __array_ufunc__.

class.__array__([dtype])

Если класс (подкласс ndarray или нет), имеющий метод __array__, используется в качестве объекта вывода ufunc, результаты будут записаны в объект, возвращённый методом __array__. Аналогичное преобразование выполняется для входных массивов.

Объекты матриц

Примечание

Сильно рекомендуется не использовать подкласс матрицы. Как описано ниже, это затрудняет написание функций, которые последовательно обрабатывают матрицы и обычные массивы. В настоящее время они используются в основном для взаимодействия с scipy.sparse. Однако мы надеемся предоставить альтернативу для этого использования и в конечном итоге удалить подкласс matrix.

matrix объекты наследуют от ndarray и поэтому имеют те же атрибуты и методы, что и ndarray. Однако существует шесть важных различий между объектами матрицы, которые могут привести к неожиданным результатам при использовании матриц, но ожидается, что они будут вести себя как массивы:

  1. Объекты матриц могут быть созданы с использованием строкового обозначения, чтобы разрешить синтаксис в стиле Matlab, где пробелы отделяют столбцы, а точки с запятой (';') отделяют строки.
  2. Объекты матриц всегда двумерны. Это имеет далеко идущие последствия, заключающиеся в том, что m.ravel() все еще двумерен (с 1 в первом измерении), а выбор элементов возвращает двумерные объекты, так что поведение последовательностей принципиально отличается от массивов.
  3. Объекты матриц переопределяют умножение, чтобы это было матричное умножение. Убедитесь, что вы понимаете это для функций, которые вы можете захотеть получить матрицы. Особенно с учетом того, что asanyarray(m) возвращает матрицу, когда m является матрицей.
  4. Объекты матриц переопределяют возведение в степень, чтобы это было возведение матрицы в степень. То же самое предупреждение о использовании возведения в степень внутри функции, которая использует asanyarray(...), чтобы получить объект массива, справедливо и в этом случае.
  5. Значение по умолчанию __array_priority__ объектов матриц равно 10.0, и поэтому смешанные операции с nd-массивами всегда производят матрицы.
  6. Матрицы имеют специальные атрибуты, которые упрощают вычисления. Это

    matrix.T

    Возвращает транспонированную матрицу.

    matrix.H

    Возвращает (комплексно) сопряжённую транспонированную матрицу self.

    matrix.I

    Возвращает (мультипликативный) обратный элемент для обратимой self.

    matrix.A

    Возвращает self как объект ndarray.

Предупреждение

Объекты матриц переопределяют умножение, ‘*’, и возведение в степень, ‘**’, соответственно на матричное умножение и возведение матрицы в степень. Если ваша подпрограмма может принимать подклассы, и вы не конвертируете в массивы базового класса, то вы должны использовать ufuncs multiply и power, чтобы быть уверенными, что вы выполняете правильную операцию для всех входных данных.

Класс матриц является подклассом ndarray в Python и может быть использован как ссылка для того, как создать свой собственный подкласс ndarray. Матрицы могут быть созданы из других матриц, строк и всего, что может быть преобразовано в ndarray. Имя «mat» является псевдонимом для «matrix» в NumPy.

matrix(data[, dtype, copy])

Примечание

Использование этого класса больше не рекомендуется, даже для линейных

asmatrix(data[, dtype])

Интерпретировать входные данные как матрицу.

bmat(obj[, ldict, gdict])

Создать объект матрицы из строки, вложенной последовательности или массива.

Пример 1: Создание матрицы из строки

>>> a=mat('1 2 3; 4 5 3')
>>> print (a*a.T).I
[[ 0.2924 -0.1345]
 [-0.1345  0.0819]]

Пример 2: Создание матрицы из вложенной последовательности

>>> mat([[1,5,10],[1.0,3,4j]])
matrix([[  1.+0.j,   5.+0.j,  10.+0.j],
        [  1.+0.j,   3.+0.j,   0.+4.j]])

Пример 3: Создание матрицы из массива

>>> mat(random.rand(3,3)).T
matrix([[ 0.7699,  0.7922,  0.3294],
        [ 0.2792,  0.0101,  0.9219],
        [ 0.3398,  0.7571,  0.8197]])

Массивы с отображением памяти в файлах

Массивы с отображением памяти в файлах полезны для чтения и/или изменения небольших фрагментов большого файла с регулярной структурой, не загружая весь файл в память. Простой подкласс ndarray использует файл с отображением памяти для буфера данных массива. Для небольших файлов накладные расходы на загрузку всего файла в память обычно незначительны, однако для больших файлов использование отображения памяти может сэкономить значительные ресурсы.

Массивы с отображением памяти в файлах имеют один дополнительный метод (кроме тех, которые они наследуют от ndarray): .flush(), который должен быть вызван пользователем вручную, чтобы убедиться, что любые изменения в массиве фактически записываются на диск.

memmap

Создать отображение памяти на массив, хранящийся в бинарном файле на диске.

memmap.flush(self)

Записать любые изменения в массиве в файл на диске.

Пример:

>>> a = memmap('newfile.dat', dtype=float, mode='w+', shape=1000)
>>> a[10] = 10.0
>>> a[30] = 30.0
>>> del a
>>> b = fromfile('newfile.dat', dtype=float)
>>> print b[10], b[30]
10.0 30.0
>>> a = memmap('newfile.dat', dtype=float)
>>> print a[10], a[30]
10.0 30.0

Массивы символов (numpy.char)

См. также

Создание массивов символов (numpy.char)

Примечание

Класс chararray существует для обратной совместимости с Numarray, он не рекомендуется для новых разработок. Начиная с numpy 1.4, если нужны массивы строк, рекомендуется использовать массивы типа dtype object_, string_ или unicode_, и использовать свободные функции в модуле numpy.char для быстрых векторизованных строковых операций.

Это расширенные массивы типа string_ или unicode_. Эти массивы наследуют от ndarray, но специально определяют операции +, *, и % на основе элемента по элементу (с широковещанием). Эти операции недоступны для стандартного ndarray символьного типа. Кроме того, chararray имеет все стандартные string (и unicode) методы, выполняющие их на основе элемента по элементу. Возможно, самый простой способ создать chararray - использовать self.view(chararray), где self является ndarray типа str или unicode. Однако chararray также можно создать, используя конструктор numpy.chararray или функцию numpy.char.array:

chararray(shape[, itemsize, unicode, …])

Обеспечивает удобный вид на массивы строковых и unicode значений.

core.defchararray.array(obj[, itemsize, …])

Создать chararray.

Другое отличие от стандартного ndarray типа str заключается в том, что chararray наследует функцию, введённую Numarray, где пробелы в конце любого элемента в массиве игнорируются при получении элемента и сравнении.

Массивы записей (numpy.rec)

См. также

Создание массивов записей (numpy.rec), Функции работы с типами данных, Объекты типов данных (dtype).

NumPy предоставляет класс recarray, который позволяет получать доступ к полям структурированного массива как к атрибутам, и соответствующий скалярный объект типа данных record.

recarray

Создать ndarray, который позволяет получать доступ к полям с использованием атрибутов.

record

Скаляр типа данных, который позволяет получать доступ к полям как к поиску атрибутов.

Маскированные массивы (numpy.ma)

См. также

Маскированные массивы

Стандартный контейнерный класс

Для обратной совместимости и как стандартный «контейнерный» класс, UserArray из Numeric был перенесен в NumPy и переименован в numpy.lib.user_array.container. Контейнерный класс — это класс Python, чьё свойство self.array является ndarray. Многократное наследование, вероятно, проще с помощью numpy.lib.user_array.container, чем с самим ndarray, и поэтому оно включено по умолчанию. Оно не документируется здесь, кроме упоминания его существования, так как рекомендуется использовать класс ndarray напрямую, если это возможно.

numpy.lib.user_array.container(data[, …])

Стандартный класс контейнера для простого множественного наследования.

Итераторы массивов

Итераторы — мощная концепция для обработки массивов. По сути, итераторы реализуют обобщённый цикл for. Если myiter — объект-итератор, то Python-код:

for val in myiter:
    ...
    some code involving val
    ...

вызывает val = next(myiter) многократно до тех пор, пока StopIteration не будет поднят итератором. Существуют несколько способов итерирования по массиву, которые могут быть полезны: стандартное итерирование, плоское итерирование и N-мерное перечисление.

Стандартное итерирование

Стандартный итератор объекта ndarray — стандартный Python-итератор типа последовательности. Таким образом, при использовании самого объекта массива в качестве итератора. Стандартное поведение эквивалентно:

for i in range(arr.shape[0]):
    val = arr[i]

Этот стандартный итератор выбирает подмассив размерности N-1 из массива. Это может быть полезной конструкцией для определения рекурсивных алгоритмов. Для перебора всего массива требуется N вложенных циклов for.

>>> a = arange(24).reshape(3,2,4)+10
>>> for val in a:
...     print 'item:', val
item: [[10 11 12 13]
 [14 15 16 17]]
item: [[18 19 20 21]
 [22 23 24 25]]
item: [[26 27 28 29]
 [30 31 32 33]]

Плоское итерирование

ndarray.flat

1-мерный итератор по массиву.

Как уже упоминалось, атрибут flat объектов ndarray возвращает итератор, который будет перебирать весь массив в порядке, непрерывном в стиле C.

>>> for i, val in enumerate(a.flat):
...     if i%5 == 0: print i, val
0 10
5 15
10 20
15 25
20 30

Здесь я использовал встроенный итератор enumerate, чтобы вернуть индекс итератора, а также значение.

N-мерное перечисление

ndenumerate(arr)

Итератор многомерных индексов.

Иногда может быть полезно получить N-мерный индекс во время итерирования. Итератор ndenumerate может это обеспечить.

>>> for i, val in ndenumerate(a):
...     if sum(i)%5 == 0: print i, val
(0, 0, 0) 10
(1, 1, 3) 25
(2, 0, 3) 29
(2, 1, 2) 32

Итератор для векторизации

broadcast

Создаёт объект, имитирующий векторизацию.

Общая концепция векторизации также доступна из Python с помощью итератора broadcast. Этот объект принимает N объекты в качестве входных данных и возвращает итератор, который возвращает кортежи, содержащие элементы каждого входного набора последовательностей в широковещательном результате.

>>> for val in broadcast([[1,0],[2,3]],[0,1]):
...     print val
(1, 0)
(0, 1)
(2, 0)
(3, 1)

© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.18/reference/arrays.classes.html

Spec-Zone.ru

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