Миграция с spmatrix на sparray#
Этот документ содержит рекомендации по преобразованию кода из разреженного матрицы
в разреженный массивы в scipy.sparse.
Переход от разреженных матриц к разреженным массивам отражает преобразование из
np.matrix to np.ndarray. По сути, мы должны перейти от ориентированной на умножение матриц 2D-модели matrix объект в 1D или 2D "массивный" объект, который поддерживает оператор матричного умножения и поэлементные вычисления.
Обозначение: В этом руководстве мы обозначаем классы разреженных массивов в общем виде как
sparray и классы разреженных матриц spmatrix. Плотные массивы numpy
обозначаются np.ndarray и классы плотных матриц являются
np.matrix. Поддерживаемые разреженные форматы обозначаются BSR, COO, CSC, CSR, DIA, DOK, LIL, и все форматы поддерживаются как sparray, так и spmatrix. Термин sparse относится к sparray или
spmatrix, в то время как dense относится к np.ndarray или
np.matrix.
Обзор и общая картина#
Имена конструкторов
*_matrix, например,csr_matrix, изменены на*_array.spmatrix
Mвсегда 2D (строки x столбцы), даже если, например,M.min(axis=0). sparrayAможет быть 1D или 2D. Скаляры NumPy возвращаются для полных (0D) редукций, т.е.M.min().Итерация по sparray даёт одномерные sparray. Итерация по spmatrix даёт двумерные строковые spmatrix.
Операторы, изменяющие поведение:
*, @, *=, @=, **Скалярное умножение, например.
5 * A, использует*, и5 @ Aне реализовано.sparrays используют
*для поэлементного умножения и@для умножения матриц, в то время как разреженные матрицы используют либо оператор*или@для умножения матриц. Можно использоватьA.multiply(B)для поэлементного умножения.Скалярные показатели степени, например.
A**2, используйте поэлементное возведение в степень для sparray и матричное возведение в степень для spmatrix. Матричное возведение в степень для sparrays используетscipy.sparse.linalg.matrix_power(A, n).
Когда функции конструкторам предоставляются индексные массивы, spmatrix выбирает dtype на основе dtype и значений входящих массивов, в то время как sparray учитывает только dtype входящих массивов. Например,
M=csr_matrix((data, indices, indptr))приводит кint32dtype дляM.indicesдо тех пор, пока значения вindicesиindptrмалы, даже еслиdtypeвходящих массивовint64. В отличие отA=csr_array((data, indices, indptr))приводит кint64dtype дляA.indicesкогда входные массивыint64. Это обеспечивает более предсказуемые, часто большие, типы данных индексов в sparrays и меньшее приведение типов для соответствия типам данных.Проверка типа и формата разреженной матрицы:
issparse(A)возвращаетTrueдля любого разреженного массива/матрицы.isspmatrix(M)возвращаетTrueдля любой разреженной матрицы.isspmatrix_csr(M)проверяет разреженную матрицу с определенным форматом. Её следует заменить на версию, совместимую с массивами, такую как:issparse(A) and A.format == 'csr'который проверяет разреженный массив/матрицу CSR.
Обработка API вашего программного пакета с разреженным вводом/выводом:
Входные данные довольно легко заставить работать как с spmatrix, так и с sparray. Пока вы используете
A.multiply(B)для поэлементных иA @ Bдля умножения матриц, и вы используетеsparse.linalg.matrix_powerдля возведения матрицы в степень, вы будете в порядке после завершения «первого прохода» шагов миграции, описанных в следующем разделе. Ваш код будет обрабатывать оба типа входных данных взаимозаменяемо.Перенос разреженных выходных данных из ваших функций требует немного больше размышлений. Составьте список всех ваших публичных функций, которые возвращают объекты spmatrix. Проверьте, считаете ли вы допустимым возвращать вместо них sparray. Это зависит от вашей библиотеки и её пользователей. Если вы хотите разрешить этим функциям продолжать возвращать объекты spmatrix или sparray, вы часто можете сделать это, используя разреженный вход, который также служит сигналом для того, какой тип выходных данных должен быть возвращён. Спроектируйте вашу функцию так, чтобы она возвращала тип, который был на входе. Этот подход можно расширить на плотные входные данные. Если вход — это np.matrix или маскированный массив с np.matrix в качестве его
._baseclassатрибут, затем вернуть spmatrix. В противном случае вернуть sparray. Без этих входных данных два других подхода — создать ключевой аргумент для указания, что возвращать, или создать новую функцию (как мы сделали, например,eye_array), который имеет тот же базовый синтаксис, но возвращает sparray. Какой метод вы выберете, должно зависеть от вашей библиотеки, ваших пользователей и ваших предпочтений.
Рекомендуемые шаги для миграции#
Первый проход (оставляя spmatrix в коде):
В вашем коде spmatrix измените
*to@для умножения матриц. Заметим, что скалярное умножение с разреженными матрицами должно использовать*. (См. helper-code Используйте тесты для поиска * и ** мест ниже)Степени матрицы, например.
M**3, должен быть преобразован вscipy.sparse.linalg.matrix_power(A, 3)Реализовать альтернативы неподдерживаемым функциям/методам, таким как
A.getnnz()->A.nnz(см. Удалённые методы и атрибуты ниже).Измените любую логику относительно
issparse()иisspmatrix()по мере необходимости. Обычно это означает заменуisspmatrixсissparse, иisspmatrix_csr(G)сissparse(G) and G.format == "csr"Более тогоisspmatrix_csr(G) or isspmatrix_csc(G)становитсяissparse(G) and G.format in ['csr', 'csc']. Идиома поиска в gitgit grep 'isspm[a-z_]*('может помочь найти их.Преобразовать все
spdiagsвызовы кdia_matrix. См. документацию вspdiags. Поискspdiagsэто все, что вам нужно здесь.Запустите все тесты на получившемся коде. Вы все еще используете spmatrix, а не sparray. Но ваш код и тесты подготовлены к изменению, и вы должны иметь возможность принимать sparrays на вход вашего кода, и они в основном будут 'просто работать'.
Второй проход (переключение на sparray):
Преобразовать функции построения, такие как
diagsиtriuверсии массива (см. Подробности: функции построения ниже).Переименовать все
*_matrixвызовы конструктора*_array.Проверьте все функции/методы, для которых миграция приводит к 1D возвращаемым значениям. В основном это индексирование и функции редукции (см. Подробности: изменения формы и редукции ниже).
Проверьте все места, где вы итерируете по spmatrices, и измените их, чтобы учесть, что sparrays возвращают одномерные sparrays, а не двумерные spmatrices.
Найдите и измените места, где ваш код использует
np.matrixособенности. Преобразовать их вnp.ndarrayпризнаков.Если ваш код читает разреженные данные из файлов с
mmread,hb_readилиloadmat, используйте новый аргумент ключевого словаspmatrix=Falseв тех функциях для чтения в sparray.Если вы используете разреженные библиотеки, которые принимают только
int32индексные массивы для разреженных представлений мы предлагаем использовать преобразование по требованию. Преобразовать вint32непосредственно перед вызовом кода, который требуетint32.sparrayвыбирает тип данных индекса на основе типа данных входного массива, а не значений в массиве. Поэтому, если вы хотите, чтобы ваши индексные массивы былиint32, вам потребуется обеспечитьint32dtype для каждого массива индексов, какindptrкоторый вы передаетеcsr_array. Сspmatrixзаманчиво использовать тип данных int64 по умолчанию дляnumpyмассивы и полагаются на разреженный конструктор для понижения типа данных, если значения были малы. Но это понижение приводит к дополнительному переприведению типов при работе с другими матрицами, срезами или арифметическими выражениями. Дляsparrayвы все еще можете полагаться на конструкторы для выбора типов данных. Но вам также предоставлена возможность выбрать тип данных индекса через тип данных входящих массивов индексов, а не их значений. Так что, если вы хотитеint32, установите dtype, например.indices = np.array([1,3,6], dtype=np.int32)илиindptr = np.arange(9, dtype=np.int32), при создании индексных массивов. См. Типы данных индексных массивов ниже для дополнительной информации. Во многих случаях тип данных индексного массива не критичен, и можно просто позволить конструкторам выбирать тип данных как для sparray, так и для spmatrix.Протестируйте свой код. И читать ваш код. Вы перешли на sparray.
Подробности: функции построения#
Эти четыре функции новые и работают только с sparrays:
block_array, diags_array,
eye_array, и random_array.
Их сигнатуры:
def block_array(blocks, format=None, dtype=None):
def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None):
def eye_array(m, n=None, *, k=0, dtype=float, format=None):
def random_array(shape, density=0.01, format='coo', dtype=None, rng=None, data_sampler=None):
The random_array функция имеет shape (2-кортеж) arg вместо двух целых чисел. И rng аргумент по умолчанию соответствует новому в NumPy default_rng().
Это отличается от spmatrix rand и random которые по умолчанию используют
глобальный экземпляр RandomState. Если вам не важны эти детали,
оставление значения по умолчанию должно работать нормально. Если вы заботитесь о посеве
случайных чисел, вам, вероятно, следует добавить rng=... keyword argument
к этому вызову при переключении функций. Вкратце, для перехода на random_array
измените имя функции, замените аргумент shape на один аргумент-кортеж, оставьте любые другие параметры как прежде и подумайте, какой тип rng= аргумент должен использоваться, если есть.
The diags_array функция использует правила только для ключевых слов аргументов. Поэтому вы должны вводить offsets= перед аргументами смещений. Это кажется неудобным при переходе от использования diags, но это помогает избежать путаницы и облегчает чтение. Единый параметр формы заменяет два целых числа для этой миграции.
Существующие функции, требующие осторожной миграции#
Эти функции возвращают sparray или spmatrix, в зависимости от типов входных данных, которые они получают: kron, kronsum,
hstack, vstack,
block_diag, tril, и
triu. Их сигнатуры:
def kron(A, B, format=None):
def kronsum(A, B, format=None):
def hstack(blocks, format=None, dtype=None):
def vstack(blocks, format=None, dtype=None):
def block_diag(mats, format=None, dtype=None):
def tril(A, k=0, format=None):
def triu(A, k=0, format=None):
Использование этих функций должно быть проверено, а входные данные скорректированы, чтобы гарантировать возврат sparrays. В свою очередь, выходные данные должны рассматриваться как sparrays. Чтобы возвращать sparrays, хотя бы один входной аргумент должен быть sparray. Если вы используете списки списков или массивы numpy в качестве входных данных, вам следует преобразовать один из них в разреженный массив, чтобы получить разреженные массивы на выходе.
Функции, изменившие названия при миграции#
Функция
Новая функция
Комментарии
eye
eye_array
идентичность
eye_array
diags
diags_array
ввод только по ключевым словам
spdiags
dia_array
форма как 2-кортеж
bmat
блок
rand
random_array
форма как 2-кортеж и default_rng
random
random_array
форма как 2-кортеж и default_rng
Подробности: изменения формы и редукции#
Построение с использованием одномерного списка значений:
csr_array([1, 2, 3]).shape == (3,)1D вход создаёт 1D массив.csr_matrix([1, 2, 3]).shape == (1, 3)1D вход создает 2D матрицу.
Индексирование и итерация:
Индексирование sparray допускает 1D объекты, которые могут быть преобразованы в 2D с помощью
np.newaxisилиNone. Например,A[3, None, :]даёт 2D строку. Индексация 2D sparray с неявным (не заданным) индексом столбца даёт 1D результат, например.A[3](примечание: лучше не делать этого — записать это какA[3, :]вместо этого). Если вам нужен 2D результат, используйтеnp.newaxis, илиNoneв вашем индексе, или оберните целочисленный индекс в список, для которого причудливая индексация дает 2D, например,A[[3], :].Итерация по разреженному объекту:
next(M)возвращает разреженную двумерную матрицу-строку,next(A)возвращает разреженный одномерный массив.
Операции редукции вдоль оси уменьшают размерность:
M.min(axis=1)возвращает двумерную строковую матрицу минимумов вдоль оси 1.A.min(axis=1)возвращает одномерныйcoo_arrayминимума вдоль оси 1. Некоторые операции редукции возвращают плотные массивы/матрицы вместо разреженных:Метод
Результат
sum(axis)
dense
mean(axis)
dense
argmin(axis)
dense
argmax(axis)
dense
min(axis)
разреженный
max(axis)
разреженный
nanmin(axis)
разреженный
nanmax(axis)
разреженный
Обычно двумерные разреженные массивы (sparray) приводят к одномерным результатам. Двумерные разреженные матрицы (spmatrix) приводят к двумерным результатам.
Некоторые редукции возвращают скаляр. Они должны вести себя как раньше и не должны учитываться при миграции. Например,
A.min()
Удалённые методы и атрибуты#
Методы
get_shape,getrow,getcol,asfptype,getnnz,getHи атрибуты.Aи.Hприсутствуют только на spmatrices, не на sparrays. Рекомендуется заменить их использование альтернативами перед началом перехода на sparray.Функция
Альтернативный
M.get_shape()
A.shape
M.getformat()
A.format
M.asfptype(…)
A.astype(…)
M.getmaxprint()
A.maxprint
M.getnnz()
A.nnz
M.getnnz(axis)
A.count_nonzero(axis)
M.getH()
A.conj().T
M.getrow(i)
A[i, :]
M.getcol(j)
A[:, j]
M.A
A.toarray()
M.H
A.conj().T
Назначение формы (
M.shape = (2, 6)) не разрешён для sparray. Вместо этого следует использоватьA.reshape.M.getnnz()возвращает количество хранимых значений – не количество ненулевых элементов.A.nnzделает то же самое. Чтобы получить количество ненулевых элементов, используйтеA.count_nonzero(). Это не ново для миграции, но может сбивать с толку.Для перехода с
axisпараметрM.getnnz(axis=...), вы можете использоватьA.count_nonzero(axis=...)но это не точная замена, потому что она подсчитывает ненулевые значения вместо сохраненных значений. Разница заключается в количестве явно сохраненных нулевых значений. Если вам действительно нужно количество сохраненных значений по оси, вам потребуется использовать некоторые инструменты numpy.Подход с использованием инструментов numpy работает для форматов COO, CSR, CSC, поэтому преобразуйте в один из них. Для CSR и CSC основная ось сжимается и
np.diff(A.indptr)возвращает плотный 1D массив с количеством сохранённых значений для каждого основного значения оси (строка для CSR и столбец для CSC). Второстепенные оси можно вычислить с помощьюnp.bincount(A.indices, minlength=N)гдеN— это длина малой оси (например,A.shape[1]для CSR).bincountфункция работает для любой оси формата COO с использованиемA.coords[axis]вместоA.indices.
Используйте тесты для поиска * и ** мест#
Может быть сложно отличить скалярное умножение
*из матричного умножения*при миграции вашего кода. Python решил эту проблему, в теории, введя оператор матричного умножения@.*используется для скалярного умножения, в то время как@для матричного умножения. Но преобразование выражений, использующих*для обоих может быть сложным и вызывать напряжение глаз. К счастью, если ваш код имеет набор тестов, который покрывает выражения, которые вам нужно преобразовать, вы можете использовать его для поиска мест, где*используется для матричного умножения с разреженными матрицами. Измените их на@.Подход изменяет методы класса spmatrix на лету, чтобы вызывать исключение, когда
*используется для матричного умножения (и не вызывает ошибку при скалярном умножении). Тестовый набор будет отмечать сбой в этих местах. И неудачный тест здесь является успехом, потому что он показывает, где вносить изменения. Измените проблемный*to@, посмотрите рядом на другие похожие изменения и снова запустите тесты. Аналогично, этот подход помогает найти, где**используется для возведения матрицы в степень. SciPy вызывает исключение, когда@используется для скалярного умножения, так что это позволит обнаружить места, где вы изменяете то, что не должны были. Таким образом, тестовый набор с этой обезьяньей заплаткой также проверяет исправления.Добавьте следующий код в ваш
conftest.pyфайл. Затем запустите тесты локально. Если есть много матричных выражений, возможно, вы захотите протестировать одну часть вашей кодовой базы за раз. Быстрое чтение кода показывает, что он вызываетValueErrorвсякий раз, когда*используется между двумя матрично-подобными объектами (разреженными или плотными), и всякий раз, когда**используется для возведения матрицы в степень. Также выдает предупреждение при использовании sum/mean/min/max/argmin/argmax с осью, так что вывод будет 2D для spmatrix и 1D для sparray. Это означает, что вы проверяете, что код обрабатывает вывод как 1D, так и 2D черезflatten/ravel,np.atleast_2dили индексирование.#================== Added to check spmatrix usage ======================== import scipy from warnings import warn def flag_this_call(*args, **kwds): raise ValueError("Old spmatrix function names for rand/spdiags called") scipy.sparse._construct.rand = flag_this_call scipy.sparse._construct.spdiags = flag_this_call class _strict_mul_mixin: def __mul__(self, other): if not scipy.sparse._sputils.isscalarlike(other): raise ValueError('Operator * used here! Change to @?') return super().__mul__(other) def __rmul__(self, other): if not scipy.sparse._sputils.isscalarlike(other): raise ValueError('Operator * used here! Change to @?') return super().__rmul__(other) def __imul__(self, other): if not scipy.sparse._sputils.isscalarlike(other): raise ValueError('Operator * used here! Change to @?') return super().__imul__(other) def __pow__(self, *args, **kwargs): raise ValueError('spmatrix ** found! Use linalg.matrix_power?') @property def A(self): raise TypeError('spmatrix A property found! Use .toarray()') @property def H(self): raise TypeError('spmatrix H property found! Use .conjugate().T') def asfptype(self): raise TypeError('spmatrix asfptype found! rewrite needed') def get_shape(self): raise TypeError('spmatrix get_shape found! Use .shape') def getformat(self): raise TypeError('spmatrix getformat found! Use .format') def getmaxprint(self): raise TypeError('spmatrix getmaxprint found! Use .shape') def getnnz(self): raise TypeError('spmatrix getnnz found! Use .nnz') def getH(self): raise TypeError('spmatrix getH found! Use .conjugate().T') def getrow(self): raise TypeError('spmatrix getrow found! Use .row') def getcol(self): raise TypeError('spmatrix getcol found! Use .col') def sum(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix sum found using axis={axis}. " "\nsparray with a single axis will produce 1D output. " "\nCheck nearby to ensure 1D output is handled OK in this spot.\n") print(f"{args=} {axis=} {kwds=}") return super().sum(*args, **kwds) def mean(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix mean found using axis={axis}." "\nsparray with a single axis will produce 1D output.\n" "Check nearby to ensure 1D output is handled OK in this spot.\n") return super().mean(*args, **kwds) def min(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix min found using axis={axis}." "\nsparray with a single axis will produce 1D output. " "Check nearby to ensure 1D output is handled OK in this spot.\n") return super().min(*args, **kwds) def max(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix max found using axis={axis}." "\nsparray with a single axis will produce 1D output. " "Check nearby to ensure 1D output is handled OK in this spot.\n") return super().max(*args, **kwds) def argmin(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix argmin found using axis={axis}." "\nsparray with a single axis will produce 1D output. " "Check nearby to ensure 1D output is handled OK in this spot.\n") return super().argmin(*args, **kwds) def argmax(self, *args, **kwds): axis = args[0] if len(args)==1 else args if args else kwds.get("axis", None) if axis is not None: warn(f"\nMIGRATION WARNING: spmatrix argmax found using axis={axis}." "\nsparray with a single axis will produce 1D output. " "Check nearby to ensure 1D output is handled OK in this spot.\n") return super().argmax(*args, **kwds) class coo_matrix_strict(_strict_mul_mixin, scipy.sparse.coo_matrix): pass class bsr_matrix_strict(_strict_mul_mixin, scipy.sparse.bsr_matrix): pass class csr_matrix_strict(_strict_mul_mixin, scipy.sparse.csr_matrix): pass class csc_matrix_strict(_strict_mul_mixin, scipy.sparse.csc_matrix): pass class dok_matrix_strict(_strict_mul_mixin, scipy.sparse.dok_matrix): pass class lil_matrix_strict(_strict_mul_mixin, scipy.sparse.lil_matrix): pass class dia_matrix_strict(_strict_mul_mixin, scipy.sparse.dia_matrix): pass scipy.sparse.coo_matrix = scipy.sparse._coo.coo_matrix = coo_matrix_strict scipy.sparse.bsr_matrix = scipy.sparse._bsr.bsr_matrix = bsr_matrix_strict scipy.sparse.csr_matrix = scipy.sparse._csr.csr_matrix = csr_matrix_strict scipy.sparse.csc_matrix = scipy.sparse._csc.csc_matrix = csc_matrix_strict scipy.sparse.dok_matrix = scipy.sparse._dok.dok_matrix = dok_matrix_strict scipy.sparse.lil_matrix = scipy.sparse._lil.lil_matrix = lil_matrix_strict scipy.sparse.dia_matrix = scipy.sparse._dia.dia_matrix = dia_matrix_strict scipy.sparse._compressed.csr_matrix = csr_matrix_strict scipy.sparse._construct.bsr_matrix = bsr_matrix_strict scipy.sparse._construct.coo_matrix = coo_matrix_strict scipy.sparse._construct.csc_matrix = csc_matrix_strict scipy.sparse._construct.csr_matrix = csr_matrix_strict scipy.sparse._construct.dia_matrix = dia_matrix_strict scipy.sparse._extract.coo_matrix = coo_matrix_strict scipy.sparse._matrix.bsr_matrix = bsr_matrix_strict scipy.sparse._matrix.coo_matrix = coo_matrix_strict scipy.sparse._matrix.csc_matrix = csc_matrix_strict scipy.sparse._matrix.csr_matrix = csr_matrix_strict scipy.sparse._matrix.dia_matrix = dia_matrix_strict scipy.sparse._matrix.dok_matrix = dok_matrix_strict scipy.sparse._matrix.lil_matrix = lil_matrix_strict del coo_matrix_strict del bsr_matrix_strict del csr_matrix_strict del csc_matrix_strict del dok_matrix_strict del lil_matrix_strict del dia_matrix_strict #==========================================
Типы данных индексных массивов#
Если вы предоставляете сжатые индексы конструктору,
например, csr_array((data, indices, indptr)) разреженные массивы устанавливают dtype индекса, проверяя только dtype массивов индексов, в то время как разреженные матрицы проверяют также значения индексов и могут понизить до int32 (см. gh-18509 для более подробной информации).
Это означает, что вы можете получить индексацию int64 там, где раньше получали int32.
Вы можете управлять этим, установив dtype перед созданием экземпляра, или путём
переприведения после построения.
Две разреженные служебные функции могут помочь с обработкой типа индекса.
Используйте get_index_dtype(arrays, maxval, check_contents) при создании индексов
для поиска подходящего типа данных (int32 или int64) для использования в ваших сжатых индексах.
Используйте safely_cast_index_arrays(A, idx_dtype) для преобразования после создания, обеспечивая отсутствие переполнений при понижении типа. Эта функция не изменяет входной массив. Преобразованные массивы возвращаются. Копии создаются только при необходимости, поэтому можно проверить, было ли выполнено преобразование, используя
if indices is not A.indices:
Сигнатуры функций:
def get_index_dtype(arrays=(), maxval=None, check_contents=False):
def safely_cast_index_arrays(A, idx_dtype=np.int32, msg=""):
Примеры идиом включают следующие для get_index_dtype:
.. code-block:: python
# select index dtype before construction based on shape
shape = (3, 3)
idx_dtype = scipy.sparse.get_index_dtype(maxval=max(shape))
indices = np.array([0, 1, 0], dtype=idx_dtype)
indptr = np.arange(3, dtype=idx_dtype)
A = csr_array((data, indices, indptr), shape=shape)
и для safely_cast_index_arrays:
.. code-block:: python
# rescast after construction, raising exception if shape too big
indices, indptr = scipy.sparse.safely_cast_index_arrays(B, np.int32)
B.indices, B.indptr = indices, indptr
Другие#
Бинарные операторы
+, -, *, /, @, !=, >работать с разреженными и/или плотными операндами:Если все входные данные разрежены, выходные данные обычно также разрежены. Исключением является
/которая возвращает плотную матрицу (делением на значение по умолчанию0являетсяnan).Если входные данные смешанные (разреженные и плотные), результат обычно плотный (т.е.,
np.ndarray). Исключениями являются*который является разреженным, и/который не реализован дляdense / sparse, и возвращает разреженную дляsparse / dense.
Бинарные операторы
+, -, *, /, @, !=, >с операндами массива и/или матрицы:Если все входные данные являются массивами, выходные данные также являются массивами, и то же самое верно для матриц.
При смешивании разреженных массивов с разреженными матрицами, ведущий операнд определяет тип вывода, например,
sparray + spmatrixдает разреженный массив, в то время как обратный порядок дает разреженную матрицу.При смешивании плотных матриц с разреженными массивами результаты обычно являются массивами, за исключением сравнений, например.
>которые возвращают плотные матрицы.При смешивании плотных массивов с разреженными матрицами, результаты обычно являются матрицами, за исключением
array @ sparse matrixкоторый возвращает плотный массив.