web-dev-qa-db-fra.com

PySpark Drop Rows

comment supprimer des lignes d'un RDD dans PySpark? En particulier la première ligne, car elle a tendance à contenir des noms de colonnes dans mes ensembles de données. En parcourant l'API, je n'arrive pas à trouver un moyen facile de le faire. Bien sûr, je pourrais le faire via Bash/HDFS, mais je veux juste savoir si cela peut être fait à partir de PySpark.

20
Jack

AFAIK, il n'y a pas de moyen "facile" de le faire.

Cela devrait faire l'affaire, cependant:

val header = data.first
val rows = data.filter(line => line != header)
18
maasg

Spécifique à PySpark:

Selon @maasg, vous pouvez faire ceci:

header = rdd.first()
rdd.filter(lambda line: line != header)

mais ce n'est pas techniquement correct, car il est possible d'exclure des lignes contenant des données ainsi que l'en-tête. Cependant, cela semble fonctionner pour moi:

def remove_header(itr_index, itr):
    return iter(list(itr)[1:]) if itr_index == 0 else itr
rdd.mapPartitionsWithIndex(remove_header)

De même:

rdd.zipWithIndex().filter(lambda tup: tup[1] > 0).map(lambda tup: tup[0])

Je suis nouveau sur Spark, je ne peux donc pas commenter intelligemment lequel sera le plus rapide.

20
user4081921

Un moyen simple d'y parvenir dans PySpark (API Python), en supposant que vous utilisez Python 3:

noHeaderRDD = rawRDD.zipWithIndex().filter(lambda row_index: row_index[1] > 0).keys()
5
noleto

J'ai fait du profilage avec différentes solutions et j'ai les éléments suivants

Configuration de cluster

Clusters

  • Cluster 1: 4 cœurs 16 Go
  • Cluster 2: 4 cœurs 16 Go
  • Cluster 3: 4 cœurs 16 Go
  • Cluster 4: 2 cœurs 8 Go

Les données

7 millions de lignes, 4 colonnes

#Solution 1
# Time Taken : 40 ms
data=sc.TextFile('file1.txt')
firstRow=data.first()
data=data.filter(lambda row:row != firstRow)

#Solution 2
# Time Taken : 3 seconds
data=sc.TextFile('file1.txt')
def dropFirstRow(index,iterator):
     return iter(list(iterator)[1:]) if index==0 else iterator
data=data.mapPartitionsWithIndex(dropFirstRow)

#Solution 3
# Time Taken : 0.3 seconds
data=sc.TextFile('file1.txt')
def dropFirstRow(index,iterator):
     if(index==0):
          for subIndex,item in enumerate(iterator):
               if subIndex > 0:
                    yield item
     else:
          yield iterator

data=data.mapPartitionsWithIndex(dropFirstRow)

Je pense que la solution 3 est la plus évolutive

2
Anant Gupta

J'ai testé avec spark2.1. Supposons que vous souhaitiez supprimer les 14 premières lignes sans connaître le nombre de colonnes du fichier.

sc = spark.sparkContext
lines = sc.textFile("s3://location_of_csv")
parts = lines.map(lambda l: l.split(","))
parts.zipWithIndex().filter(lambda tup: tup[1] > 14).map(lambda x:x[0])

withColumn est une fonction df. Donc, ci-dessous ne fonctionnera pas dans le style RDD comme utilisé dans le cas ci-dessus.

parts.withColumn("index",monotonically_increasing_id()).filter(index > 14)
1
kartik

Personnellement, je pense que l'utilisation d'un filtre pour se débarrasser de ces trucs est le moyen le plus simple. Mais selon votre commentaire, j'ai une autre approche. Glom le RDD pour que chaque partition soit un tableau (je suppose que vous avez 1 fichier par partition, et chaque fichier a la ligne incriminée en haut), puis sautez simplement le premier élément (c'est avec le scala api).

data.glom().map(x => for (elem <- x.drop(1){/*do stuff*/}) //x is an array so just skip the 0th index

Gardez à l'esprit que l'une des grandes caractéristiques des RDD est qu'ils sont immuables, donc supprimer naturellement une ligne est une chose délicate à faire

MISE À JOUR: Meilleure solution.
rdd.mapPartions(x => for (elem <- x.drop(1){/*do stuff*/} )
Identique au glom mais n'a pas la surcharge de tout mettre dans un tableau, car x est un itérateur dans ce cas

1
aaronman