如何在Dask中实现无数据重复的增量缓存?

如何在Dask中实现无数据重复的增量缓存?,dask,Dask,我试图在Dask中找到Spark的unpersist的等价物。我的需要 对于显式非持久性,出现在以下情况: 调用上下文已经保留了一个大的df,例如,因为它需要 为预处理目的计算多个聚合 调用上下文调用一个函数,该函数也需要调用persist,例如。, 因为它执行一些迭代算法 一个基本示例如下所示: def迭代算法(df,num迭代): 对于范围内的迭代(num_迭代): #需要映射分区等转换逻辑 def映射器(df): # ... 返回df df=df.map\u分区(映射器) df=df.

我试图在Dask中找到Spark的
unpersist
的等价物。我的需要 对于显式非持久性,出现在以下情况:

  • 调用上下文已经保留了一个大的
    df
    ,例如,因为它需要 为预处理目的计算多个聚合
  • 调用上下文调用一个函数,该函数也需要调用persist,例如。, 因为它执行一些迭代算法
一个基本示例如下所示:

def迭代算法(df,num迭代):
对于范围内的迭代(num_迭代):
#需要映射分区等转换逻辑
def映射器(df):
# ...
返回df
df=df.map\u分区(映射器)
df=df.persist()
#现在我想显式地取消旧快照的持久性
返回df
在Spark中,可以通过释放旧快照来解决此问题 明确地显然,Dask没有显式的
unpersist
,但可以处理这个问题 通过基础期货的参考计数。这意味着示例 因为调用上下文包含引用,所以上面的操作将复制数据 对于旧的期货,而子函数保存对已修改期货的引用 坚持在我的实际用例中,有几个嵌套的级别 转换调用,导致数据重复甚至多次

有没有一种方法可以在不增加任何副本的情况下解决迭代缓存问题?

我将发布一些解决方法,但我仍在寻找更好的方法 替代品

由于引用计数的原因,避免复制是很困难的,但是 这些都是可能的。问题是调用方持有引用的结果 到原始的
df
和通过
df=df创建新实例的子函数。
电话。为了解决这个问题,我们必须引用
df
本身 易变的。不幸的是,Python通常不允许对引用进行变异 函数参数的定义

解决方案1:原始可变引用

解决该限制的最简单方法是将
df
包装到一个列表中 或说明。在这种情况下,子功能可以修改外部参考,例如:

df_列表[0]=df_列表[0]。映射分区(映射器)
df_list[0]=df_list[0]。persist()
然而,这在句法上很尴尬,必须非常小心,因为 通过
df=df_list[0]
简化语法,再次创建对 潜在的未来,这可能会导致数据重复

解决方案2:基于包装器的可变引用

在此基础上,可以编写一个小的包装器类,其中包含一个引用 到数据帧。通过这个包装器,子函数可以发生变化 参考资料。为了改进语法问题,可以考虑包装器。 应自动或继承将功能委托给数据帧 从它。总的来说,这个解决方案也感觉不对劲

解决方案3:显式突变

为了避免其他解决方案的语法问题,我目前更喜欢以下解决方案 变量,它有效地模拟了
map\u分区的可变版本
并通过对原始
df
实例的就地修改来保持

def就地修改(旧的、新的):
#当前需要访问数据帧的私有字段,但
#也许这可以得到Dask的官方支持。
旧的_df.dask=新的_df.dask
旧的_-df._-meta=新的_-df._-meta
旧的_-df._-name=新的_-df._-name
旧分区=新分区
def迭代算法(df、num迭代):
对于范围内的迭代(num_迭代):
def映射器(df):
#实际转换逻辑。。。
返回df
#模拟可变/就地映射分区
new_df=df.map_分区(映射器)
就地修改(df,新的_df)
#模拟可变/就地持久化
new_df=df.persist()
就地修改(df,新的_df)
#从技术上讲,无需返回,因为所有操作都已就绪
返回df
这对我来说相当有效,但需要仔细遵守以下规则:

  • 用上面的模式替换所有不可变的调用,如
    df=df.
  • 注意创建对
    df
    的引用。例如,为了语法上的方便,使用像
    some\u col=df[“some\u sol”]
    这样的变量需要在调用persist之前
    del some\u col
    。否则,存储在某些列中的引用将再次导致数据重复

您可以编写如下释放函数:

from distributed.client import futures_of

def release(collection):
    for future in futures_of(collection):
        future.release()
这将只释放当前实例。如果您有多个这些未来的实例,您可能需要多次调用它或添加如下循环:

while future.client.refcount[future.key] > 0:

但是,通常多次调用此函数似乎是不明智的,因为您可能会有其他副本在您的
modify\u in place
函数中包含
df.divisions