Spark数据框爆炸功能

时间:2016-08-24 06:13:07

标签: apache-spark apache-spark-sql

任何人都可以解释为什么在Row字段爆炸后使用案例Seq[Row]dataframe,该字段包含元素集合。 还有,请你解释一下为什么需要asInstanceOf才能从爆炸字段中获取值?

以下是语法:

val explodedDepartmentWithEmployeesDF = departmentWithEmployeesDF.explode(departmentWithEmployeesDF("employees")) {     
                          case Row(employee: Seq[Row]) => 
                          employee.map(employee =>
                          Employee(employee(0).asInstanceOf[String], 
                          employee(1).asInstanceOf[String], employee(2).asInstanceOf[String]) ) }

2 个答案:

答案 0 :(得分:2)

首先我要注意,我无法解释为什么explode()变成Row(employee: Seq[Row]),因为我不知道您的DataFrame的架构。我必须假设它与您的数据结构有关。

我不知道你的原始数据,我已经创建了一个小数据集来工作

scala> val df = sc.parallelize( Array( (1, "dsfds dsf dasf dsf dsf d"), (2, "2344 2353 24 23432 234"))).toDF("id", "text")
df: org.apache.spark.sql.DataFrame = [id: int, text: string]

如果我现在映射它,你可以看到它返回包含Any类型数据的行。

scala> df.map {case row: Row => (row(0), row(1)) }
res21: org.apache.spark.rdd.RDD[(Any, Any)] = MapPartitionsRDD[17] at map at <console>:33

您基本上丢失了类型信息,这就是您需要在行中使用数据时明确指定类型的原因

scala> df.map {case row: Row => (row(0).asInstanceOf[Int], row(1).asInstanceOf[String]) }
res22: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[18] at map at <console>:33

所以,为了爆炸它,我必须做以下

scala> :paste
// Entering paste mode (ctrl-D to finish)

import org.apache.spark.sql.Row
df.explode(col("id"), col("text")) {case row: Row =>
    val id = row(0).asInstanceOf[Int]
    val words = row(1).asInstanceOf[String].split(" ")
    words.map(word => (id, word))
}

// Exiting paste mode, now interpreting.

import org.apache.spark.sql.Row
res30: org.apache.spark.sql.DataFrame = [id: int, text: string, _1: int, _2: string]

scala> res30 show
+---+--------------------+---+-----+
| id|                text| _1|   _2|
+---+--------------------+---+-----+
|  1|dsfds dsf dasf ds...|  1|dsfds|
|  1|dsfds dsf dasf ds...|  1|  dsf|
|  1|dsfds dsf dasf ds...|  1| dasf|
|  1|dsfds dsf dasf ds...|  1|  dsf|
|  1|dsfds dsf dasf ds...|  1|  dsf|
|  1|dsfds dsf dasf ds...|  1|    d|
|  2|2344 2353 24 2343...|  2| 2344|
|  2|2344 2353 24 2343...|  2| 2353|
|  2|2344 2353 24 2343...|  2|   24|
|  2|2344 2353 24 2343...|  2|23432|
|  2|2344 2353 24 2343...|  2|  234|
+---+--------------------+---+-----+

如果您想要命名列,可以定义一个案例类来保存爆炸数据

scala> :paste
// Entering paste mode (ctrl-D to finish)

import org.apache.spark.sql.Row
case class ExplodedData(word: String)
df.explode(col("id"), col("text")) {case row: Row =>
    val words = row(1).asInstanceOf[String].split(" ")
    words.map(word => ExplodedData(word))
}

// Exiting paste mode, now interpreting.

import org.apache.spark.sql.Row
defined class ExplodedData
res35: org.apache.spark.sql.DataFrame = [id: int, text: string, word: string]

scala> res35.select("id","word").show
+---+-----+
| id| word|
+---+-----+
|  1|dsfds|
|  1|  dsf|
|  1| dasf|
|  1|  dsf|
|  1|  dsf|
|  1|    d|
|  2| 2344|
|  2| 2353|
|  2|   24|
|  2|23432|
|  2|  234|
+---+-----+

希望这会带来一些清晰。

答案 1 :(得分:0)

我认为您可以阅读有关该文档的更多信息并首先进行测试。

数据帧的爆炸仍会返回一个数据帧。它接受一个lambda函数f :(行)⇒TraversableOnce[A]作为参数。

在lambda函数中,您将按大小写匹配输入。您已经知道您的输入将是员工行,这仍然是行的Seq.So输入的情况将为Row(员工:Seq [Row]),如果您不理解这部分,你可以在scala中了解更多关于unapply funciton的事情。

而且,员工(我相信你应该在这里使用员工)作为Seq of Row,将应用map函数将每一行映射到Employee。并且您将使用scala apply函数来获取此行中的第i个值。但返回值是一个Object,因此您必须使用asInstanceOf将其转换为您期望的类型。