假设我想编写一个转换DataFrame的函数foo:
#!/bin/bash
for filename in foo*.txt;
do
echo "${filename}" | grep -Eo '[0-9]{1,4}';
done
由于foo的实现有很多“动作”(收集,减少等),调用foo会立即触发昂贵的执行。
这不是一个大问题,但是因为foo只将DataFrame转换为另一个,按照惯例,允许延迟执行应该更好:foo的实现只有在生成的DataFrame或其衍生物是在驱动程序上使用(通过另一个“动作”)。
到目前为止,可靠地实现这一目标的唯一方法是将所有实现写入SparkPlan,并将其叠加到DataFrame的SparkExecution中,这非常容易出错,并且涉及大量的样板代码。建议的方法是什么?
答案 0 :(得分:4)
我并不完全清楚您尝试实现的目标,但Scala本身至少提供了一些您可能会觉得有用的工具:
lazy vals:
val rdd = sc.range(0, 10000)
lazy val count = rdd.count // Nothing is executed here
// count: Long = <lazy>
count // count is evaluated only when it is actually used
// Long = 10000
按名称调用(在函数定义中用=>
表示):
def foo(first: => Long, second: => Long, takeFirst: Boolean): Long =
if (takeFirst) first else second
val rdd1 = sc.range(0, 10000)
val rdd2 = sc.range(0, 10000)
foo(
{ println("first"); rdd1.count },
{ println("second"); rdd2.count },
true // Only first will be evaluated
)
// first
// Long = 10000
注意:实际上,您应该创建本地延迟绑定,以确保不会在每次访问时评估参数。
无限懒惰的集合,例如Stream
import org.apache.spark.mllib.random.RandomRDDs._
val initial = normalRDD(sc, 1000000L, 10)
// Infinite stream of RDDs and actions and nothing blows :)
val stream: Stream[RDD[Double]] = Stream(initial).append(
stream.map {
case rdd if !rdd.isEmpty =>
val mu = rdd.mean
rdd.filter(_ > mu)
case _ => sc.emptyRDD[Double]
}
)
这些中的某些子集应足以实现复杂的延迟计算。