我一直在研究aSspark应用程序并尝试转换数据帧,如表1所示。我想将列的每个元素(_2)除以另一个元素(同一列)的元素之和列(_1)。表2是预期结果。
表1
+---+----+
| _1| _2 |
+---+----+
| 0|13/x|
| 0| 7/x|
| 0| 3/x|
| 0| 1/x|
| 0| 1/x|
| 1| 4/y|
| 1| 8/y|
| 1|18/y|
| 1| 4/y|
+---+----+
表2
+---+---------+
| _1| ENTROPY |
+---+---------+
| 0|entropy_1|
| 1|entropy_2|
+---+---------+
其中,x =(13 + 7 + 3 + 1 + 1)和y =(4 + 8 + 18 + 4)
然后,我想计算_1列中每个元素的熵: 即,对于_1列中的每个元素,在_2列中计算 sum(p_i x log(p_i)) 。其中,p_i基本上是表2 中_1列中每个值的_2列中的值。
最终输出将是。
{{1}}
我如何在spark(最好是scala)中实现它?执行上述操作的优化方法是什么?我是scala的新手,任何相关的建议都将受到高度赞赏。
谢谢。
答案 0 :(得分:4)
如果您想要一个简洁的解决方案,并且群组相当小,您可以使用窗口功能。首先,您必须定义一个窗口:
import org.apache.spark.sql.expressions.Window
val w = Window.partitionBy("_1").rowsBetween(Long.MinValue, Long.MaxValue)
概率:
import org.apache.spark.sql.functions.sum
val p = $"_2" / sum($"_2").over(w)
val withP = df.withColumn("p", p)
最后是熵:
import org.apache.spark.sql.functions.log2
withP.groupBy($"_1").agg((-sum($"p" * log2($"p"))).alias("entropy"))
对于示例数据
val df = Seq(
(0, 13), (0, 7), (0, 3), (0, 1), (0, 1), (1, 4), (1, 8), (1, 18), (1, 4)).toDF
结果是:
+---+------------------+
| _1| entropy|
+---+------------------+
| 1|1.7033848993102918|
| 0|1.7433726580786888|
+---+------------------+
如果窗口函数在性能方面不可接受,您可以尝试聚合 - 连接聚合:
df.groupBy($"_1").agg(sum("_2").alias("total"))
.join(df, Seq("_1"), "inner")
.withColumn("p", $"_2" / $"total")
.groupBy($"_1").agg((-sum($"p" * log2($"p"))).alias("entropy"))
其中:
df.groupBy($"_1").agg(sum("_2").alias("total"))
按_2
,
_1
的总和
_.join(df, Seq("_1"), "inner")
将聚合列添加到原始数据
_.withColumn("p", $"_2" / $"total")
计算概率,并且:
_.groupBy($"_1").agg((-sum($"p" * log2($"p"))).alias("entropy"))
聚合以获得熵。