web-dev-qa-db-fra.com

Comment convertir le fichier csv en rdd

Je suis nouveau pour susciter. Je souhaite effectuer certaines opérations sur des données particulières d'un enregistrement CSV.

J'essaie de lire un fichier CSV et de le convertir en RDD. Mes autres opérations sont basées sur l’en-tête fourni dans le fichier CSV.

(D'après les commentaires) Ceci est mon code jusqu'à présent:

final JavaRDD<String> File = sc.textFile(Filename).cache();
final JavaRDD<String> lines = File.flatMap(new FlatMapFunction<String, String>() { 
    @Override public Iterable<String> call(String s) { 
    return Arrays.asList(EOL.split(s)); 
    } 
});
final String heading=lines.first().toString();

Je peux obtenir les valeurs d'en-tête comme ceci. Je veux mapper cela à chaque enregistrement dans un fichier CSV.

final String[] header=heading.split(" "); 

Je peux obtenir les valeurs d'en-tête comme ceci. Je veux mapper cela à chaque enregistrement dans un fichier CSV.

Dans Java j'utilise CSVReader record.getColumnValue(Column header)] pour obtenir la valeur particulière. Je dois faire quelque chose de similaire à cela ici.

49
Ramya

Une approche simpliste consisterait à avoir un moyen de préserver l’en-tête.

Disons que vous avez un fichier.csv comme:

user, topic, hits
om,  scala, 120
daniel, spark, 80
3754978, spark, 1

Nous pouvons définir une classe d'en-tête qui utilise une version analysée de la première ligne:

class SimpleCSVHeader(header:Array[String]) extends Serializable {
  val index = header.zipWithIndex.toMap
  def apply(array:Array[String], key:String):String = array(index(key))
}

Que nous pouvons utiliser cet en-tête pour traiter les données plus tard:

val csv = sc.textFile("file.csv")  // original file
val data = csv.map(line => line.split(",").map(elem => elem.trim)) //lines in rows
val header = new SimpleCSVHeader(data.take(1)(0)) // we build our header with the first line
val rows = data.filter(line => header(line,"user") != "user") // filter the header out
val users = rows.map(row => header(row,"user")
val usersByHits = rows.map(row => header(row,"user") -> header(row,"hits").toInt)
...

Notez que header n’est guère plus qu’une simple carte d’un mnémonique à l’index du tableau. Presque tout cela pourrait être fait à la place ordinale de l'élément dans le tableau, comme user = row(0)

PS: Bienvenue à Scala :-)

54
maasg

Vous pouvez utiliser la bibliothèque spark-csv: https://github.com/databricks/spark-csv

Ceci provient directement de la documentation:

import org.Apache.spark.sql.SQLContext

SQLContext sqlContext = new SQLContext(sc);

HashMap<String, String> options = new HashMap<String, String>();
options.put("header", "true");
options.put("path", "cars.csv");

DataFrame df = sqlContext.load("com.databricks.spark.csv", options);
16
Saman

Tout d'abord, je dois dire que c'est beaucoup plus simple si vous placez vos en-têtes dans des fichiers séparés - c'est la convention dans le Big Data.

Quoi qu'il en soit, la réponse de Daniel est plutôt bonne, mais elle présente une inefficacité et un bogue, alors je vais poster la mienne. L'inefficacité est qu'il n'est pas nécessaire de vérifier chaque enregistrement pour voir si c'est l'en-tête, il vous suffit de vérifier le premier enregistrement pour chaque partition. Le problème est qu’en utilisant .split(","), vous pouvez obtenir une exception ou obtenir la mauvaise colonne lorsque les entrées sont la chaîne vide et se produisent au début ou à la fin de l’enregistrement - pour corriger la nécessité d’utiliser .split(",", -1). Alors voici le code complet:

val header =
  scala.io.Source.fromInputStream(
    hadoop.fs.FileSystem.get(new Java.net.URI(filename), sc.hadoopConfiguration)
    .open(new hadoop.fs.Path(path)))
  .getLines.head

val columnIndex = header.split(",").indexOf(columnName)

sc.textFile(path).mapPartitions(iterator => {
  val head = iterator.next()
  if (head == header) iterator else Iterator(head) ++ iterator
})
.map(_.split(",", -1)(columnIndex))

Les points finaux, considérez Parquet si vous voulez pêcher seulement certaines colonnes. Ou au moins, envisagez de mettre en œuvre une fonction de division évaluée paresseusement si vous avez de grandes lignes.

9
samthebest

Nous pouvons utiliser le nouveau DataFrameRDD pour lire et écrire les données CSV. DataFrameRDD présente quelques avantages par rapport à NormalRDD:

  1. DataFrameRDD sont un peu plus rapides que NormalRDD puisque nous déterminons le schéma, ce qui nous permet d’optimiser énormément au moment de l’exécution et de nous apporter un gain de performances significatif.
  2. Même si la colonne se décale en CSV, elle prendra automatiquement la colonne correcte, car nous ne codons pas en dur le numéro de colonne présent lors de la lecture des données en tant que textFile, puis en le divisant, puis en utilisant le numéro de colonne pour obtenir les données.
  3. En quelques lignes de code, vous pouvez lire le fichier CSV directement.

Vous aurez besoin de cette bibliothèque: Ajoutez-la dans build.sbt

libraryDependencies += "com.databricks" % "spark-csv_2.10" % "1.2.0"

Spark Scala code pour cela:

val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val csvInPath = "/path/to/csv/abc.csv"
val df = sqlContext.read.format("com.databricks.spark.csv").option("header","true").load(csvInPath)
//format is for specifying the type of file you are reading
//header = true indicates that the first line is header in it

Pour convertir en RDD normal en prenant certaines des colonnes et

val rddData = df.map(x=>Row(x.getAs("colA")))
//Do other RDD operation on it

Enregistrement du format RDD au format CSV:

val aDf = sqlContext.createDataFrame(rddData,StructType(Array(StructField("colANew",StringType,true))))
aDF.write.format("com.databricks.spark.csv").option("header","true").save("/csvOutPath/aCSVOp")

Puisque l'en-tête est défini sur true, nous allons obtenir le nom de l'en-tête dans tous les fichiers de sortie.

5
Ajay Gupta

Je vous recommande de lire l'en-tête directement à partir du pilote, et non via Spark. Deux raisons à cela: 1) C'est une seule ligne. Une approche distribuée ne présente aucun avantage. 2) Nous avons besoin de cette ligne dans le pilote, pas des nœuds de travail.

