根据spark dataframe scala中的列值过滤行

时间:2016-04-02 15:13:51

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

我有一个数据框(spark):

id  value 
3     0
3     1
3     0
4     1
4     0
4     0

我想创建一个新的数据框:

3 0
3 1
4 1

需要为每个id删除1(值)之后的所有行。我尝试使用spark dateframe(Scala)中的窗口函数。但无法找到解决方案。看起来我的方向是错误的。

我正在寻找Scala.Thanks

的解决方案

使用monotonically_increasing_id输出

 scala> val data = Seq((3,0),(3,1),(3,0),(4,1),(4,0),(4,0)).toDF("id", "value")
data: org.apache.spark.sql.DataFrame = [id: int, value: int]

scala> val minIdx = dataWithIndex.filter($"value" === 1).groupBy($"id").agg(min($"idx")).toDF("r_id", "min_idx")
minIdx: org.apache.spark.sql.DataFrame = [r_id: int, min_idx: bigint]

scala> dataWithIndex.join(minIdx,($"r_id" === $"id") && ($"idx" <= $"min_idx")).select($"id", $"value").show
+---+-----+
| id|value|
+---+-----+
|  3|    0|
|  3|    1|
|  4|    1|
+---+-----+

如果我们在原始数据框中进行了排序转换,那么解决方案就无法工作。那个时候 monotonically_increasing_id()是基于原始DF生成的,而不是排序DF.I之前已经错过了这个要求。

欢迎所有建议。

4 个答案:

答案 0 :(得分:8)

一种方法是使用monotonically_increasing_id()和自我加入:

val data = Seq((3,0),(3,1),(3,0),(4,1),(4,0),(4,0)).toDF("id", "value")
data.show
+---+-----+
| id|value|
+---+-----+
|  3|    0|
|  3|    1|
|  3|    0|
|  4|    1|
|  4|    0|
|  4|    0|
+---+-----+

现在,我们生成一个名为idx的列,其Long增加:

val dataWithIndex = data.withColumn("idx", monotonically_increasing_id())
// dataWithIndex.cache()

现在我们得到min(idx)的{​​{1}} id

value = 1

现在我们将val minIdx = dataWithIndex .filter($"value" === 1) .groupBy($"id") .agg(min($"idx")) .toDF("r_id", "min_idx") 加入原来的min(idx)

DataFrame

注意: dataWithIndex.join( minIdx, ($"r_id" === $"id") && ($"idx" <= $"min_idx") ).select($"id", $"value").show +---+-----+ | id|value| +---+-----+ | 3| 0| | 3| 1| | 4| 1| +---+-----+ 根据行的分区生成其值。每次重新评估monotonically_increasing_id()时,此值可能会更改。在我的上面的代码中,由于懒惰的评估,只有当我调用dataWithIndex show进行评估时才会这样做。

如果您想强制值保持不变,例如,您可以使用monotonically_increasing_id()评估上述步骤,请取消注释以上这一行:

show

答案 1 :(得分:1)

您好我找到了使用Window和自我加入的解决方案。

val data = Seq((3,0,2),(3,1,3),(3,0,1),(4,1,6),(4,0,5),(4,0,4),(1,0,7),(1,1,8),(1,0,9),(2,1,10),(2,0,11),(2,0,12)).toDF("id", "value","sorted")

data.show

scala> data.show
+---+-----+------+
| id|value|sorted|
+---+-----+------+
|  3|    0|     2|
|  3|    1|     3|
|  3|    0|     1|
|  4|    1|     6|
|  4|    0|     5|
|  4|    0|     4|
|  1|    0|     7|
|  1|    1|     8|
|  1|    0|     9|
|  2|    1|    10|
|  2|    0|    11|
|  2|    0|    12|
+---+-----+------+




val sort_df=data.sort($"sorted")

scala> sort_df.show
+---+-----+------+
| id|value|sorted|
+---+-----+------+
|  3|    0|     1|
|  3|    0|     2|
|  3|    1|     3|
|  4|    0|     4|
|  4|    0|     5|
|  4|    1|     6|
|  1|    0|     7|
|  1|    1|     8|
|  1|    0|     9|
|  2|    1|    10|
|  2|    0|    11|
|  2|    0|    12|
+---+-----+------+



var window=Window.partitionBy("id").orderBy("$sorted")

 val sort_idx=sort_df.select($"*",rowNumber.over(window).as("count_index"))

val minIdx=sort_idx.filter($"value"===1).groupBy("id").agg(min("count_index")).toDF("idx","min_idx")

val result_id=sort_idx.join(minIdx,($"id"===$"idx") &&($"count_index" <= $"min_idx"))

result_id.show

+---+-----+------+-----------+---+-------+
| id|value|sorted|count_index|idx|min_idx|
+---+-----+------+-----------+---+-------+
|  1|    0|     7|          1|  1|      2|
|  1|    1|     8|          2|  1|      2|
|  2|    1|    10|          1|  2|      1|
|  3|    0|     1|          1|  3|      3|
|  3|    0|     2|          2|  3|      3|
|  3|    1|     3|          3|  3|      3|
|  4|    0|     4|          1|  4|      3|
|  4|    0|     5|          2|  4|      3|
|  4|    1|     6|          3|  4|      3|
+---+-----+------+-----------+---+-------+

仍在寻找更优化的解决方案。谢谢

答案 2 :(得分:0)

您可以像这样简单地使用groupBy

val df2 = df1.groupBy("id","value").count().select("id","value")

您的df1

id  value 
3     0
3     1
3     0
4     1
4     0
4     0

结果数据帧为df2,这是您预期的输出

id  value 
3     0
3     1
4     1
4     0

答案 3 :(得分:0)

use isin method and filter as below:

val data = Seq((3,0,2),(3,1,3),(3,0,1),(4,1,6),(4,0,5),(4,0,4),(1,0,7),(1,1,8),(1,0,9),(2,1,10),(2,0,11),(2,0,12)).toDF("id", "value","sorted")
val idFilter = List(1, 2)
 data.filter($"id".isin(idFilter:_*)).show
+---+-----+------+
| id|value|sorted|
+---+-----+------+
|  1|    0|     7|
|  1|    1|     8|
|  1|    0|     9|
|  2|    1|    10|
|  2|    0|    11|
|  2|    0|    12|
+---+-----+------+

Ex: filter based on val
val valFilter = List(0)
data.filter($"value".isin(valFilter:_*)).show
+---+-----+------+
| id|value|sorted|
+---+-----+------+
|  3|    0|     2|
|  3|    0|     1|
|  4|    0|     5|
|  4|    0|     4|
|  1|    0|     7|
|  1|    0|     9|
|  2|    0|    11|
|  2|    0|    12|
+---+-----+------+