切片
内容
切片¶
Dask 数组支持大部分 NumPy 切片语法。特别是,它支持以下内容:
按整数和切片进行切片:
x[0, :5]
通过整数列表/数组切片:
x[[1, 2, 4]]
通过布尔列表/数组的切片:
x[[False, True, True, False, True]]
使用布尔类型的
Array
对一个Array
进行切片:x[x > 0]
使用一个零维或一维的
Array
对Array
进行切片:a[b.argtopk(5)]
然而,它目前不支持以下内容:
在多个轴上使用列表进行切片:
x[[1, 2, 3], [3, 2, 1]]
这很容易添加。如果你有一个用例,那么可以提出一个问题。此外,对此感兴趣的用户应该查看
vindex
。使用多维的
Array
对一个Array
进行切片
效率¶
普通的 Dask 调度器足够智能,只会计算那些为了达到所需切片而必需的块。因此,如果只需要少量输出,大型操作可能会很廉价。
在下面的示例中,我们创建了一个包含一万亿个元素的Dask数组,每个块的大小为百万个元素。然后我们对整个数组进行操作,最后只切分出输出的一部分:
>>> # Trillion element array of ones, in 1000 by 1000 blocks
>>> x = da.ones((1000000, 1000000), chunks=(1000, 1000))
>>> da.exp(x)[:1500, :1500]
...
这只需要计算左上角的四个块来达到结果。我们在那些只需要部分结果的块上稍微浪费了一些。此外,我们也有点浪费,因为我们仍然需要操作包含大约一百万个任务的Dask图。这可能会导致一两秒的交互开销。
使用具体索引器(例如整数列表)进行切片有几种可能的失败模式值得提及。首先,当你对分块轴进行索引时,Dask通常会在输出上“匹配”分块。
# Array of ones, chunked along axis 0
>>> a = da.ones((4, 10000, 10000), chunks=(1, -1, -1))
如果我们用一个 排序 的整数序列来切片,Dask 将返回每个输入块一个块(注意输出 chunksize 是 1,因为索引 0
和 1
在输入中位于不同的块中)。
>>> a[[0, 1], :, :]
dask.array<getitem, shape=(2, 10000, 10000), dtype=float64, chunksize=(1, 10000, 10000), chunktype=numpy.ndarray>
但是重复的索引呢?Dask 继续为每个输入块返回一个块,但如果您有许多来自同一输入块的重复项,您的输出块可能会大得多。
>>> a[[0] * 15, :, :]
PerformanceWarning: Slicing is producing a large chunk. To accept the large
chunk and silence this warning, set the option
>>> with dask.config.set({'array.slicing.split_large_chunks': False}):
... array[indexer]
To avoid creating the large chunks, set the option
>>> with dask.config.set({'array.slicing.split_large_chunks': True}):
... array[indexer]
dask.array<getitem, shape=(15, 10000, 10000), dtype=float64, chunksize=(15, 10000, 10000), chunktype=numpy.ndarray>
之前我们在第一个维度上的chunksize为``1``,因为我们从每个输入块中只选择了一个元素。但现在我们从第一个块中选择了15个元素,生成了一个较大的输出块。
当以这种方式索引产生一个比 array.chunk-size
配置选项大5倍的块时,Dask 会发出警告。你有两个选项来处理这个警告:
设置
dask.config.set({"array.slicing.split_large_chunks": False})
以允许大块并静默警告。设置
dask.config.set({"array.slicing.split_large_chunks": True})
以避免首先创建大块。
正确的选择将取决于你的下游操作。有关选择块大小的更多信息,请参见 块。