我注意到,用于在稀疏矩阵类型之间进行转换的方法中,没有一个使用方法中提供的copy
kwarg。(在有效的情况下)总是有一个base
集,这意味着它在代码中显示为view
。
这是故意的行为吗?
例如,这里有csr
和csc
数组的例子,正如你所看到的,它们都有基,不管是什么。
In [1]: import numpy as np
...: from scipy import sparse
...:
...: a = np.arange(20).reshape(4, 5)
...: csr = sparse.csr_array(a, copy=True)
...: print('csr.data.base', id(csr.data.base) if csr.data.base is not None else None)
...:
...: csr_copy = csr.copy()
...: print('csr_copy.data.base', id(csr_copy.data.base) if csr_copy.data.base is not None else None)
...:
...: csc_copy = csr.tocsc(copy=True)
...: print('csc_copy.data.base', id(csc_copy.data.base) if csc_copy.data.base is not None else None)
...:
...: csc_copy_2 = csr.tocsc()
...: print('csc_copy_2.data.base', id(csc_copy_2.data.base) if csc_copy_2.data.base is not None else None)
csr.data.base 4392865488
csr_copy.data.base 4392866448
csc_copy.data.base 4392866640
csc_copy_2.data.base 4392867120
虽然csr_copy
具有与csr.data
相同的base
是有意义的,但我不明白为什么其他对象都为数据设置了base
属性。
特别是,此行为会防止使用者直接操作数组的data
和indices
参数。例如,无法使用resize
方法新增数据列来扩充csr
矩阵:
In [2]: old_nnz = csr.nnz
...: row = [1, 2, 3, 4, 5] # Lets append row of 5 elements to csr
...:
...: csr.resize(5, 5)
...:
...: print(id(csr.data))
...: print(csr.data)
...:
...: print(id(csr.data.base))
...: print(csr.data.base)
...:
...: csr.data.resize((old_nnz + len(row),), refcheck=True)
4757413808
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
4757413520
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
Traceback (most recent call last):
File "/opt/homebrew/Caskroom/miniforge/base/envs/dev/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3433, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-34-c52e3457494e>", line 12, in <module>
csr.data.resize((old_nnz + len(row),), refcheck=True)
ValueError: cannot resize this array: it does not own its data
虽然使用np.resize
可能可行,但我不确定它是否合适:
In [3]: old_nnz = csr.nnz
...: row = [1, 2, 3, 4, 5] # Let's append row of 5 elements to csr
...:
...: csr.resize(5, 5)
...:
...: print('Data')
...: print(id(csr.data))
...: print(csr.data)
...:
...: print("Data's Base")
...: print(id(csr.data.base))
...: print(csr.data.base)
...:
...: print('New Data')
...: new_data = np.resize(csr.data, (old_nnz + len(row),))
...: print(id(new_data))
...: print(new_data)
...:
...: print("New Data's Base")
...: print(id(new_data.base))
...: print(new_data.base)
...:
...: new_indices = np.resize(csr.indices, (old_nnz + len(row),))
Data
5256251600
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
Data's Base
5256250736
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
New Data
5256250928
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5]
New Data's Base
5256253040
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5
6 7 8 9 10 11 12 13 14 15 16 17 18 19]
我一直在阅读这些函数的源代码,但我没有看到其中一些函数使用了copy
。
def tocsc(self, copy=False):
idx_dtype = get_index_dtype((self.indptr, self.indices),
maxval=max(self.nnz, self.shape[0]))
indptr = np.empty(self.shape[1] + 1, dtype=idx_dtype)
indices = np.empty(self.nnz, dtype=idx_dtype)
data = np.empty(self.nnz, dtype=upcast(self.dtype))
csr_tocsc(self.shape[0], self.shape[1],
self.indptr.astype(idx_dtype),
self.indices.astype(idx_dtype),
self.data,
indptr,
indices,
data)
A = self._csc_container((data, indices, indptr), shape=self.shape)
A.has_sorted_indices = True
return A
即使我看到新的数组(data
)被创建了,在某个地方,也许在C/Python接口之间的某个地方,它被放入base
。
1条答案
按热度按时间v1uwarro1#
我只有scipy v 1.7.3,所以没有访问1.8中稀疏模块的主要重写(例如,不是
csr_array
或_data.py
文件)。是否有
base
并不能可靠地衡量一个副本是否被复制。a
是由arange
产生的1d数组的view
。该数组不可访问-除非作为base
。data
属性有一个base
-看起来和它自己一样。id
是不同的。但是我们必须研究代码,看看data
是如何从它的base
派生出来的。它不是
a
或a.base
的view
,我们可以通过修改元素来证明。copy
参数在保持相同格式时最有意义。更改格式可能涉及重新排序数据(csr到csc),或对重复项求和(coo到csr)等。让我们尝试创建一个新的
csr
:和
csr
一样,这两个元素都有data.base
和不同的id,但是如果我修改csr
的一个元素,这个改变只会出现在csr1
中。csr2
实际上是一个副本。调整大小
我以前没有用过
resize
来进行稀疏运算,也很少用它来进行numpy
运算。csr.resize(5,5)
似乎只是更改了indptr
(和shape
),而没有更改为data
或indices
。csr.resize(5,6)
似乎只是改变了shape
。我没有看到主属性的变化。两者都没有添加非零值,所以用0填充没有太大变化。您不希望执行
csr.data.resize(...)
。这样的更改还需要更改indices
和indptr
(以保持一致的csr
)。data
可以有0,但应该通过调用eliminate_zeros
来清除。