我必须处理一个巨大的数据框,并通过该数据框的id
列从服务中下载文件。下载的逻辑以及所有更改已准备就绪,但是我不确定围绕此循环的最佳方法是什么。我在 Databricks 上运行它,这就是为什么我需要分块执行这些过程的原因。
数据框具有“ 状态”列,其中可以包含以下值:
“待办事项”,“处理”,“失败”,“成功”
在while循环中,我要执行以下任务:
while (there are rows with status "todo") {
- get the first 10 rows if status is todo (DONE)
- start processing the dataframe, update status to processing (DONE)
- download files (call UDF), update status to succeeded or failed
(DONE, not in the code here)
}
我要运行此,直到所有行中的status
都不是todo
!问题在于,while循环尚未完成,因为数据帧本身未更新。需要将其分配给另一个数据帧,但是如何将新的数据帧添加到循环中?
我现在的代码:
while(statusDoc.where("status == 'todo'").count > 0) {
val todoDF = test.filter("status == 'todo'")
val processingDF = todoDF.limit(10).withColumn("status", when(col("status") === "todo", "processing")
.otherwise(col("status")))
statusDoc.join(processingDF, Seq("id"), "outer")
.select($"id", \
statusDoc("fileUrl"), \
coalesce(processingDF("status"), statusDoc("status")).alias("status"))
}
联接应如下所示:
val update = statusDoc.join(processingDF, Seq("id"), "outer")
.select($"id", statusDoc("fileUrl"),\
coalesce(processingDF("status"), statusDoc("status")).alias("status"))
然后,此新的update
数据帧应用于下一轮循环。
答案 0 :(得分:1)
这里要记住的一件事是DataFrame(Spark)不可更改,因为它们是分布式的。如果您进行了某些修改,则不能保证给定的修改会在所有执行者网络中正确传播。而且,您也不能保证数据的给定部分尚未在其他地方(例如在另一个节点中)使用。
您可以做的一件事是添加另一列具有更新后的值,然后删除旧列。
val update = statusDoc.
.withColumnRenamed("status", "status_doc")
.join(processingDF, Seq("id"), "outer")
.withColumn("updated_status", udf((stold: String, stold: String) => if (stnew != null) stnew else stold).apply(col("status"), col("status_doc"))
.drop("status_doc", "status")
.withColumnRenamed("updated_status", "status")
.select("id", "fileUrl", "status")
然后确保将“ statusDoc”替换为“ update” DataFrame。不要忘记将DataFrame设为“ var”,而不是“ val”。我很惊讶您的IDE还没有大喊大叫。
此外,我确定您可以想到一种分发问题的方式,从而避免了while循环-我可以为您提供帮助,但是我需要更清晰地描述您的问题。如果使用while循环,则不会使用群集的全部功能,因为while循环仅在主服务器上执行。然后,您一次只能处理10条线。我确定您可以通过一次映射操作将所需的所有数据附加到整个DataFrame中。