Стандартные подклассы массивов#
Примечание
Наследование от numpy.ndarray возможно, но если ваша цель — создать массив с изменено поведение, как и массивы dask для распределённых вычислений и массивы cupy для вычислений на GPU, наследование не рекомендуется. Вместо этого используйте
механизм диспетчеризации рекомендуется.
The ndarray может быть унаследован (в Python или C), если это необходимо. Поэтому он может служить основой для многих полезных классов. Часто выбор между созданием подкласса объекта массива или простым использованием основного компонента массива как внутренней части нового класса является сложным решением и может быть просто вопросом выбора. NumPy имеет несколько инструментов для упрощения взаимодействия вашего нового объекта с другими объектами массивов, поэтому выбор может не быть значимым в конечном итоге. Один из способов упростить вопрос — спросить себя, может ли объект, который вас интересует, быть заменён одним массивом или ему действительно требуется два или более массива в своей основе.
Обратите внимание, что asarray всегда возвращает базовый класс ndarray. Если вы уверены, что ваше использование объекта массива может обрабатывать любой подкласс ndarray, то asanyarray может использоваться для того, чтобы
подклассы могли более чисто распространяться через вашу подпрограмму. В
принципе, подкласс может переопределить любой аспект массива и,
следовательно, согласно строгим правилам, asanyarray редко будет
полезным. Однако большинство подклассов объекта массива не будут
переопределять определённые аспекты объекта массива, такие как интерфейс буфера или атрибуты массива. Один важный пример,
однако, почему ваша подпрограмма может не обрабатывать произвольный
подкласс массива, заключается в том, что матрицы переопределяют оператор "*" как
матричное умножение, а не поэлементное умножение.
Специальные атрибуты и методы#
Смотрите также
NumPy предоставляет несколько хуков, которые классы могут настраивать:
- класс.__array_ufunc__(универсальная функция (ufunc), метод, *входные данные, **kwargs)#
Любой класс, подкласс ndarray или нет, может определить этот метод или установить его в None, чтобы переопределить поведение универсальных функций NumPy. Это работает аналогично
__mul__и другие процедуры бинарных операций.универсальная функция (ufunc) является объектом ufunc, который был вызван.
метод — это строка, указывающая, какой метод Ufunc был вызван (один из
"__call__","reduce","reduceat","accumulate","outer","inner").входные данные является кортежем входных аргументов для
ufunc.kwargs это словарь, содержащий необязательные входные аргументы ufunc. Если задан, любой
outаргументы, как позиционные, так и ключевые, передаются какtupleв kwargs. См. обсуждение в Универсальные функции (ufunc) подробности.
Метод должен возвращать либо результат операции, либо
NotImplementedесли запрошенная операция не реализована.Если один из входных, выходных или
whereаргументы имеют__array_ufunc__метод, он выполняется вместо универсальной функции. Если более одного из аргументов реализует__array_ufunc__, они пробуются в порядке: подклассы перед суперклассами, входы перед выходами, выходы передwhere, в противном случае слева направо. Первая процедура, возвращающая что-либо отличное отNotImplementedопределяет результат. Если все__array_ufunc__операции возвращаютNotImplemented, aTypeErrorвызывается исключение.Примечание
Мы планируем перереализовать функции numpy как (обобщённые) Ufunc, в этом случае станет возможным их переопределение с помощью
__array_ufunc__метод. Основной кандидат —matmul, который в настоящее время не является Ufunc, но может быть относительно легко переписан как (набор) обобщённых Ufuncs. То же может произойти с функциями, такими какmedian,amin, иargsort.Как и с некоторыми другими специальными методами в Python, такими как
__hash__и__iter__, возможно указать, что ваш класс не не поддержка ufuncs путем установки__array_ufunc__ = None. Универсальные функции всегда вызываютTypeErrorпри вызове на объекте, который устанавливает__array_ufunc__ = None.Наличие
__array_ufunc__также влияет на то, какndarrayобрабатывает бинарные операции, такие какarr + objиarr < objкогдаarrявляетсяndarrayиobjявляется экземпляром пользовательского класса. Есть две возможности. Еслиobj.__array_ufunc__присутствует и не равен None, тогдаndarray.__add__и аналогичные функции будут делегировать механизму универсальных функций, означая, чтоarr + objстановитсяnp.add(arr, obj), а затемaddвызываетobj.__array_ufunc__. Это полезно, если вы хотите определить объект, который ведёт себя как массив.В качестве альтернативы, если
obj.__array_ufunc__установлено в None, тогда как особый случай, специальные методы, такие какndarray.__add__заметит это и безусловно raiseTypeError. Это полезно, если вы хотите создавать объекты, которые взаимодействуют с массивами через бинарные операции, но сами не являются массивами. Например, система обработки единиц измерения может иметь объект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__которые делегируют ufuncs, как это делает ndarray. Простой способ сделать это — создать подкласс отNDArrayOperatorsMixin.Если вы создаёте подкласс
ndarray, мы рекомендуем поместить всю вашу логику переопределения в__array_ufunc__и не переопределять специальные методы. Это гарантирует, что иерархия классов определяется только в одном месте, а не отдельно механизмом ufunc и правилами бинарных операций (которые отдают предпочтение специальным методам подклассов; альтернативный способ обеспечить иерархию только в одном месте — установка__array_ufunc__в None, казалось бы очень неожиданным и, следовательно, запутанным, так как тогда подкласс вообще не будет работать с ufuncs).ndarrayопределяет свой собственный__array_ufunc__, который, вычисляет ufunc, если нет переопределений аргументов, и возвращаетNotImplementedв противном случае. Это может быть полезно для подклассов, для которых__array_ufunc__преобразует любые экземпляры своего класса вndarray: он может затем передать их в свой суперкласс с помощьюsuper().__array_ufunc__(*inputs, **kwargs), и наконец возвращает результаты после возможного обратного преобразования. Преимущество этой практики в том, что она гарантирует возможность иметь иерархию подклассов, расширяющих поведение. См. Наследование от ndarray подробности.
- класс.__array_function__(функция, типы, args, kwargs)#
func— это произвольный вызываемый объект, доступный через публичный API NumPy, который был вызван в формеfunc(*args, **kwargs).typesявляется коллекциейcollections.abc.Collectionуникальных типов аргументов из исходного вызова функции NumPy, которые реализуют__array_function__.Кортеж
argsи dictkwargsнепосредственно передаются из исходного вызова.
В качестве удобства для
__array_function__реализаторов,typesпредоставляет все типы аргументов с'__array_function__'атрибут. Это позволяет разработчикам быстро идентифицировать случаи, когда они должны делегировать__array_function__реализации для других аргументов. Реализации не должны полагаться на порядок итерацииtypes.Большинство реализаций
__array_function__начнёт с двух проверок:Является ли данная функция чем-то, что мы знаем, как перегрузить?
Все ли аргументы имеют тип, который мы умеем обрабатывать?
Если эти условия выполняются,
__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__на первый аргумент каждого уникального типа. Это соответствует правило вызова отраженных методов, и это гарантирует, что проверка перегрузок имеет приемлемую производительность даже при большом количестве перегруженных аргументов.
- класс.__array_finalize__(obj)#
Этот метод вызывается всякий раз, когда система внутренне выделяет новый массив из obj, где obj является подклассом (подтипом)
ndarray. Это может использоваться для изменения атрибутов self после создания (чтобы обеспечить, например, 2-мерную матрицу), или для обновления метаинформации от "родителя". Подклассы наследуют реализацию по умолчанию этого метода, которая ничего не делает.
- класс.__array_wrap__(массив, контекст=None, return_scalar=False)#
В конце каждого универсальная функция (ufunc), этот метод вызывается для входного объекта с наивысшим приоритетом массива или выходного объекта, если он был указан. Массив, вычисленный ufunc, передается, и то, что возвращается, передается пользователю. Подклассы наследуют реализацию этого метода по умолчанию, которая преобразует массив в новый экземпляр класса объекта. Подклассы могут использовать этот метод для преобразования выходного массива в экземпляр подкласса и обновления метаданных перед возвратом массива пользователю.
NumPy также может вызывать эту функцию без контекста из не-ufunc, чтобы сохранить информацию о подклассе.
Изменено в версии 2.0:
return_scalarтеперь передается как либоFalse(обычно) илиTrueуказывая, что NumPy вернет скаляр. Подклассы могут игнорировать значение или возвращатьarray[()]вести себя больше как NumPy.Примечание
Надеемся, что в конечном итоге этот метод будет устаревшим в пользу
__array_ufunc__для ufuncs (и__array_function__для нескольких других функций, таких какnumpy.squeeze).
- класс.__array_priority__#
Значение этого атрибута используется для определения типа объекта, который следует возвращать в ситуациях, когда существует более одной возможности для типа Python возвращаемого объекта. Подклассы наследуют значение по умолчанию 0.0 для этого атрибута.
Примечание
Для ufuncs планируется в конечном итоге устаревание этого метода в пользу
__array_ufunc__.
- класс.__array__(dtype=None, copy=None)#
Если определён для объекта, он должен возвращать NumPy
ndarray. Этот метод вызывается функциями приведения массива, такими какnp.array()если объект, реализующий этот интерфейс, передаётся в эти функции.Сторонние реализации
__array__должен приниматьdtypeиcopyаргументы.Устарело с версии NumPy: 2.0 Не реализуется
copyиdtypeустарел с NumPy 2. При их добавлении вы должны обеспечить корректное поведение дляcopy.dtypeэто запрошенный тип данных возвращаемого массива и передаётся NumPy позиционно (только если запрошено пользователем). Допустимо игнорироватьdtypeпотому что NumPy проверит результат и приведёт кdtypeпри необходимости. Если более эффективно привести данные к запрошенному dtype без использования NumPy, вы должны обработать это в своей библиотеке.copyявляется логическим значением, передаваемым по ключевому слову. Еслиcopy=Trueвы должен возвращает копию. Возврат представления в существующих данных приведёт к некорректному коду пользователя. Еслиcopy=Falseпользователь запросил, чтобы копия никогда не создавалась, и вы должен вызывает ошибку, если не создается копия и возвращаемый массив является представлением существующих данных. Допустимо всегда вызывать ошибку дляcopy=False. По умолчаниюcopy=None(не передано) позволяет результату быть либо представлением, либо копией. Однако возврат представления следует предпочитать, когда это возможно.
Пожалуйста, обратитесь к Совместимость с NumPy для иерархии протоколов, из которых
__array__является самым старым и наименее желательным.Примечание
Если класс (подкласс ndarray или нет), имеющий
__array__метод используется как выходной объект для универсальная функция (ufunc), результаты будут не будет записано в объект, возвращенный__array__. Эта практика вернётTypeError.
объекты Matrix#
Примечание
Настоятельно рекомендуется не использовать подкласс matrix. Как описано
ниже, это затрудняет написание функций, которые последовательно работают с матрицами
и обычными массивами. В настоящее время они в основном используются для
взаимодействия с scipy.sparse. Мы надеемся предоставить альтернативу для этого использования, однако, и в конечном итоге удалить matrix подкласс.
matrix объекты наследуются от ndarray и, следовательно, имеют те же атрибуты и методы, что и ndarrays. Однако есть шесть важных отличий объектов matrix, которые могут привести к неожиданным результатам, когда вы используете матрицы, но ожидаете, что они будут вести себя как массивы:
Объекты Matrix можно создавать с использованием строковой нотации для разрешения синтаксиса в стиле Matlab, где пробелы разделяют столбцы, а точки с запятой (';') разделяют строки.
Объекты матриц всегда двумерные. Это имеет далеко идущие последствия: m.ravel() остается двумерным (с 1 в первом измерении), и выбор элементов возвращает двумерные объекты, так что поведение последовательности принципиально отличается от массивов.
Объекты Matrix переопределяют умножение для матричного умножения. Убедитесь, что вы понимаете это для функций, которые могут получать матрицы. Особенно в свете того, что asanyarray(m) возвращает матрицу, когда m является матрицей.
Объекты матрицы переопределяют возведение в степень для возведения матрицы в степень. То же предупреждение об использовании power внутри функции, которая использует asanyarray(…) для получения объекта массива, справедливо и для этого факта.
Значение __array_priority__ по умолчанию для объектов матрицы равно 10.0, поэтому смешанные операции с ndarrays всегда производят матрицы.
Матрицы имеют специальные атрибуты, которые упрощают вычисления. Это
Предупреждение
Объекты Matrix переопределяют умножение, '*', и возведение в степень, '**', чтобы они были матричным умножением и матричной степенью соответственно. Если ваша подпрограмма может принимать подклассы и вы не преобразуете в массивы базового класса, то вы должны использовать ufuncs multiply и power, чтобы быть уверенным, что выполняете правильную операцию для всех входных данных.
Класс matrix — это подкласс Python для ndarray и может использоваться
в качестве примера того, как создать свой собственный подкласс ndarray.
Матрицы могут быть созданы из других матриц, строк и всего,
что можно преобразовать в ndarray . Имя "mat" является псевдонимом для "matrix" в NumPy.
|
Возвращает матрицу из объекта, подобного массиву, или из строки данных. |
|
Интерпретировать входные данные как матрицу. |
|
Построение объекта матрицы из строки, вложенной последовательности или массива. |
Пример 1: Создание матрицы из строки
>>> import numpy as np
>>> a = np.asmatrix('1 2 3; 4 5 3')
>>> print((a*a.T).I)
[[ 0.29239766 -0.13450292]
[-0.13450292 0.08187135]]
Пример 2: Создание матрицы из вложенной последовательности
>>> import numpy as np
>>> 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: Создание матрицы из массива
>>> import numpy as np
>>> 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() который
должен быть вызван вручную пользователем, чтобы гарантировать, что любые изменения в
массиве действительно записываются на диск.
|
Создать отображение памяти на массив, хранящийся в бинарный файл на диске. |
Записать любые изменения в массиве в файл на диске. |
Пример:
>>> import numpy as np
>>> 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)#
Смотрите также
Примечание
The 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, что пробелы в конце любого элемента массива будут игнорироваться при извлечении элементов и операциях сравнения.
Записывающие массивы#
Смотрите также
Создание массивов записей, Подпрограммы типов данных, Объекты типа данных (dtype).
NumPy предоставляет recarray класс, который позволяет обращаться к
полям структурированного массива как к атрибутам, и соответствующий
скалярный тип данных объекта record.
|
Создать ndarray, который позволяет обращаться к полям через атрибуты. |
|
Скаляр типа данных, который позволяет обращаться к полям как к атрибутам. |
Примечание
DataFrame pandas мощнее, чем массив записей. По возможности используйте DataFrame pandas.
Маскированные массивы (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.
>>> import numpy as np
>>> 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]]
Плоская итерация#
Одномерный итератор по массиву. |
Как упоминалось ранее, атрибут flat объектов ndarray возвращает итератор, который будет перебирать весь массив в порядке C-стиля непрерывности.
>>> import numpy as np
>>> 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 может этого достичь.
>>> import numpy as np
>>> 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
Итератор для трансляции#
|
Создать объект, имитирующий вещание. |
Общая концепция трансляции (broadcasting) также доступна в Python с использованием broadcast итератор. Этот объект принимает \(N\)
объекты в качестве входных данных и возвращает итератор, который возвращает кортежи,
предоставляющие каждый из элементов входной последовательности в широковещательном
результате.
>>> import numpy as np
>>> 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))