当我尝试分配 scipy.sparse
时矩阵 s
(任何可用的稀疏类型)到 NumPy 数组 a
,如下所示:
a[:] = s
我收到一个TypeError
:
TypeError: float() argument must be a string or a number
有办法解决这个问题吗?
我知道 todense()
和 toarray()
方法,但我真的很想避免不必要的复制,我更愿意使用相同的方法NumPy 数组和 SciPy 稀疏矩阵的代码。
现在,我不担心从稀疏矩阵中获取的值效率低下。
是否可能存在某种可与 NumPy 索引分配一起使用的稀疏矩阵包装器?
如果没有,有什么建议我可以自己构建这样的东西吗?
在这种情况下是否有与 NumPy 配合的不同的稀疏数组库?
更新:
我在 NumPy 源代码中四处寻找,搜索错误消息字符串,我想我在 numpy/core/src/multiarray/arraytypes.c.src
around line 187 中找到了索引分配发生的部分。在函数 @TYPE@_setitem()
中。
我还是不太明白,但是在某些时候,似乎调用了float()
函数(如果a
是一个 float 组) .因此,我尝试对 SciPy 稀疏矩阵类之一进行猴子修补,以允许调用此函数:
import scipy
s = scipy.sparse.dok_matrix((5, 1))
def myfloat(self):
assert self.shape == (1, 1)
return self[0, 0]
scipy.sparse.dok.dok_matrix.__float__ = myfloat
a[:] = s
遗憾的是,这不起作用,因为 float()
是在整个稀疏矩阵上调用的,而不是在其中的单个项上调用的。
所以我想我的新问题是:如何进一步更改稀疏矩阵类以使 NumPy 遍历所有项并对每个项调用 float()
?
另一项更新:
我在 Github ( https://github.com/FRidh/sparse ) 上找到了一个稀疏数组模块,它允许分配给 NumPy 数组。遗憾的是,该模块的功能非常有限(例如,切片还不能真正起作用),但它可能有助于理解如何实现对 NumPy 数组的分配。 我会进一步调查......
又一更新:
我做了更多挖掘,发现一个更有趣的源文件可能是 numpy/core/src/multiarray/ctors.c
.
我怀疑函数 PySequence_Check()
( docs/code ) 在分配过程中的某个时间被调用。来自 https://github.com/FRidh/sparse 的简单稀疏数组类通过测试,但看起来 SciPy 中的稀疏矩阵类没有(尽管在我看来它们是序列)。
检查它们的 __array_struct__
、__array_interface__
和 __array__
,然后以某种方式确定它们不是序列。
未检查属性 __getitem__
和 __len__
(所有稀疏数组类都有!)。
这让我想到了另一个问题:如何以通过 PySequence_Check()
的方式操作稀疏矩阵类(或其对象)?
我认为一旦它们被识别为序列,赋值就应该起作用,因为 __getitem__()
和 __len__()
应该足够了。
最佳答案
正如在对我的问题的评论中提到的,序列接口(interface)不适用于稀疏矩阵,因为它们在用单个数字索引时不会丢失维度。
无论如何尝试,我在纯 Python 中创建了一个非常有限的快速和肮脏的稀疏 array 类,当用单个数字索引时,它返回一个“行”类(它持有一个 View 原始数据),它又可以用单个数字进行索引以产生该索引处的实际值。使用我的类的实例 s
,分配给 NumPy 数组 a
完全按照要求工作:
a[:] = s
我预计这会有点低效,但它真的、真的、真的、非常慢。分配一个 500.000 x 100 的稀疏数组需要几分钟! 不过,好消息是在赋值期间不会创建完整大小的临时数组。在分配期间内存使用率保持不变(同时其中一个 CPU 已达到极限)。
所以这基本上是原始问题的一种解决方案。
为了使分配更高效并且仍然不使用密集数组数据的临时副本,NumPy 必须在内部执行类似的操作
s.toarray(out=a)
据我所知,目前还没有办法让 NumPy 这样做。
但是,有一种方法可以做一些非常相似的事情,方法是提供一个返回 NumPy 数组的 __array__()
方法。顺便说一句,SciPy 稀疏矩阵已经有了这样的方法,只是名称不同:toarray()
。所以我只是重命名它:
scipy.sparse.dok_matrix.__array__ = scipy.sparse.dok_matrix.toarray
a[:] = s
这就像一个魅力(也适用于其他稀疏矩阵类)并且非常快!
根据我对情况的有限理解,这应该创建一个与 a
大小相同的临时 NumPy 数组,它包含 s
中的所有值(以及许多零) 然后分配给 a
。
但奇怪的是,即使我使用非常大的 a
几乎占据了我所有可用的 RAM,分配仍然很快发生并且没有使用额外的 RAM。
所以我想这是对我原来的问题的另一个更好的解决方案。
这留下了另一个问题:为什么这在没有临时数组的情况下也能工作?
关于python - 如何通过索引将 scipy.sparse 矩阵分配给 NumPy 数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26934349/