当我尝试将scipy.sparse
矩阵s
(任何可用的稀疏类型)分配给NumPy数组a
时,如下所示:
a[:] = s
我得到TypeError
:
TypeError:float()参数必须是字符串或数字
有没有办法解决这个问题?
我知道todense()
和toarray()
方法,但我真的想避免不必要的副本,我更喜欢为NumPy数组使用相同的代码SciPy稀疏矩阵。
目前,我并不关心从稀疏矩阵中获取低效率的值。
是否有某种类型的稀疏矩阵包装器可以与NumPy索引赋值配合使用?
如果没有,任何建议我如何自己建立这样的东西?
在这种情况下,是否存在与NumPy合作的不同稀疏数组库?
更新
我在NumPy源代码中搜索过,并且在搜索错误消息字符串时,我想我找到了@TYPE@_setitem()
函数float()
中索引分配发生的部分。
我仍然没有真正得到它,但在某些时候,a
函数似乎被调用(如果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
是一个浮点数组)。所以我试图修补其中一个SciPy稀疏矩阵类,以允许调用此函数:
float()
可悲的是,这并不起作用,因为在整个稀疏矩阵上调用float()
而不是在其中的单个项目上调用。{/ p>
所以我想我的新问题是:如何进一步更改稀疏矩阵类以使NumPy迭代所有项目并在每个项目上调用PySequence_Check()
?
另一个更新:
我在Github上发现了一个稀疏数组模块(numpy/core/src/multiarray/arraytypes.c.src
around line 187),它允许赋值给NumPy数组。遗憾的是,模块的功能非常有限(例如,切片还没有真正起作用),但它可能有助于理解如何实现分配给NumPy数组。
我会进一步调查......
再次更新:
我做了一些挖掘,发现更有趣的源文件可能是https://github.com/FRidh/sparse。
我怀疑函数__array_struct__
(numpy/core/src/multiarray/ctors.c
/ docs)在分配期间的某个时间被调用。来自code的简单稀疏数组类通过了测试,但它看起来像SciPy中的稀疏矩阵类(尽管在我看来它们是序列)。
他们会检查__array_interface__
,__array__
和__getitem__
,然后它会以某种方式决定它们不是序列。
不检查属性__len__
和PySequence_Check()
(所有稀疏数组类都有!)。
这引出了另一个问题:如何以他们通过__getitem__()
的方式操纵稀疏矩阵类(或其对象)?
我认为一旦它们被识别为序列,分配就应该有效,因为__len__()
和{{1}}应该足够了。
答案 0 :(得分:1)
正如我对问题的评论中所提到的,序列界面不适用于稀疏的矩阵,因为当使用单个数字编制索引时,它们不会丢失维度。
无论如何要尝试它,我在纯Python中创建了一个非常有限的快速和脏的稀疏数组类,当用单个数字索引时,它返回一个"行" class(包含原始数据的视图),再次可以使用单个数字编制索引以生成此索引处的实际值。使用我的类的实例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。
所以我想这是我原来问题的另一个更好的解决方案。
这留下了另一个问题:为什么没有临时数组会有效?
答案 1 :(得分:0)
如何使用nonzero
来识别哪些元素不为零?
x = np.ones((3,4))
s = sparse.csr_matrix((3,4))
s[0,0] = 2
s[1,2] = 3
I,J = s.nonzero()
x[:] = 0 # omit if just changing nonzero values
x[I,J] = s.data
x
对于密集和nonzero
数组, csr
的功能相同。我还没有尝试过其他格式。
对于csr(和coo)稀疏矩阵,值存储在s.data
数组中。在此示例中,它看起来像:
array([ 2., 4.])
x
值位于data
缓冲区x.data
中。在这种情况下,它是12个连续的花车。
x.ravel()
# array([ 2., 0., 0., 0., 0., 0., 4., 0., 0., 0., 0., 0.])
s
的这两个值无法在不复制的情况下映射到x
的12个值。通常,稀疏数据值不会与其密集等效值中的连续值块匹配。
您担心I
和J
数组的大小。如果稀疏矩阵采用coo
格式,则可以以相同的方式使用其row
和col
属性:
sc=s.tocoo()
x[sc.row, sc.col]=sc.data
但是从一种稀疏格式转换为另一种稀疏格式涉及复制数据。并且铜阵列不是可订阅的。
x = np.zeros((3,4))
x[:]=['123','321','0','1']
产生
array([[ 123., 321., 0., 1.],
[ 123., 321., 0., 1.],
[ 123., 321., 0., 1.]])
它会将float
应用于右侧的每个项目,然后播放'它适合x
大小。
[]
转换为对-_setitem__
的调用。
x.__setitem__((1,2),3) # same as x[1,2]=3
x.__setitem__((None,2),'3') # sets the last row
似乎在任何可迭代的每个项目上调用float
(需要仔细检查这个)。但是如果值是某个其他对象,我们会得到类似于原始对象的错误:
class Foo(): pass
x.__setitem__((1,2), Foo())
# TypeError: float() argument must be a string or a number, not 'Foo'
稀疏coo
和dok
格式产生类似错误,而csr
和lil
产生
ValueError: setting an array element with a sequence.
我还没弄清楚这里使用了稀疏矩阵的哪种方法或属性。
看看np.broadcast
。我认为这复制了这些分配中使用的迭代类型。
b = np.broadcast(x[:], [1,2,3,4])
list(b)
我们可以通过从带有dtype对象的数组开始来移除浮点转换复杂度,该数组可以包含任何内容:
xa=np.zeros((3,4), dtype=object)
xa[:]=s
但现在s
的每个元素中都会出现xa
。它没有s
超过xa
的值。
我猜测当s
不是np.array
时,numpy
在执行作业时首先将其包裹起来,例如:
x[:] = np.array(s)
当s
是标量或列表时,会生成一个可以广播以适合x
的数组。但是当它是一个对象(稀疏数组不是一个numpy数组)时,这个包装只是一个带有dtype = object的0d数组。您需要通过一个函数传递s
,该函数将其转换为可以广播的迭代。最明显的是toarray()
。