Стандартные подклассы массивов
Примечание
Наследование от numpy.ndarray возможно, но если вашей целью является создание массива с изменённым поведением, как это делают массивы dask для распределённых вычислений и массивы cupy для вычислений на графическом процессоре, наследование не рекомендуется. Вместо этого рекомендуется использовать механизм диспетчеризации NumPy механизм диспетчеризации.
Класс ndarray можно унаследовать (в Python или C), если это необходимо. Поэтому он может служить основой для многих полезных классов. Часто выбор между наследованием от объекта массива или простым использованием базового компонента массива в качестве внутренней части нового класса является сложным решением и может быть просто вопросом выбора. NumPy предоставляет несколько инструментов для упрощения взаимодействия вашего нового объекта с другими объектами массивов, поэтому этот выбор в итоге может не иметь большого значения. Один из способов упростить вопрос — спросить себя, можно ли заменить интересующий вас объект одним массивом или он действительно требует двух или более массивов в своей основе.
Обратите внимание, что asarray всегда возвращает базовый класс ndarray. Если вы уверены, что ваш способ использования объекта массива может обрабатывать любой подкласс ndarray, то asanyarray может быть использован для более чистого распространения подклассов через вашу подпрограмму. В принципе, подкласс может переопределить любой аспект массива, и, следовательно, при строгих правилах asanyarray редко будет полезен. Однако большинство подклассов объекта массива не будут переопределять некоторые аспекты объекта массива, такие как интерфейс буфера или атрибуты массива. Однако одним важным примером того, почему ваша подпрограмма может не справиться с произвольным подклассом массива, является то, что матрицы переопределяют оператор «*» для выполнения матричного умножения, а не поэлементного.
Специальные атрибуты и методы
См. также
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, если запрошенная операция не реализована.Если у одного из входных, выходных или
whereаргументов есть метод__array_ufunc__, он выполняется вместо ufunc. Если более одного из аргументов реализуют__array_ufunc__, они проверяются в следующем порядке: подклассы перед суперклассами, входные данные перед выходными, выходные передwhere, иначе слева направо. Первый метод, возвращающий значение отличное от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для указания того, что массив имеет единицы измерения «метры», но не хочет взаимодействовать с массивами через 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.
- class.__array_function__(func, types, args, kwargs)
-
New in version 1.16.
-
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__начинаются с двух проверок:- Является ли данная функция чем-то, что мы можем перегрузить?
- Являются ли все аргументы типа, с которым мы можем работать?
Если эти условия выполняются,
__array_function__должно вернуть результат вызова своей реализации дляfunc(*args, **kwargs). В противном случае оно должно вернуть значение sentinelaNotImplemented, указывая, что функция не реализована для этих типов.Нет общих требований к возвращаемому значению из
__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_wrap__(array, context=None, return_scalar=False)
-
В конце каждого ufunc этот метод вызывается для объекта входного объекта с наивысшим приоритетом массива или объекта вывода, если он был указан. Вычисленный ufunc массив передаётся, и то, что возвращается, передаётся пользователю. Подклассы наследуют реализацию этого метода по умолчанию, которая преобразует массив в новый экземпляр класса объекта. Подклассы могут выбрать использование этого метода для преобразования выходного массива в экземпляр подкласса и обновления метаданных перед возвращением массива пользователю.
NumPy также может вызвать эту функцию без контекста из не-ufunc, чтобы сохранить информацию о подклассах.
Изменено в версии 2.0:
return_scalarтеперь передаётся либо какFalse(обычно), либо какTrue, указывая, что NumPy вернёт скаляр. Подклассы могут игнорировать значение или возвращатьarray[()], чтобы вести себя более похожим образом на NumPy.Примечание
Надеемся в конечном итоге устареть этот метод в пользу func:
__array_ufunc__для ufunc (и__array_function__для некоторых других функций, например,numpy.squeeze).
- class.__array_priority__
-
Значение этого атрибута используется для определения типа объекта, который следует вернуть в ситуациях, когда существует более одной возможности для типа Python возвращаемого объекта. Подклассы наследуют значение по умолчанию 0,0 для этого атрибута.
Примечание
Для ufunc, надеемся в конечном итоге устареть этот метод в пользу
__array_ufunc__.
- class.__array__(dtype=None, copy=None)
-
Если определён на объекте, должен вернуть
ndarray. Этот метод вызывается функциями приведения к массиву, такими как np.array(), если объекту, реализующему этот интерфейс, передаётся в эти функции. Реализации__array__сторонних разработчиков должны принимать ключевые аргументыdtypeиcopy, так как игнорирование их может нарушить код сторонних разработчиков или сам NumPy.-
dtype— тип данных возвращаемого массива. -
copy— необязательный булевый, указывающий, следует ли возвращать копию. ДляTrueкопия всегда должна быть создана, дляNoneтолько если это необходимо (например, из-за переданного значенияdtype), а дляFalseкопия никогда не должна создаваться (если копия всё же требуется, должно быть выброшено соответствующее исключение).
См. Взаимодействие с NumPy для иерархии протоколов, из которых
__array__является самым старым и наименее желательным. -
Матричные объекты
Примечание
Сильно рекомендуется не использовать подкласс матриц. Как описано ниже, это затрудняет написание функций, которые последовательно обрабатывают матрицы и обычные массивы. В настоящее время они в основном используются для взаимодействия с scipy.sparse. Мы надеемся предоставить альтернативу для этого использования и, в конечном итоге, удалить подкласс matrix.
matrix объекты наследуют от ndarray и, следовательно, обладают теми же атрибутами и методами, что и ndarray. Однако существуют шесть важных различий между матричными объектами, которые могут привести к непредсказуемым результатам, когда вы используете матрицы, но ожидаете, что они будут вести себя как массивы:
- Матричные объекты могут быть созданы с использованием строкового обозначения, что позволяет использовать синтаксис в стиле Matlab, где пробелы отделяют столбцы, а точки с запятой (';') — строки.
- Матричные объекты всегда двумерные. Это имеет далеко идущие последствия, поскольку m.ravel() все еще двумерный (с 1 в первом измерении), а выбор элементов возвращает двумерные объекты, так что поведение последовательности принципиально отличается от массивов.
- Матричные объекты переопределяют умножение для выполнения матричного умножения. Убедитесь, что вы понимаете это для функций, которые могут получать матрицы. Особенно с учетом того, что asanyarray(m) возвращает матрицу, когда m — матрица.
- Матричные объекты переопределяют возведение в степень для выполнения возведения матрицы в степень. То же предупреждение о использовании возведения в степень внутри функции, использующей asanyarray(...), чтобы получить объект массива, справедливо и в этом случае.
- Значение по умолчанию __array_priority__ для матричных объектов составляет 10,0, и поэтому смешанные операции с ndarray всегда производят матрицы.
-
Матрицы имеют специальные атрибуты, которые упрощают вычисления. Это
Предупреждение
Матричные объекты переопределяют умножение, «*», и возведение в степень, «**», для выполнения матричного умножения и возведения матрицы в степень соответственно. Если ваш подпрограмма может принимать подклассы, и вы не конвертируете в массивы базового класса, тогда вы должны использовать ufunc multiply и power, чтобы убедиться, что вы выполняете правильную операцию для всех входных данных.
Класс матриц — это подкласс Python ndarray, который может использоваться как справочник для создания собственного подкласса ndarray. Матрицы могут быть созданы из других матриц, строк и всего, что может быть преобразовано в ndarray. Имя «mat» — псевдоним для «matrix» в NumPy.
| Возвращает матрицу из объекта типа массив или из строки данных. |
| Рассматривает входные данные как матрицу. |
| Создаёт объект матрицы из строки, вложенной последовательности или массива. |
Пример 1: Создание матрицы из строки
>>> a = np.asmatrix('1 2 3; 4 5 3')
>>> print((a*a.T).I)
[[ 0.29239766 -0.13450292]
[-0.13450292 0.08187135]]
Пример 2: Создание матрицы из вложенной последовательности
>>> np.asmatrix([[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.asmatrix(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(), который должен вызываться пользователем вручную, чтобы гарантировать, что любые изменения в массиве действительно записываются на диск.
| Создает отображение в памяти массива, хранящегося в бинарном файле на диске. |
Записывает любые изменения в массив в файл на диске. |
Пример:
>>> 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)
См. также
Примечание
Класс 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 также можно создать, используя конструктор chararray или функцию numpy.char.array:
| Предоставляет удобный вид на массивы строковых и юникод-значений. |
| Создаёт |
Другое отличие от стандартного ndarray типа str заключается в том, что chararray наследует функцию, введенную Numarray, что пробелы в конце любого элемента массива будут игнорироваться при получении элемента и сравнениях.
Массивы записей
NumPy предоставляет класс recarray, который позволяет получать доступ к полям структурированного массива как к атрибутам, и соответствующий скалярный объект типа данных record.
| Создаёт массив ndarray, позволяющий получить доступ к полям с использованием атрибутов. |
Скалярный тип данных, позволяющий получать доступ к полям как к атрибутам. |
Примечание
Объект pandas DataFrame более мощный, чем массив записей. По возможности, используйте pandas DataFrame.
Маскированные массивы (numpy.ma)
См. также
Стандартный контейнерный класс
Для обеспечения обратной совместимости и в качестве стандартного «контейнерного» класса, класс UserArray из Numeric перенесён в NumPy и переименован в numpy.lib.user_array.container. Контейнерный класс — это класс Python, атрибут self.array которого является ndarray. Наследование с множественным наследованием, вероятно, проще с numpy.lib.user_array.container, чем с самим ndarray, поэтому оно включено по умолчанию. Оно не документируется здесь, кроме упоминания о его существовании, так как рекомендуется использовать класс ndarray напрямую, если это возможно.
| Стандартный контейнерный класс для простого множественного наследования. |
Итераторы массивов
Итераторы — мощная концепция для обработки массивов. По сути, итераторы реализуют обобщённый цикл 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]]
Плоское итерирование
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\)-мерное перечисление
| Итератор многомерного индекса. |
Иногда может быть полезно получить \(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
Итератор для широковещательной передачи
Создаёт объект, имитирующий широковещательную передачу. |
Общая концепция широковещательной передачи также доступна из Python с помощью итератора broadcast. Этот объект принимает \(N\) объектов в качестве входных данных и возвращает итератор, который возвращает кортежи, содержащие каждый элемент входной последовательности в результате широковещательной передачи.
>>> for val in np.broadcast([[1, 0], [2, 3]], [0, 1]): ... print(val) (np.int64(1), np.int64(0)) (np.int64(0), np.int64(1)) (np.int64(2), np.int64(0)) (np.int64(3), np.int64(1))
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/arrays.classes.html