Ca fait plutot comme ca:

// Ridiculous amount of code to read one line.
val uri = new Java.net.URI(filename)
val conf = sc.hadoopConfiguration
val fs = hadoop.fs.FileSystem.get(uri, conf)
val path = new hadoop.fs.Path(filename)
val stream = fs.open(path)
val source = scala.io.Source.fromInputStream(stream)
val header = source.getLines.head

Maintenant, lorsque vous créez le RDD, vous pouvez supprimer l’en-tête.

val csvRDD = sc.textFile(filename).filter(_ != header)

Ensuite, nous pouvons créer un RDD à partir d’une colonne, par exemple:

val idx = header.split(",").indexOf(columnName)
val columnRDD = csvRDD.map(_.split(",")(idx))
4
Daniel Darabos

Voici un autre exemple d'utilisation de Spark/Scala en conversion d'un fichier CSV en fichier RDD . Pour une description plus détaillée, voir ceci post .

def main(args: Array[String]): Unit = {
  val csv = sc.textFile("/path/to/your/file.csv")

  // split / clean data
  val headerAndRows = csv.map(line => line.split(",").map(_.trim))
  // get header
  val header = headerAndRows.first
  // filter out header (eh. just check if the first val matches the first header name)
  val data = headerAndRows.filter(_(0) != header(0))
  // splits to map (header/value pairs)
  val maps = data.map(splits => header.Zip(splits).toMap)
  // filter out the user "me"
  val result = maps.filter(map => map("user") != "me")
  // print result
  result.foreach(println)
}
4
cmd

