Spec-Zone.ru › NumPy 1.19

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

Примечание

Наследование от 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 — это коллекция уникальных типов аргументов из исходного вызова функции 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, и поэтому смешанные операции с массивами ndarray всегда дают матрицы.
  6. Матрицы имеют специальные атрибуты, которые упрощают вычисления. Это

    matrix.T

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

    matrix.H

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

    matrix.I

    Возвращает (мультипликативное) обратное значение обратимой матрицы.

    matrix.A

    Возвращает матрицу как объект 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

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

memmap.flush(self)

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

Пример:

>>> 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_, 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 = 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–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/reference/arrays.classes.html

Spec-Zone.ru

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