切片
目录
切片¶
Dask Array 支持大部分 NumPy 的切片语法。特别是,它支持以下用法:
通过整数和切片进行切片:
x[0, :5]
通过整数列表/数组进行切片:
x[[1, 2, 4]]
通过布尔值列表/数组进行切片:
x[[False, True, True, False, True]]
但是,它目前不支持以下用法:
在多个轴上使用列表进行切片:
x[[1, 2, 3], [3, 2, 1]]
不过,添加此功能也很简单。如果您有相关用例,请提交 issue。此外,对此感兴趣的用户应该查看
vindex
。
效率¶
普通的 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})
以避免一开始就创建大块。
正确的选择取决于您的下游操作。有关如何选择块大小的更多信息,请参阅 块。