Une autre alternative consiste à utiliser la méthode mapPartitionsWithIndex pour obtenir le numéro d'index de la partition et une liste de toutes les lignes de cette partition. La partition 0 et la ligne 0 seront l'en-tête

val rows = sc.textFile(path)
  .mapPartitionsWithIndex({ (index: Int, rows: Iterator[String]) => 
    val results = new ArrayBuffer[(String, Int)]

    var first = true
    while (rows.hasNext) {
      // check for first line
      if (index == 0 && first) {
        first = false
        rows.next // skip the first row
      } else {
        results += rows.next
      }
    }

    results.toIterator
}, true)

rows.flatMap { row => row.split(",") }
2
mightymephisto

Que dis-tu de ça?

val Delimeter = ","
val textFile = sc.textFile("data.csv").map(line => line.split(Delimeter))
1
om-nom-nom

Pour spark scala j'utilise généralement lorsque je ne peux pas utiliser les packages spark csv ...

val sqlContext = new org.Apache.spark.sql.SQLContext(sc)
val rawdata = sc.textFile("hdfs://example.Host:8020/user/example/example.csv")
val header = rawdata.first()
val tbldata = rawdata.filter(_(0) != header(0))
1
Bruce Nelson

Je pense que vous pouvez essayer de charger ce csv dans un RDD puis de créer un cadre de données à partir de ce RDD. Voici le document de création d'un cadre de données à partir de rdd: http://spark.Apache.org/docs/latest/sql -programming-guide.html # interopérabilité-avec-rdds

0
taotao.li

Je vous suggère d'essayer

https://spark.Apache.org/docs/latest/sql-programming-guide.html#rdds

JavaRDD<Person> people = sc.textFile("examples/src/main/resources/people.txt").map(
  new Function<String, Person>() {
    public Person call(String line) throws Exception {
      String[] parts = line.split(",");

      Person person = new Person();
      person.setName(parts[0]);
      person.setAge(Integer.parseInt(parts[1].trim()));

      return person;
    }
  });

Vous devez avoir une classe dans cet exemple, personne avec la spécification de votre en-tête de fichier et associer vos données au schéma et appliquer des critères comme dans mysql .. pour obtenir le résultat souhaité

0
mithra

À partir de Spark 2.0, le CSV peut être lu directement dans un DataFrame.

Si le fichier de données n'a pas de ligne d'en-tête, alors ce serait:

val df = spark.read.csv("file://path/to/data.csv")

Cela va charger les données, mais donnez à chaque colonne des noms génériques comme _c0, _c1, Etc.

S'il y a des en-têtes, alors l'ajout de .option("header", "true") utilisera la première ligne pour définir les colonnes du DataFrame:

val df = spark.read
  .option("header", "true")
  .csv("file://path/to/data.csv")

Pour un exemple concret, disons que vous avez un fichier avec le contenu:

user,topic,hits
om,scala,120
daniel,spark,80
3754978,spark,1

Ensuite, les résultats suivants seront regroupés par sujet:

import org.Apache.spark.sql.functions._
import spark.implicits._

val rawData = spark.read
  .option("header", "true")
  .csv("file://path/to/data.csv")

// specifies the query, but does not execute it
val grouped = rawData.groupBy($"topic").agg(sum($"hits))

// runs the query, pulling the data to the master node
// can fail if the amount of data is too much to fit 
// into the master node's memory!
val collected = grouped.collect

// runs the query, writing the result back out
// in this case, changing format to Parquet since that can
//   be nicer to work with in Spark
grouped.write.parquet("hdfs://some/output/directory/")

// runs the query, writing the result back out
// in this case, in CSV format with a header and 
// coalesced to a single file.  This is easier for human 
// consumption but usually much slower.
grouped.coalesce(1)
  .write
  .option("header", "true")
  .csv("hdfs://some/output/directory/")
0
hayden.sikh