web-dev-qa-db-ja.com

sparkデータフレームで重複しているすべてのレコードを削除します

複数の列を含むsparkデータフレームがあります。列の値が重複している行を見つけて削除したいと思います(他の列は異なる場合があります)。

dropDuplicates(col_name)を使用してみましたが、重複するエントリのみが削除され、データフレームに1つのレコードが保持されます。必要なのは、最初は重複したエントリを含んでいたすべてのエントリを削除することです。

私はSpark 1.6およびScala 2.10を使用しています。

4
salmanbw

これにはウィンドウ関数を使用します。重複するid行を削除するとします:

import org.Apache.spark.sql.expressions.Window

df
  .withColumn("cnt", count("*").over(Window.partitionBy($"id")))
  .where($"cnt"===1).drop($"cnt")
  .show()
8
Raphael Roth

これは、1つまたは複数の列でグループ化して重複を探し、結果を集計してフィルタリングすることで実行できます。

データフレームの例df

_+---+---+
| id|num|
+---+---+
|  1|  1|
|  2|  2|
|  3|  3|
|  4|  4|
|  4|  5|
+---+---+
_

重複を削除するためのid列によるグループ化(最後の2行):

_val df2 = df.groupBy("id")
  .agg(first($"num").as("num"), count($"id").as("count"))
  .filter($"count" === 1)
  .select("id", "num")
_

これはあなたに与えるでしょう:

_+---+---+
| id|num|
+---+---+
|  1|  1|
|  2|  2|
|  3|  3|
+---+---+
_

または、joinを使用して実行することもできます。遅くなりますが、列が多い場合は、列を保持するために各列にfirst($"num").as("num")を使用する必要はありません。

_val df2 = df.groupBy("id").agg(count($"id").as("count")).filter($"count" === 1).select("id")
val df3 = df.join(df2, Seq("id"), "inner")
_

オープンソースにkillDuplicates()メソッドを追加しました spark-daria @RaphaelRothのソリューションを使用するライブラリ。コードの使用方法は次のとおりです。

import com.github.mrpowers.spark.daria.sql.DataFrameExt._

df.killDuplicates(col("id"))

// you can also supply multiple Column arguments
df.killDuplicates(col("id"), col("another_column"))

コードの実装は次のとおりです。

object DataFrameExt {

  implicit class DataFrameMethods(df: DataFrame) {

    def killDuplicates(cols: Column*): DataFrame = {
      df
        .withColumn(
          "my_super_secret_count",
          count("*").over(Window.partitionBy(cols: _*))
        )
        .where(col("my_super_secret_count") === 1)
        .drop(col("my_super_secret_count"))
    }

  }

}

このロジックをコードベースから除外するために、spark-dariaライブラリを活用することをお勧めします。

0
Powers