web-dev-qa-db-ja.com

spark dataframe scalaの列値に基づいて行をフィルタリングする

私はデータフレーム(スパーク)を持っています:

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

新しいデータフレームを作成したい:

3 0
3 1
4 1

1(value)の後のすべての行を各idで削除する必要があります。spark dateframe(Scala)のウィンドウ関数で試しました。しかし、できませんでした。私は間違った方向に進んでいるようです。

Scalaで解決策を探しています。ありがとう

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に基づいて生成されます。

すべての提案を歓迎します。

9
John

1つの方法は、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()
_

ここで、各idmin(idx)を取得します。ここで_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を使用して上記のステップごとに評価できるように、値を同じままにする場合は、上記のこの行のコメントを解除します。

_//  dataWithIndex.cache()
_
8
David Griffin

こんにちは、私はウィンドウと自己結合を使用してソリューションを見つけました。

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|
+---+-----+------+-----------+---+-------+

さらに最適化されたソリューションを探しています。ありがとう

1
John
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|
+---+-----+------+
0
mputha

このように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
0
Abu Shoeb