web-dev-qa-db-fra.com

Suppression des doublons des lignes en fonction de colonnes spécifiques dans un fichier de données RDD/Spark

Disons que j'ai un ensemble de données plutôt volumineux sous la forme suivante:

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

Ce que je voudrais faire est de supprimer les lignes en double en fonction des valeurs des première, troisième et quatrième colonnes uniquement. 

Supprimer des lignes en double est simple:

data = data.distinct()

et soit la ligne 5 ou la ligne 6 seront supprimés

Mais comment ne supprimer que les lignes en double basées sur les colonnes 1, 3 et 4 uniquement? c'est-à-dire supprimer l'un ou l'autre de ceux-ci:

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

En Python, cela pourrait être fait en spécifiant des colonnes avec .drop_duplicates(). Comment puis-je obtenir le même résultat dans Spark/Pyspark? 

36
Jason

Pyspark ne inclut une méthode 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|
+---+------+-----+

Peut-être qu’il a été introduit dans une version plus récente que celle utilisée par @Jason (OP)?

edit: oui, il a été introduit en 1.4

42
vaer-k

D'après votre question, il est difficile de savoir quelles colonnes vous souhaitez utiliser pour déterminer les doublons. L'idée générale derrière la solution est de créer une clé basée sur les valeurs des colonnes identifiant les doublons. Ensuite, vous pouvez utiliser la réduction ou réduire les opérations pour éliminer les doublons.

Voici du code pour vous aider à démarrer:

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

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

Maintenant, vous avez une valeur-clé RDD qui est associée aux colonnes 1, 3 et 4. L’étape suivante serait reduceByKey ou groupByKey et filter. Cela éliminerait les doublons.

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

D'accord avec David. Pour ajouter, peut ne pas être le cas le cas où nous voulons groupBy toutes les colonnes autres que la ou les colonnes de la fonction d'agrégat, c'est-à-dire si nous voulons supprimer les doublons uniquement à partir d'un sous-ensemble des colonnes et conserver toutes les colonnes dans le cadre de données d'origine. Donc, le meilleur moyen de le faire pourrait être d'utiliser dropDuplicates api Dataframe disponible dans Spark 1.4.0

Pour référence, voir: https://spark.Apache.org/docs/1.4.0/api/scala/index.html#org.Apache.spark.sql.DataFrame

11
technotring

Je sais que vous avez déjà accepté l’autre réponse, mais si vous voulez le faire en tant que DataFrame, utilisez simplement groupBy et agg. En supposant que vous ayez déjà créé un DF (avec des colonnes nommées "col1", "col2", etc.), vous pourriez faire:

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

Notez que dans ce cas, j'ai choisi le maximum de col2, mais vous pouvez faire avg, min, etc.

10
David Griffin

J'ai utilisé la fonction intégrée dropDuplicates (). Code Scala donné ci-dessous

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

Sortie:

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