You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scipy稀疏矩阵copy参数不符合Numpy复制/视图设计理念的问询

Scipy稀疏矩阵类型转换中copy参数与data.base属性的疑问

我发现Scipy中稀疏矩阵的类型转换方法并未正确使用方法提供的copy参数。多数情况下实际已经完成了数据复制,但生成的data数组始终被设置了base属性,在代码中表现为view,可实际上复制操作已经完成。请问这是有意设计的行为吗?

示例验证

以下是csr和csc数组的测试示例,可见无论是否指定copy=True,它们的data都带有base属性:

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.data与csr.data拥有相同base是合理的,但其他对象的data也被设置base属性的行为无法理解。

对用户操作的阻碍

这种行为会阻碍用户直接操作数组的data和indices参数,例如无法通过原地resize方法扩展csr矩阵的行数:

In [2]: old_nnz = csr.nnz 
   ...: row = [1, 2, 3, 4, 5]  # 给csr追加一行5个元素
   ...: 
   ...: 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]  # 给csr追加一行5个元素
   ...: 
   ...: 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参数,例如tocsc方法:

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属性。


内容的提问来源于stack exchange,提问作者Kirill Shumilov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 13:15:54