我正在处理一个复杂的应用程序。根据源数据,我们可以计算许多统计数据,例如。
<video autoplay webkit-playsinline playsinline style="width: 500px">
<source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4">
</video>
由于数据框被分组在同一列上,因此结果数据框随后被分组在一起:
.container {
max-width: 1200px;
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.text {
font-size: 120px;
font-weight: 900;
}
.container .text .text-row span {
float: left;
}
(在我的代码中,这是循环完成的)
这不是高效能的,问题在于每个数据帧(我大约有30个)以一个动作结束,因此据我了解,每个数据帧都经过计算并返回给驱动程序,然后驱动程序将数据发回执行程序以执行联接
这给了我内存错误,我可以增加驱动程序的内存,但是我正在寻找一种更好的方法。对于前。如果所有数据帧仅在最后计算(保存连接的数据帧),我想一切都将由集群管理。
有没有办法做一种懒惰的动作?还是应该以其他方式加入数据框?
Thx
答案 0 :(得分:0)
首先,您显示的代码仅包含一个类似操作的操作-DataFrameWriter.save
。所有其他组件都是惰性的。
但是懒惰在这里并没有真正帮助您。最大的问题(假设没有难看的数据偏斜或配置错误的广播)是各个聚合需要单独的混洗和昂贵的后续合并。
一个幼稚的解决方案是利用:
数据框分组在同一列上
先洗牌
val groupColumns: Seq[Column] = ???
val sourceDataPartitioned = sourceData.groupBy(groupColumns: _*)
并使用结果来计算单个聚合
val df1 = sourceDataPartitioned
...
val df2 = sourceDataPartitioned
...
但是,这种方法比较脆弱,并且在存在大型/偏斜群体的情况下不太可能扩展。
因此,最好重写代码以仅执行聚合。幸运的是,standard SQL behavior就是您所需要的。
让我们开始使用以下代码将代码结构化为三个元素元组:
_1
是谓词(您在filter
中使用的条件)。_2
是要为其计算聚合的Columns
的列表。_3
是一个聚合函数。示例结构如下所示:
import org.apache.spark.sql.Column
import org.apache.spark.sql.functions.{count, min}
val ops: Seq[(Column, Seq[Column], Column => Column)] = Seq(
($"col1" === "a" and $"col2" === "b", Seq($"col3", $"col4"), count),
($"col2" === "b" and $"col3" === "c", Seq($"col4", $"col5"), min)
)
现在,您可以使用
编写聚合表达式agg_function(when(predicate, column))
模式
import org.apache.spark.sql.functions.when
val exprs: Seq[Column] = ops.flatMap {
case (p, cols, f) => cols.map {
case c => f(when(p, c))
}
}
并在sourceData
sourceData.groupBy(groupColumns: _*).agg(exprs.head, exprs.tail: _*)
在必要时添加aliases
。