稀疏数组
目录
稀疏数组¶
通过将内存中的 NumPy 数组替换为内存中的稀疏数组,我们可以重用 Dask 数组的分块算法来实现并行和分布式稀疏数组。
Dask 数组中的分块算法通常围绕内存中的 NumPy 数组进行并行化。然而,如果另一个内存数组库支持 NumPy 接口,那么它也可以利用 Dask 数组的并行算法。特别是 sparse 数组库满足 NumPy API 的子集,并且与 Dask 数组配合良好(并经过测试)。
示例¶
假设我们有一个 Dask 数组,其中大部分元素为零
rng = da.random.default_rng()
x = rng.random((100000, 100000), chunks=(1000, 1000))
x[x < 0.95] = 0
我们可以将 NumPy 数组的每个块转换为一个 sparse.COO 数组
import sparse
s = x.map_blocks(sparse.COO)
现在,我们的数组不再由许多 NumPy 数组组成,而是由许多稀疏数组组成。从语义上讲,这没有任何改变。可用的操作将继续完全相同地工作(假设 numpy
和 sparse
的行为相同),但性能特征和存储成本可能会发生显著变化
>>> s.sum(axis=0)[:100].compute()
<COO: shape=(100,), dtype=float64, nnz=100>
>>> _.todense()
array([ 4803.06859272, 4913.94964525, 4877.13266438, 4860.7470773 ,
4938.94446802, 4849.51326473, 4858.83977856, 4847.81468485,
... ])
要求¶
任何复制 NumPy ndarray 接口的内存库都应该在这里工作。sparse 库是一个最小的示例。特别是,内存库应至少实现以下操作:
使用切片、列表和元素进行简单切片(用于切片、重新分块、重塑等)
一个
concatenate
函数,与np.concatenate
的接口匹配。此函数必须注册在dask.array.core.concatenate_lookup
中所有 ufuncs 必须支持完整的 ufunc 接口,包括
dtype=
和out=
参数(即使它们功能不正常)所有归约操作必须支持完整的
axis=
和keepdims=
关键字,并且在这方面与 NumPy 的行为一致数组类应遵循
__array_priority__
协议,并准备好响应优先级较低的其他数组如果需要支持
dot
,则应将一个与np.tensordot
接口匹配的tensordot
函数注册到dask.array.core.tensordot_lookup
中
其他操作(如 reshape、transpose 等)的实现应遵循标准的 NumPy 约定,关于 shape 和 dtype。不实现这些操作也是可以的;如果在运行时尝试这些操作,并行 dask.array
将会出错。
混合数组¶
Dask 数组支持混合使用不同类型的内存数组。这依赖于内存数组在必要时知道如何相互作用。当两个数组交互时,具有最高 __array_priority__
的数组的函数将优先(例如,对于 concatenate、tensordot 等)。