RDD [Array [String]]到Dataframe

时间:2016-12-30 19:50:08

标签: scala apache-spark spark-dataframe rdd

我是Spark和Hive的新手,我的目标是在Hive表中加载分隔符(比如说csv)。经过一些阅读后,我发现将数据加载到Hive的路径是csv->dataframe->Hive。(如果我错了,请纠正我)。

CSV:
1,Alex,70000,Columbus
2,Ryan,80000,New York
3,Johny,90000,Banglore
4,Cook, 65000,Glasgow
5,Starc, 70000,Aus

我使用以下命令读取csv文件:

val csv =sc.textFile("employee_data.txt").map(line => line.split(",").map(elem => elem.trim))
csv: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[29] at map at <console>:39

现在我正在尝试将此RDD转换为Dataframe并使用以下代码:

scala> val df = csv.map { case Array(s0, s1, s2, s3) => employee(s0, s1, s2, s3) }.toDF()
df: org.apache.spark.sql.DataFrame = [eid: string, name: string, salary: string, destination: string]

employee是一个案例类,我将其用作架构定义。

case class employee(eid: String, name: String, salary: String, destination: String)

然而,当我df.show时,我收到以下错误:

  

org.apache.spark.SparkException:作业因阶段失败而中止:   阶段10.0中的任务0失败4次,最近失败:丢失任务   阶段10.0中的0.3(TID 22,user.hostname):scala.MatchError:[Ljava.lang.String; @ 88ba3cb(类)   [Ljava.lang.String;)

我期待一个数据帧作为输出。我知道为什么我可能会收到此错误,因为RDD中的值以Ljava.lang.String;@88ba3cb格式存储,我需要使用mkString来获取实际值,但我无法找到如何执行此操作。我很感激你的时间。

3 个答案:

答案 0 :(得分:3)

如果您修复了案例类,那么它应该可以工作:

scala> case class employee(eid: String, name: String, salary: String, destination: String)
defined class employee

scala> val txtRDD = sc.textFile("data.txt").map(line => line.split(",").map(_.trim))
txtRDD: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[30] at map at <console>:24

scala> txtRDD.map{case Array(s0, s1, s2, s3) => employee(s0, s1, s2, s3)}.toDF.show
+---+-----+------+-----------+
|eid| name|salary|destination|
+---+-----+------+-----------+
|  1| Alex| 70000|   Columbus|
|  2| Ryan| 80000|   New York|
|  3|Johny| 90000|   Banglore|
|  4| Cook| 65000|    Glasgow|
|  5|Starc| 70000|        Aus|
+---+-----+------+-----------+

否则您可以将String转换为Int

scala> case class employee(eid: Int, name: String, salary: String, destination: String)
defined class employee

scala> val df = txtRDD.map{case Array(s0, s1, s2, s3) => employee(s0.toInt, s1, s2, s3)}.toDF
df: org.apache.spark.sql.DataFrame = [eid: int, name: string ... 2 more fields]

scala> df.show
+---+-----+------+-----------+
|eid| name|salary|destination|
+---+-----+------+-----------+
|  1| Alex| 70000|   Columbus|
|  2| Ryan| 80000|   New York|
|  3|Johny| 90000|   Banglore|
|  4| Cook| 65000|    Glasgow|
|  5|Starc| 70000|        Aus|
+---+-----+------+-----------+

然而,最好的解决方案是使用spark-csv(将薪水视为Int)。

另请注意,当您运行df.show时会抛出错误,因为在此之前所有内容都被延迟评估。 df.show是一个将导致所有排队转换执行的操作(有关详细信息,请参阅this article)。

答案 1 :(得分:2)

在数组元素上使用map,而不是在数组上使用:

val csv = sc.textFile("employee_data.txt")
    .map(line => line
                     .split(",")
                     .map(e => e.map(_.trim))
     )
val df = csv.map { case Array(s0, s1, s2, s3) => employee(s0, s1, s2, s3) }.toDF()

但是,为什么要读取CSV然后将RDD转换为DF? Spark 1.5已经可以通过spark-csv包读取CSV:

val df = sqlContext.read
    .format("com.databricks.spark.csv")
    .option("header", "true") 
    .option("inferSchema", "true") 
    .option("delimiter", ";") 
    .load("employee_data.txt")

答案 2 :(得分:1)

正如您在评论中所说,您的案例类员工(名为Employee)会收到Int作为其构造函数的第一个参数,但您传递的是String。因此,在实例化或修改将Int定义为eid的案例之前,您应将其转换为String