如何将groupBy()。count()添加到源DataFrame?

时间:2017-06-08 11:34:36

标签: scala apache-spark apache-spark-sql

我有以下数据框:

+---------------+--------------+--------------+-----+
|        column0|       column1|       column2|label|
+---------------+--------------+--------------+-----+
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|
+---------------+--------------+--------------+-----+

我想应用groupBy并依靠它并产生以下结果:

+--------------+--------------+-----+
|       column1|       column2|count|
+--------------+--------------+-----+
|10.0.0.2.54880| 10.0.0.3.5001|   19|
| 10.0.0.3.5001|10.0.0.2.54880|   10|
+--------------+--------------+-----+

我知道我必须使用它:

dataFrame_Train.groupBy("column1", "column2").count().show()

但问题是我需要将“count”列添加为数据框的永久列。 在上述情况下,如果我在dataFrame_Train.show()之后使用groupBy,我会看到第一个没有“count”列的数据框。这段代码:

dataFrame_Train.groupBy("column1", "column2").count().show()
`dataFrame_Train.show()`

您能帮我将groupBy("column1", "column2").count()添加到数据框吗? (因为我将来需要使用“count”列来训练数据)谢谢。

2 个答案:

答案 0 :(得分:4)

我们将使用您以csv格式提供的相同数据。

让我们阅读这些数据:

scala> val df = spark.read.format("csv").load("data.txt").toDF("column0","column1","column2","label")
// df: org.apache.spark.sql.DataFrame = [column0: string, column1: string ... 2 more fields]

我们现在可以通过聚合执行我们的小组:

scala> val df2 = df.groupBy("column1","column2").count
df2: org.apache.spark.sql.DataFrame = [column1: string, column2: string ... 1 more field]

我们需要做的就是在按键执行分组的相同列上进行等同连接:

scala> val df3 = df.join(df2, Seq("column1","column2"))
df3: org.apache.spark.sql.DataFrame = [column1: string, column2: string ... 3 more fields]

Etvoilà!

scala> df3.show
+--------------+--------------+---------------+-----+-----+                     
|       column1|       column2|        column0|label|count|
+--------------+--------------+---------------+-----+-----+
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604900|    2|   13|
|10.0.0.2.54880| 10.0.0.3.5001|05:49:56.604899|    2|   13|
| 10.0.0.3.5001|10.0.0.2.54880|05:49:56.604908|    2|    7|
+--------------+--------------+---------------+-----+-----+

答案 1 :(得分:4)

@ eliasah的答案很好,但可能不是最有效的,代码和性能方面的。

窗口聚合函数(又名窗口聚合)​​

每当您发现需要groupByjoin时,尤其是对于这样一个简单的用例,请考虑窗口聚合函数

groupBy和窗口聚合之间的主要区别在于,前者最多为您提供源数据集中的行数,而后者(窗口聚合)​​为您提供完全源数据集中的行数。这似乎完全符合您的要求,不是吗?

有了这个,让我们看看代码。

import org.apache.spark.sql.expressions.Window
val columns1and2 = Window.partitionBy("column1", "column2") // <-- matches groupBy

import org.apache.spark.sql.functions._
// using count aggregate function over entire partition frame
val counts = ips.withColumn("count", count($"label") over columns1and2)
scala> counts.show
+---------------+--------------+--------------+-----+-----+
|        column0|       column1|       column2|label|count|
+---------------+--------------+--------------+-----+-----+
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604900|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604899|10.0.0.2.54880| 10.0.0.3.5001|    2|   13|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
|05:49:56.604908| 10.0.0.3.5001|10.0.0.2.54880|    2|    7|
+---------------+--------------+--------------+-----+-----+

完成!干净简单。这是我心爱的窗口聚合功能!

性能比较

现在,有趣的部分来了。这个和@eliasah的解决方案之间的区别仅仅是纯语法吗?我不这么认为(但我还在学习如何得出正确的结论)。查看执行计划并自我判断。

以下是窗口聚合的执行计划。

enter image description here

以下是groupByjoin的执行计划(由于计划太大而无法合并到一个计划中,我不得不拍两张截图)。

enter image description here enter image description here

Job-wise groupByjoin查询轻松打开窗口聚合,前者为2个Spark工作,后者为5个。

操作员,他们的号码,最重要的是Exchange(Spark SQL的shuffle),窗口聚合可能与groupBy一起击败join