web-dev-qa-db-de.com

Entfernen von Duplikaten aus Zeilen basierend auf bestimmten Spalten in einem RDD/Spark-DataFrame

Nehmen wir an, ich habe einen ziemlich großen Datensatz in der folgenden Form:

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)])

Ich würde gerne doppelte Zeilen basierend auf den Werten der ersten, dritten und vierten Spalte entfernen. 

Das Entfernen vollständig doppelter Zeilen ist unkompliziert:

data = data.distinct()

und entweder Zeile 5 oder Zeile 6 wird entfernt

Aber wie entferne ich nur doppelte Zeilen, die nur auf den Spalten 1, 3 und 4 basieren? d. h. eine der folgenden entfernen:

('Baz',22,'US',6)
('Baz',36,'US',6)

In Python kann dies durch Angabe von Spalten mit .drop_duplicates() erfolgen. Wie kann ich in Spark/Pyspark dasselbe erreichen? 

36
Jason

Pyspark do enthält eine dropDuplicates()-Methode. 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|
+---+------+-----+

Vielleicht wurde es in einer späteren Version als das, was @Jason (OP) verwendete, eingeführt?

edit: ja, es wurde in 1.4 eingeführt

42
vaer-k

Aus Ihrer Frage ist nicht klar, welche Spalten Sie zur Ermittlung von Duplikaten verwenden möchten. Die Grundidee der Lösung besteht darin, einen Schlüssel basierend auf den Werten der Spalten zu erstellen, die Duplikate identifizieren. Anschließend können Sie mit den Funktionen reduziByKey oder verkleinern Duplikate entfernen.

Hier ist ein Code zum Einstieg:

def get_key(x):
    return "{0}{1}{2}".format(x[0],x[2],x[3])

m = data.map(lambda x: (get_key(x),x))

Nun haben Sie einen Schlüsselwert RDD, den die Spalten 1,3 und 4 verwenden Der nächste Schritt wäre entweder reduceByKey oder groupByKey und filter. Dadurch würden Duplikate eliminiert.

r = m.reduceByKey(lambda x,y: (x))
22
Mike

Stimmen Sie David zu. Zum Hinzufügen von möglicherweise nicht ist es der Fall, dass groupBy alle Spalten außer den Spalten in der Aggregatfunktion sein soll, dh wenn Duplikate rein auf der Basis einer Teilmenge entfernt werden soll Spalten beibehalten und alle Spalten im ursprünglichen Datenrahmen beibehalten. Der bessere Weg, dies zu tun, könnte also die Verwendung von dropDuplicates Dataframe api in Spark 1.4.0 sein

Weitere Informationen finden Sie unter: https://spark.Apache.org/docs/1.4.0/api/scala/index.html#org.Apache.spark.sql.DataFrame

11
technotring

Ich weiß, dass Sie die andere Antwort bereits akzeptiert haben, aber wenn Sie dies als DataFrame tun möchten, verwenden Sie einfach groupBy und agg. Angenommen, Sie haben bereits ein DF erstellt (mit Spalten mit den Namen "col1", "col2" usw.), könnten Sie Folgendes tun:

myDF.groupBy($"col1", $"col3", $"col4").agg($"col1", max($"col2"), $"col3", $"col4")

Beachten Sie, dass ich in diesem Fall den Max von Col2 gewählt habe, aber Sie könnten avg, min usw. machen.

10
David Griffin

Ich habe die eingebaute Funktion dropDuplicates () verwendet. Scala-Code unten angegeben

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()

Ausgabe :

+---+---+---+-----+
|  x|  y|  z|count|
+---+---+---+-----+
|Baz| 22| US|    6|
|Foo| 39| UK|    1|
|Foo| 41| US|    3|
|Bar| 57| CA|    2|
+---+---+---+-----+

Mit dem folgenden Programm können Sie Duplikate ganz löschen. Wenn Sie Duplikate anhand bestimmter Spalten löschen möchten, können Sie dies sogar tun:

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()

 }
 }
0
Sampat Kumar