连接
内容
连接¶
DataFrame 的连接是一种常见且计算成本高的操作,在不同情况下可以从各种优化中受益。了解数据的布局以及你想要实现的目标对性能有很大影响。本文档页面详细介绍了各种不同的选项及其对性能的影响。
大到大的未排序连接¶
在最坏的情况下,你有两个每个都有许多分区的大表,并且你想沿着一个可能未排序的列将它们连接起来。
这可能会很慢。在这种情况下,Dask DataFrame 需要移动所有数据,以便在连接列中具有匹配值的行位于同一分区中。这种大规模的移动会产生通信成本,并且可能需要大量内存。如果找不到足够的内存,Dask 将不得不读取和写入磁盘,这可能会导致其他性能成本。
这些问题是可以解决的,但会比许多其他操作慢得多。如果可能的话,最好避免这些问题。
大到小连接¶
许多连接或合并计算将一个大表与一个小表结合。如果小表是单个分区 Dask DataFrame 或甚至只是一个普通的 Pandas DataFrame,那么计算可以以一种令人尴尬的并行方式进行,其中大 DataFrame 的每个分区都与单个小表连接。这相对于 Pandas 连接几乎不会产生任何开销。
如果你的小表可以轻松地放入内存中,那么你可能希望确保它是一个单一分区,使用 repartition 方法。
import dask
large = dask.datasets.timeseries(freq="10s", npartitions=10)
small = dask.datasets.timeseries(freq="1D", dtypes={"z": int})
small = small.repartition(npartitions=1)
result = large.merge(small, how="left", on=["timestamp"])
排序连接¶
Pandas 的 merge API 支持 left_index=
和 right_index=
选项,用于在索引上执行连接。对于 Dask DataFrames,如果索引具有已知的分区(参见 分区),这些关键字选项具有特殊意义。在这种情况下,DataFrame 分区沿着这些分区对齐(通常速度较快),然后在分区对之间进行令人尴尬的并行 Pandas 连接。这通常相对较快。
排序或索引连接是解决大-大连接问题的一个好方法。如果你计划反复连接一个数据集,那么提前设置索引可能是值得的,并且可能将数据存储在保持该索引的格式中,如Parquet。
import dask
import dask.dataframe as dd
left = dask.datasets.timeseries(dtypes={"foo": int})
# timeseries returns a dataframe indexed by
# timestamp, we don't need to set_index.
# left.set_index("timestamp")
left.to_parquet("left", overwrite=True)
left = dd.read_parquet("left")
right_one = dask.datasets.timeseries(dtypes={"bar": int})
right_two = dask.datasets.timeseries(dtypes={"baz": int})
result = left.merge(
right_one, how="left", left_index=True, right_index=True)
result = result.merge(
right_two, how="left", left_index=True, right_index=True)