2017-04-06 7 views
4

ich eine Eingabedatei foo.txt mit dem folgenden Inhalt haben:Scala-Funken (version1.5.2) Datenrahmen Split Fehler

c1|c2|c3|c4|c5|c6|c7|c8| 
00| |1.0|1.0|9|27.0|0|| 
01|2|3.0|4.0|1|10.0|1|1| 

Ich möchte verwandeln es zu einem Dataframe, einige Sql Abfragen durchführen:

var text = sc.textFile("foo.txt") 
var header = text.first() 
var rdd = text.filter(row => row != header) 
case class Data(c1: String, c2: String, c3: String, c4: String, c5: String, c6: String, c7: String, c8: String) 

Bis zu diesem Punkt alles in Ordnung ist, kommt das Problem im nächsten Satz:

var df = rdd.map(_.split("\\|")).map(p => Data(p(0), p(1), p(2), p(3), p(4), p(5), p(6), p(7))).toDF() 

Wenn ich versuche, df mit df.show zu drucken, erhalte ich eine Fehlermeldung:

scala> df.show() 
java.lang.ArrayIndexOutOfBoundsException: 7 

Ich weiß, dass der Fehler aufgrund des geteilten Satzes sein könnte. Ich habe auch versucht foo.txt mit folgenden Syntax zu spalten:

var df = rdd.map(_.split("""|""")).map(p => Data(p(0), p(1), p(2), p(3), p(4), p(5), p(6), p(7))).toDF() 

Und dann bekomme ich so etwas wie diese:

scala> df.show() 
+------+---------+----------+-----------+-----+-----------+----------------+----------------+ 
| c1 |  c2 | c3 |  c4 | c5 |  c6 |  c7  |  c8  | 
+------+---------+----------+-----------+-----+-----------+----------------+----------------+ 
|  0|  0|   ||   | ||   1|    .|    0| 
|  0|  1|   ||   2| ||   3|    .|    0| 
+------+---------+----------+-----------+-----+-----------+----------------+----------------+ 

Daher meine Frage ist, wie kann ich richtig diese Datei passieren zu einem Datenrahmen.

EDIT: Der Fehler ist in der ersten Zeile aufgrund || Feld ohne Zwischenraum. Diese Art von Felddefinition, die von den Beispielen abhängt, funktioniert gut oder stürzt ab.

+1

Ich kann den Fehler nicht reproduzieren, auf Spark 2.0 funktioniert es gut. (Keine ArrayIndexOutOfBoundsException auf df.show()) – jamborta

+0

Sorry, ich habe vergessen zu kommentieren, dass es auf Spark 1.5.2 ist. Ich bearbeite die Frage – qwerty

+0

Kannst du die Datei nicht in '.csv' umbenennen und direkt in ein' df' lesen? – pheeleeppoo

Antwort

3

Dies liegt daran, einer Ihrer Leitungen ist kürzer als die anderen:

scala> var df = rdd.map(_.split("\\|")).map(_.length).collect() 
df: Array[Int] = Array(7, 8) 

Sie können manuell in den Reihen füllen (aber Sie müssen jeden Fall manuell zu handhaben):

val df = rdd.map(_.split("\\|")).map{row => 
    row match { 
    case Array(a,b,c,d,e,f,g,h) => Data(a,b,c,d,e,f,g,h) 
    case Array(a,b,c,d,e,f,g) => Data(a,b,c,d,e,f,g," ") 
    } 
} 

scala> df.show() 
+---+---+---+---+---+----+---+---+ 
| c1| c2| c3| c4| c5| c6| c7| c8| 
+---+---+---+---+---+----+---+---+ 
| 00| |1.0|1.0| 9|27.0| 0| | 
| 01| 2|3.0|4.0| 1|10.0| 1| 1| 
+---+---+---+---+---+----+---+---+ 

EDIT :

val df = rdd.map(_.split("\\|", -1)).map(_.slice(0,8)).map(p => Data(p(0), p(1), p(2), p(3), p(4), p(5), p(6), p(7))).toDF() 
:

Ein generische Lösung so etwas wie dies würde

Wenn Sie davon ausgehen, dass Sie immer die richtige Anzahl von Trennzeichen haben, ist es sicher, diese Syntax zu verwenden und den letzten Wert abzuschneiden.

+0

Das zweite Fragment ist was ich gesucht habe, aber ich vermisse etwas. In diesem Beispiel befindet sich das Nullfeld im letzten Feld, aber stellen Sie sich vor, dass es sich um eine der 8 Spalten handeln könnte. Wie würden Sie Ihren Code verallgemeinern, um in dieser Situation richtig arbeiten zu können? – qwerty

+0

Warum schließen Sie '.map (_. Slice (0,8))' ein? – qwerty

+1

Nur um zu zeigen, dass es länger ist. Es ist nicht notwendig, wenn Sie es nur in die Fallklasse einspeisen möchten. – jamborta

2

Mein Vorschlag wäre, Datasricks CSV-Parser zu verwenden.

-Link: https://github.com/databricks/spark-csv

Ihr Beispiel zu laden:

ich eine Beispieldatei ähnlich wie bei Ihnen geladen:

c1|c2|c3|c4|c5|c6|c7|c8| 
00| |1.0|1.0|9|27.0|0|| 
01|2|3.0|4.0|1|10.0|1|1| 

der Datenrahmen So erstellen Sie den folgenden Code verwenden:

val df = sqlContext.read 
    .format("com.databricks.spark.csv") 
    .option("header", "true") // Use first line of all files as header 
    .option("inferSchema", "true") // Automatically infer data types 
    .option("delimiter", "|") // default is "," 
    .load("foo.txt") 
    .show 

Ich habe den folgenden Ausgang

+---+---+---+---+---+----+---+----+---+ 
| c1| c2| c3| c4| c5| c6| c7| c8| | 
+---+---+---+---+---+----+---+----+---+ 
| 0| |1.0|1.0| 9|27.0| 0|null| | 
| 1| 2|3.0|4.0| 1|10.0| 1| 1| | 
+---+---+---+---+---+----+---+----+---+ 

Auf diese Weise müssen Sie sich nicht darum kümmern, die Datei selbst zu analysieren.Sie erhalten einen Datenrahmen direkt

Verwandte Themen