次の形式のかなり大きなデータセットがあるとします。
data = sc.parallelize([('Foo',41,'US',3),
('Foo',39,'UK',1),
('Bar',57,'CA',2),
('Bar',72,'CA',2),
('Baz',22,'US',6),
('Baz',36,'US',6)])
私がしたいのは、1番目、3番目、4番目の列の値のみに基づいて重複する行を削除することです。
完全に重複した行の削除は簡単です。
data = data.distinct()
行5または行6のいずれかが削除されます
しかし、列1、3、および4のみに基づいて重複行のみを削除するにはどうすればよいですか?つまり、次のいずれかを削除します:
('Baz',22,'US',6)
('Baz',36,'US',6)
Pythonでは、.drop_duplicates()
で列を指定することでこれを行うことができます。 Spark/Pysparkで同じことを実現するにはどうすればよいですか?
PysparkdoesにはdropDuplicates()
メソッドが含まれます。 https://spark.Apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.DataFrame.dropDuplicates
>>> from pyspark.sql import Row
>>> df = sc.parallelize([ \
... Row(name='Alice', age=5, height=80), \
... Row(name='Alice', age=5, height=80), \
... Row(name='Alice', age=10, height=80)]).toDF()
>>> df.dropDuplicates().show()
+---+------+-----+
|age|height| name|
+---+------+-----+
| 5| 80|Alice|
| 10| 80|Alice|
+---+------+-----+
>>> df.dropDuplicates(['name', 'height']).show()
+---+------+-----+
|age|height| name|
+---+------+-----+
| 5| 80|Alice|
+---+------+-----+
@Jason(OP)が使用していたものよりも後のバージョンで導入されたのでしょうか?
編集:はい、それは1.4で導入されました
あなたの質問から、重複を決定するためにどの列を使用したいかは不明です。ソリューションの背後にある一般的な考え方は、重複を識別する列の値に基づいてキーを作成することです。その後、reduceByKeyまたはreduce操作を使用して、重複を排除できます。
始めるためのコードを次に示します。
def get_key(x):
return "{0}{1}{2}".format(x[0],x[2],x[3])
m = data.map(lambda x: (get_key(x),x))
これで、列1,3および4でキー設定されるキー値RDD
があります。次のステップは、reduceByKey
またはgroupByKey
およびfilter
のいずれかです。これにより、重複がなくなります。
r = m.reduceByKey(lambda x,y: (x))
既に他の回答を受け入れていることは承知していますが、これをDataFrameとして行いたい場合は、groupByとaggを使用してください。 DFが既に作成されていると仮定すると(「col1」、「col2」などの名前の列で)、次のことができます。
myDF.groupBy($"col1", $"col3", $"col4").agg($"col1", max($"col2"), $"col3", $"col4")
この場合、col2のMaxを選択しましたが、avg、minなどを実行できることに注意してください。
デビッドに同意します。追加するために、ではないかもしれません、groupByを除くすべての列集計関数の列。つまり、列のサブセットのみに基づいて重複を削除し、元のデータフレームのすべての列を保持する場合。したがって、これを行うためのより良い方法は、dropDuplicatesを使用することです。Spark 1.4.0
参照については、以下を参照してください: https://spark.Apache.org/docs/1.4.0/api/scala/index.html#org.Apache.spark.sql.DataFrame
組み込み関数dropDuplicates()を使用しました。 Scala以下のコード
val data = sc.parallelize(List(("Foo",41,"US",3),
("Foo",39,"UK",1),
("Bar",57,"CA",2),
("Bar",72,"CA",2),
("Baz",22,"US",6),
("Baz",36,"US",6))).toDF("x","y","z","count")
data.dropDuplicates(Array("x","count")).show()
出力:
+---+---+---+-----+
| x| y| z|count|
+---+---+---+-----+
|Baz| 22| US| 6|
|Foo| 39| UK| 1|
|Foo| 41| US| 3|
|Bar| 57| CA| 2|
+---+---+---+-----+
以下のプログラムは、重複を全体として削除するのに役立ちます、または特定の列に基づいて重複を削除する場合は、それを行うこともできます:
import org.Apache.spark.sql.SparkSession
object DropDuplicates {
def main(args: Array[String]) {
val spark =
SparkSession.builder()
.appName("DataFrame-DropDuplicates")
.master("local[4]")
.getOrCreate()
import spark.implicits._
// create an RDD of tuples with some data
val custs = Seq(
(1, "Widget Co", 120000.00, 0.00, "AZ"),
(2, "Acme Widgets", 410500.00, 500.00, "CA"),
(3, "Widgetry", 410500.00, 200.00, "CA"),
(4, "Widgets R Us", 410500.00, 0.0, "CA"),
(3, "Widgetry", 410500.00, 200.00, "CA"),
(5, "Ye Olde Widgete", 500.00, 0.0, "MA"),
(6, "Widget Co", 12000.00, 10.00, "AZ")
)
val customerRows = spark.sparkContext.parallelize(custs, 4)
// convert RDD of tuples to DataFrame by supplying column names
val customerDF = customerRows.toDF("id", "name", "sales", "discount", "state")
println("*** Here's the whole DataFrame with duplicates")
customerDF.printSchema()
customerDF.show()
// drop fully identical rows
val withoutDuplicates = customerDF.dropDuplicates()
println("*** Now without duplicates")
withoutDuplicates.show()
// drop fully identical rows
val withoutPartials = customerDF.dropDuplicates(Seq("name", "state"))
println("*** Now without partial duplicates too")
withoutPartials.show()
}
}