Spec-Zone.ru › NumPy 1.20

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

Примечание

Наследование от 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 для переопределения поведения ufunc NumPy. Это работает очень похоже на операторы Python __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 для вызова отражённых методов https://docs.python.org/3/reference/datamodel.html#object.__ror__, и это гарантирует, что проверка перегрузок имеет приемлемую производительность даже при большом количестве перегруженных аргументов.

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

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

Объекты матриц переопределяют умножение, «*», и возведение в степень, «**», на матричное умножение и возведение матрицы в степень соответственно. Если ваш подпрограмме могут принимать подклассы, и вы не конвертируете в массивы базового класса, то вы должны использовать функции 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

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

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, …])

Предоставляет удобный вид на массивы строковых и 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 = 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

Итератор по массиву в 1-D.

Как уже упоминалось ранее, атрибут 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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/arrays.classes.html

Spec-Zone.ru

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