Spec-Zone.ru › NumPy 1.21

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

Примечание

Наследование от 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. Это работает очень похоже на Python's __mul__ и другие процедуры бинарных операций.

  • 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__, можно указать, что ваш класс не поддерживает функции ufunc, установив __array_ufunc__ = None. Функции ufunc всегда генерируют 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 для представления того, что массив имеет единицы «метры», но не взаимодействовать иначе с массивами через функции ufunc или иным образом. Это можно сделать, установив __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, что может показаться очень неожиданным и запутанным, так как тогда подкласс вообще не будет работать с функциями ufunc).
  • ndarray определяет свой собственный __array_ufunc__, который оценивает функцию ufunc, если ни один из аргументов не имеет переопределения, и возвращает NotImplemented в противном случае. Это может быть полезно для подклассов, для которых __array_ufunc__ преобразует все экземпляры собственного класса в ndarray: затем он может передать их своему суперклассу с помощью super().__array_ufunc__(*inputs, **kwargs), и, наконец, вернуть результаты после возможного преобразования обратно. Преимущество этого подхода заключается в том, что он гарантирует, что можно иметь иерархию подклассов, которые расширяют поведение. См. Наследование от ndarray для получения дополнительной информации.

Примечание

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

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 — коллекция collections.abc.Collection уникальных типов аргументов из исходного вызова функции 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 после создания (например, для обеспечения двумерной матрицы) или для обновления метаданных из «родителя». Подклассы наследуют реализацию этого метода по умолчанию, которая ничего не делает.

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__. Эта практика вернёт TypeError.

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

Примечание

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

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

  1. Объекты матриц могут быть созданы с использованием строковой нотации, что позволяет использовать синтаксис в стиле Matlab, где пробелы разделяют столбцы, а точки с запятой (';') разделяют строки.
  2. Объекты матриц всегда двумерные. Это имеет далеко идущие последствия, так как m.ravel() все еще двумерный (с 1 в первом измерении), а выбор элементов возвращает двумерные объекты, поэтому поведение последовательностей фундаментально отличается от массивов.
  3. Объекты матриц переопределяют умножение, чтобы оно выполнялось как матричное умножение. Убедитесь, что вы понимаете это для функций, которые вы, возможно, хотите использовать для матриц. Особенно в свете того, что asanyarray(m) возвращает матрицу, когда m является матрицей.
  4. Объекты матриц переопределяют возведение в степень, чтобы оно выполнялось как возведение матрицы в степень. То же предупреждение о использовании возведения в степень внутри функции, которая использует asanyarray(…), чтобы получить объект массива, справедливо и в этом случае.
  5. Значение __array_priority__ по умолчанию для объектов матриц равно 10.0, поэтому смешанные операции с ndarrays всегда производят матрицы.
  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 = np.mat('1 2 3; 4 5 3')
>>> print((a*a.T).I)
    [[ 0.29239766 -0.13450292]
     [-0.13450292  0.08187135]]

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

>>> np.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: Создание матрицы из массива

>>> np.mat(np.random.rand(3,3)).T
matrix([[4.17022005e-01, 3.02332573e-01, 1.86260211e-01],
        [7.20324493e-01, 1.46755891e-01, 3.45560727e-01],
        [1.14374817e-04, 9.23385948e-02, 3.96767474e-01]])

Массивы с памятью в файлах

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

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

memmap(filename[, dtype, mode, offset, …])

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

memmap.flush()

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

Пример:

>>> a = np.memmap('newfile.dat', dtype=float, mode='w+', shape=1000)
>>> a[10] = 10.0
>>> a[30] = 30.0
>>> del a
>>> b = np.fromfile('newfile.dat', dtype=float)
>>> print(b[10], b[30])
10.0 30.0
>>> a = np.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_, bytes_ или str_, и использовать свободные функции в модуле numpy.char для быстрых векторизованных строковых операций.

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

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

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

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

Создать chararray.

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

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

См. также

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

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

recarray(shape[, dtype, buf, offset, …])

Построить 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 = np.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

Итератор по массиву в одномерном виде.

Как уже упоминалось ранее, атрибут 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 np.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 np.broadcast([[1,0],[2,3]],[0,1]):
...     print(val)
(1, 0)
(0, 1)
(2, 0)
(3, 1)

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

Spec-Zone.ru

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