如何Mapscipy.sparse.lil_array?

66bbxpm5  于 2023-11-19  发布在  其他
关注(0)|答案(3)|浏览(114)

我有一个布尔值的起始lil_array。(Docs
例如尺寸3,5和数值:

(0, 2)    True
  (0, 4)    True
  (1, 0)    True
  (1, 1)    True
  (1, 3)    True
  (2, 0)    True
  (2, 3)    True

字符串
所以这是一个类似于下面的矩阵(True表示为1):

0 0 1 0 1
1 1 0 1 0
1 0 0 1 0


我也有一个np.ndarray,它的大小与用Int值填充的行的大小相同。对于这个例子,我将使用以下内容:

arr = np.array([0, -1, 0, 3, 2])


我想生成下面的lil_array(当然,零不会保存在稀疏数组中):

0  0  0  0  2
0 -1  0  3  0
0  0  0  3  0


其中每行是初始LIL_array对应行与arr的逻辑与。
我知道如何用几种方法来实现这一点,首先将lil_array转换为矩阵,或者将行转换为ndarray或列表,但这会降低利用该矩阵的稀疏属性所获得的效率。(这是一个玩具示例,但我的问题涉及一个更大的矩阵)
如何在不将稀疏矩阵转化为矩阵的情况下,以有效和干净的方式生成输出?

zzzyeukh

zzzyeukh1#

首先,如果你主要关心的是内存效率,我会引导你远离lil_array。LIL在内部使用列表,而Python are not very memory efficient中的列表。lil_array主要是在你想创建一个稀疏数组但不知道它将有多少非零元素时有用。我会引导你转向COO或CSR。
第二,您要查找的实际上是np.where(boolean_array, arr, 0),除了您希望它在稀疏数组上工作而不首先将它们转换为非稀疏数组。
在google上搜索“scipy sparse where”可以找到this thread,这几乎就是我们需要的代码。
where1函数几乎就是我们需要的,除了它假设它索引的第二个数组是2D稀疏数组,而你拥有的是1D密集数组。我们可以通过删除对.tocsr()的调用来解决这个问题,并且只按列索引。
代码:

def where1(cond, x):
    assert len(x.shape) == 1, "x should be 1d"
    # elements of x where cond
    row, col = cond.nonzero()
    data = np.empty(row.shape, dtype=x.dtype)
    zs = scipy.sparse.coo_matrix((data, (row, col)), shape=cond.shape)
    xx = x[col]
    zs.data[:] = xx
    zs.eliminate_zeros()
#     zs = zs.tolil()
    return zs

字符串
(如果您希望结果以lil格式显示,请取消注解zs = zs.tolil()。)
根据基准测试,这比@Malcolm对100000 x10000矩阵(0.1%非零元素)的回答快100倍。

axr492tv

axr492tv2#

我建议不要将原始数组存储为lil_array。相反,只需存储与真实值对应的元组列表。或者如果您已经将其存储为lil_array,则使用lil_array.nonzero方法将其转换为索引列表。然后使用这些元组填充lil_array中的值作为结果。

>>> x = lil_array((3,5), dtype=bool)
>>> x[0,2] = True
>>> x[0,4] = True
>>> x[1,0] = True
>>> x[1,1] = True
>>> x[1,3] = True
>>> x[2,0] = True
>>> x[2,3] = True
>>> arr = np.array([0, -1, 0, 3, 2])
>>> nz = x.nonzero()
>>> result = lil_array((3,5), dtype=int)
>>> for i, j in zip(nz[0], nz[1]):
...     result[i, j] = arr[j]
... 
>>> print(result)
  (0, 4)    2
  (1, 1)    -1
  (1, 3)    3
  (2, 3)    3
>>> print(result.toarray())
[[ 0  0  0  0  2]
 [ 0 -1  0  3  0]
 [ 0  0  0  3  0]]
>>>

字符串

ocebsuys

ocebsuys3#

最简单的代码-如果稀疏矩阵实际上是稠密的

In [42]: mat=[[0, 0, 1, 0, 1],
    ...: [1, 1, 0, 1, 0],
    ...: [1, 0, 0, 1, 0]]
    ...: #I also have a np.ndarray of the same size as the rows filled with Int values. For this example I will use the following:
    ...: 
    ...: arr = np.array([0, -1, 0, 3, 2])

In [43]: mat = np.array(mat)

In [44]: np.where(mat, arr, 0)
Out[44]: 
array([[ 0,  0,  0,  0,  2],
       [ 0, -1,  0,  3,  0],
       [ 0,  0,  0,  3,  0]])

字符串
如果矩阵不是太大(即没有内存错误),那么很容易在稀疏和密集之间转换:

In [45]: from scipy import sparse    
In [46]: M = sparse.lil_matrix(mat)    
In [47]: M
Out[47]: 
<3x5 sparse matrix of type '<class 'numpy.intc'>'
    with 7 stored elements in List of Lists format>    
In [48]: print(M)
  (0, 2)    1
  (0, 4)    1
  (1, 0)    1
  (1, 1)    1
  (1, 3)    1
  (2, 0)    1
  (2, 3)    1


我开始的时候很密集:

In [49]: M.A
Out[49]: 
array([[0, 0, 1, 0, 1],
       [1, 1, 0, 1, 0],
       [1, 0, 0, 1, 0]], dtype=int32)


请注意,通常lil用于迭代构造矩阵。coo更适合从[48]中所示的坐标进行构造。csr最适合计算。sparse很容易在这些选项之间转换。(toarray()通常通过csr格式创建)。
lil值存储为两个对象dtype数组-每个数组包含代表每行的列表:

In [50]: M.data, M.rows
Out[50]: 
(array([list([1, 1]), list([1, 1, 1]), list([1, 1])], dtype=object),
 array([list([2, 4]), list([0, 1, 3]), list([0, 3])], dtype=object))


同样,这种格式很容易迭代修改,但不是最紧凑或计算上最方便的。
在进一步的编辑中,我将探索从M创建你想要的Map,虽然很容易在M的行上进行操作(甚至还有一个getrowview),但rwo和arr之间的逻辑和并不明显。
下面是一种迭代lil的行的方法

In [51]: M = sparse.lil_matrix(mat)
    ...: for i in range(3):
    ...:     M.data[i] = arr[M.rows[i]].tolist()
    ...: M.A
Out[51]: 
array([[ 0,  0,  0,  0,  2],
       [ 0, -1,  0,  3,  0],
       [ 0,  0,  0,  3,  0]], dtype=int32)


换句话说,使用rows列表索引arr,并将这些值(作为列表)赋回M
由于arr有一些0值,这些值将出现在新的M中:

...: M.data
Out[52]: array([list([0, 2]), list([0, -1, 3]), list([0, 3])], dtype=object)


去掉这些0还需要修剪rows属性。
也可以在csr的行上进行扩展,这样可能会更快,尽管在概念上更复杂。

相关问题