在Python中翻转稀疏数组的行

问题描述 投票:0回答:3

我有一个稀疏数组,说:

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
python arrays sparse-matrix
3个回答
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

0
投票

您可以进行多索引分配:

ii = [2,1,0,3]
a[:,1] = a[ii,:]

其中

ii
是您必须以某种方式创建的索引数组。


0
投票

我知道这是一个老问题,但我仍然会添加一个答案: 如果之后不需要更改稀疏结构,则 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 顺序迭代行时,反转每个行中的数据和索引
  1. 对于轴 1:按“原样”顺序迭代行时,反转每个行中的数据和索引
  2. 对于轴 0:以反向顺序迭代行,就是这样
  3. 如果所有行都包含相同数量的条目,则需要奇怪的
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.]]
列表数组并反转其顺序时使用列表推导式,因此它可能最终无法实现高性能。改进这一点的一种方法可能是使用 NumPy-Arrays

object

这也可能是
[max_col_idx - idx for idx in row[::-1]

但这里的性能高度依赖于用例,因为这会创建一个 NumPy 数组(开销)来执行快速数字操作,然后将其转换为列表(开销),因此在给定矩阵大小和稀疏度的情况下应该有一个最佳点。

我希望这对某人有帮助🙃



© www.soinside.com 2019 - 2024. All rights reserved.