我有一个稀疏数组,说:
from scipy import sparse
a = sparse.lil_matrix((2,3),)
a[0] = [1, 2, 3]
a[1, 2] = 5
所以看起来像:
(0, 0) 1.0
(0, 1) 2.0
(0, 2) 3.0
(1, 2) 5.0
我想知道 - 有没有一种简单的方法来翻转行(类似于
numpy.fliplr
等价物)? ...所以我得到的输出为:
(0, 0) 3.0
(0, 1) 2.0
(0, 2) 1.0
(1, 0) 5.0
一种方法是将数组转换为
csr
格式,然后操作行索引:
from scipy import sparse
a = sparse.lil_matrix((2,3),)
a[0] = [1, 2, 3]
a[1, 2] = 5
a = a.tocsr()
a.indices = -a.indices + a.shape[1] - 1
print(a)
产量
(0, 2) 1.0
(0, 1) 2.0
(0, 0) 3.0
(1, 0) 5.0
您可以进行多索引分配:
ii = [2,1,0,3]
a[:,1] = a[ii,:]
其中
ii
是您必须以某种方式创建的索引数组。
我知道这是一个老问题,但我仍然会添加一个答案: 如果之后不需要更改稀疏结构,则 user3232321 的
csr_matrix
方法非常有效。对于这些矩阵来说,改变稀疏结构是相当昂贵的,而且切片的可能性也非常有限。lil_matrix
格式(列表列表)适合稀疏结构可能仍会发生变化或需要对单个元素进行索引的用例。object
### Imports ###
from typing import Optional, Tuple, Union
import numpy as np
from scipy import sparse as sps
### Type Aliases ###
_Axis = Union[int, Tuple[int], Tuple[int, int]]
### Functions ###
def flip_lil_matrix(matrix: Optional[sps.lil_matrix], *, axis: _Axis) -> None:
"""
Drop-in replacement for ``np.flip`` for sparse LIL matrices which performs the
flipping in-place.
Parameters
----------
matrix : scipy.sparse.lil_matrix of shape (m, n) or None
The matrix to flip.
A matrix will be flipped in-place while nothing happens for ``None``.
axis : int or tuple of int
The axis or axes along which to flip over.
""" # noqa: E501
# None-matrices result in another None
if matrix is None:
return None
# end if
# if not already, the axis is converted to a tuple ...
if not isinstance(axis, tuple):
axis = (axis,)
# end if
# ... and checked for being valid
if axis not in {(0,), (1,), (0, 1), (1, 0)}:
raise ValueError(
f"\nOnly tuple axis (0,), (1,), (0, 1) or (1, 0) are available for 2D-"
f"matrices, not {axis}."
)
# end if
# the flipping depends on the axis
# Case 1: both axes are flipped
if axis in {(0, 1), (1, 0)}:
# first, the data is flipped ...
data_flipped = [
matrix.data[row_idx][::-1] for row_idx in range(matrix.shape[0] - 1, -1, -1)
]
matrix.data = np.empty_like(matrix.data)
matrix.data[::] = data_flipped
del data_flipped
# ... followed by the indexing
max_col_idx = matrix.shape[1] - 1
rows_flipped = [
[max_col_idx - idx for idx in matrix.rows[row_idx][::-1]]
for row_idx in range(matrix.shape[0] - 1, -1, -1)
]
matrix.rows = np.empty_like(matrix.rows)
matrix.rows[::] = rows_flipped
del rows_flipped
return None
# Case 2: only the order INSIDE each row is reversed
elif axis == (1,):
# first, the data is flipped ...
data_flipped = [row[::-1] for row in matrix.data]
matrix.data = np.empty_like(matrix.data)
matrix.data[::] = data_flipped
del data_flipped
# ... followed by the indexing
max_col_idx = matrix.shape[1] - 1
rows_flipped = [[max_col_idx - idx for idx in row[::-1]] for row in matrix.rows]
matrix.rows = np.empty_like(matrix.rows)
matrix.rows[::] = rows_flipped
del rows_flipped
return None
# end if
# Case 3: only the order of the rows themselves is reversed
matrix.data = np.flip(matrix.data, axis=0)
matrix.rows = np.flip(matrix.rows, axis=0)
return None
语句被拆分,因为两个轴上的翻转可以同时完成,因此顺序翻转不会那么有效(即
首先翻转第一个轴,然后翻转第二个轴)。分支是: 对于两个轴:以 REVERSE 顺序迭代行时,反转每个行中的数据和索引
if
赋值,因为
empty_like
会在后台应用一些列表重新排列魔法,当 np.array([list1, list2, list3...], dtype=object)
都具有相同的长度时,导致数组布局错误(请参阅这个问题)。
listx
# generation of a LIL-matrix
lil_mat = sps.lil_matrix((5, 6))
lil_mat[0, 0] = 1
lil_mat[0, 1] = 2
lil_mat[1, 1] = 3
lil_mat[1, 5] = 4
lil_mat[2, 2] = 5
lil_mat[3, 5] = 7
lil_mat[3, 0] = 6
lil_mat[4, 1] = 8
print(f"Original matrix\n{lil_mat.toarray()}")
# Original matrix
# [[1. 2. 0. 0. 0. 0.]
# [0. 3. 0. 0. 0. 4.]
# [0. 0. 5. 0. 0. 0.]
# [6. 0. 0. 0. 0. 7.]
# [0. 8. 0. 0. 0. 0.]]
的示例:
axis = 0
flip_lil_matrix(lil_mat, axis=(0,))
print(f"\nFlipped\n{lil_mat.toarray()}")
# Flipped
# [[0. 8. 0. 0. 0. 0.]
# [6. 0. 0. 0. 0. 7.]
# [0. 0. 5. 0. 0. 0.]
# [0. 3. 0. 0. 0. 4.]
# [1. 2. 0. 0. 0. 0.]]
print(f"\nElement second row, first column: {lil_mat[1, 0]}")
# Element second row, first column after flip: 6.0
示例(
相当于问题中所问的
axis = 1
):np.fliplr
flip_lil_matrix(lil_mat, axis=(1,))
print(f"\nFlipped\n{lil_mat.toarray()}")
# Flipped
# [[0. 0. 0. 0. 2. 1.]
# [4. 0. 0. 0. 3. 0.]
# [0. 0. 0. 5. 0. 0.]
# [7. 0. 0. 0. 0. 6.]
# [0. 0. 0. 0. 8. 0.]]
print(f"\nElement second row, first column after flip: {lil_mat[1, 0]}")
# Element second row, first column after flip: 4.0
axis = (0, 1)
的示例:
axis = (1, 0)
如图所示,索引单个元素仍然是可能的,并且添加其他元素根本没有问题,例如,在最后一次测试之后:
flip_lil_matrix(lil_mat, axis=(0, 1))
print(f"\nFlipped\n{lil_mat.toarray()}")
# Flipped
# [[0. 0. 0. 0. 8. 0.]
# [7. 0. 0. 0. 0. 6.]
# [0. 0. 0. 5. 0. 0.]
# [4. 0. 0. 0. 3. 0.]
# [0. 0. 0. 0. 2. 1.]]
print(f"\nElement second row, first column after flip: {lil_mat[1, 0]}")
# Element second row, first column after flip: 7.0
如前所述,此代码在迭代
lil_mat[0, 0] = 99
print(f"\nFlipped with changed element\n{lil_mat.toarray()}")
# Flipped with changed element
# [[99. 0. 0. 0. 8. 0.]
# [ 7. 0. 0. 0. 0. 6.]
# [ 0. 0. 0. 5. 0. 0.]
# [ 4. 0. 0. 0. 3. 0.]
# [ 0. 0. 0. 0. 2. 1.]]
object
这也可能是[max_col_idx - idx for idx in row[::-1]
但这里的性能高度依赖于用例,因为这会创建一个 NumPy 数组(开销)来执行快速数字操作,然后将其转换为列表(开销),因此在给定矩阵大小和稀疏度的情况下应该有一个最佳点。 我希望这对某人有帮助🙃