web-dev-qa-db-fra.com

Comment sauter des lignes lors de la lecture d'un fichier CSV en tant que dataFrame à l'aide de PySpark?

J'ai un fichier CSV structuré comme suit:

Header
Blank Row
"Col1","Col2"
"1,200","1,456"
"2,000","3,450"

J'ai deux problèmes à lire ce fichier. 

  1. Je veux ignorer l'en-tête et ignorer la ligne vide
  2. Les virgules dans la valeur n'est pas un séparateur

Voici ce que j'ai essayé: 

df = sc.textFile("myFile.csv")\
              .map(lambda line: line.split(","))\ #Split By comma
              .filter(lambda line: len(line) == 2).collect() #This helped me ignore the first two rows

Toutefois, cela n'a pas fonctionné, car les virgules de la valeur étaient lues comme un séparateur et que len(line) en renvoyait 4 au lieu de 2. 

J'ai essayé une approche alternative: 

data = sc.textFile("myFile.csv")
headers = data.take(2) #First two rows to be skipped

L'idée était alors d'utiliser le filtre et de ne pas lire les en-têtes. Mais, lorsque j'ai essayé d'imprimer les en-têtes, j'ai obtenu des valeurs codées. 

[\x00A\x00Y\x00 \x00J\x00u\x00l\x00y\x00 \x002\x000\x001\x006\x00]

Quelle est la bonne façon de lire un fichier CSV et de sauter les deux premières lignes? 

7

Answer by Zlidime a eu la bonne idée. La solution de travail est la suivante:

import csv

customSchema = StructType([ \
    StructField("Col1", StringType(), True), \
    StructField("Col2", StringType(), True)])

df = sc.textFile("file.csv")\
        .mapPartitions(lambda partition: csv.reader([line.replace('\0','') for line in partition],delimiter=',', quotechar='"')).filter(lambda line: len(line) > 2 and line[0] != 'Col1')\
        .toDF(customSchema)
1

Essayez d'utiliser csv.reader avec le paramètre 'quotechar'. Il divisera la ligne correctement . Vous pourrez ensuite ajouter des filtres à votre guise.

import csv
from pyspark.sql.types import StringType

df = sc.textFile("test2.csv")\
           .mapPartitions(lambda line: csv.reader(line,delimiter=',', quotechar='"')).filter(lambda line: len(line)>=2 and line[0]!= 'Col1')\
           .toDF(['Col1','Col2'])
6
zlidime

Pour votre premier problème, il suffit de compresser les lignes dans le RDD avec zipWithIndex et de filtrer les lignes que vous ne voulez pas . diviser la ligne sur ",".

rdd = sc.textFile("myfile.csv")
rdd.zipWithIndex().
    filter(lambda x: x[1] > 2).
    map(lambda x: x[0]).
    map(lambda x: x.strip('"').split('","')).
    toDF(["Col1", "Col2"])

Toutefois, si vous recherchez un moyen standard de gérer les fichiers CSV dans Spark, il est préférable d’utiliser le package spark-csv de databricks.

5
septra

Si la structure de fichier CSV a toujours deux colonnes, Scala peut être implémenté:

val struct = StructType(
  StructField("firstCol", StringType, nullable = true) ::
  StructField("secondCol", StringType, nullable = true) :: Nil)

val df = sqlContext.read
  .format("com.databricks.spark.csv")
  .option("header", "false")
  .option("inferSchema", "false")
  .option("delimiter", ",")
  .option("quote", "\"")
  .schema(struct)
  .load("myFile.csv")

df.show(false)

val indexed = df.withColumn("index", monotonicallyIncreasingId())
val filtered = indexed.filter(col("index") > 2).drop("index")

filtered.show(false)

Le résultat est:

+---------+---------+
|firstCol |secondCol|
+---------+---------+
|Header   |null     |
|Blank Row|null     |
|Col1     |Col2     |
|1,200    |1,456    |
|2,000    |3,450    |
+---------+---------+

+--------+---------+
|firstCol|secondCol|
+--------+---------+
|1,200   |1,456    |
|2,000   |3,450    |
+--------+---------+
1
pasha701

Pourquoi n'essayez-vous pas simplement l'API DataFrameReader de pyspark.sql? C'est assez facile. Pour ce problème, je suppose que cette seule ligne suffirait.

df = spark.read.csv("myFile.csv") # By default, quote char is " and separator is ','

Avec cette API, vous pouvez également jouer avec quelques autres paramètres tels que les lignes d'en-tête, en ignorant les espaces blancs de début et de fin. Voici le lien: API DataFrameReader