【问题标题】:Spark DataFrame column names not passed to slave nodes?Spark DataFrame 列名未传递给从节点?
【发布时间】:2016-03-31 02:59:20
【问题描述】:

我正在应用一个函数,比如说 f(),通过 map 方法到 DataFrame 的行(称为 df),但是如果 df.columns 被传递为f() 的参数。

以下可粘贴到 spark-shell 中的 Scala 代码显示了该问题的一个最小示例(请参阅函数 prepRDD_buggy())。我还在函数prepRDD() 中发布了我当前针对此问题的解决方法,其中列名作为val 而不是df.columns 传递的唯一区别。

能否请一些 Spark 专家指出发生这种情况的确切原因,或者确认我们的假设,即从节点没有获取 DataFrame 列名?

import org.apache.spark.SparkContext
import org.apache.spark.sql.{DataFrame, Row}
import org.apache.spark.sql.types._
import org.apache.spark.rdd.RDD
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.Vectors

// A Simple DataFrame
val dataRDD: RDD[Row] = sc.parallelize(Array(
  Row(1.0,2.1,3.3),
  Row(3.4,5.9,8.9),
  Row(3.1,2.3,4.1)))
val struct: StructType = StructType(
  StructField("y", DoubleType, false) ::
  StructField("x1", DoubleType, false) ::
  StructField("x2", DoubleType, false) :: Nil)
val df: DataFrame = sqlContext.createDataFrame(dataRDD, struct)

// Make LabeledPoint object from Row objects
def makeLP(row: Row, colnames: Array[String]) =
  LabeledPoint(row.getDouble(0), 
    Vectors.dense((1 until row.length).toArray map (i => row.getDouble(i))))

// Make RDD[LabeledPoint] from DataFrame
def prepRDD_buggy(df: DataFrame): RDD[LabeledPoint] = {
  df map (row => makeLP(row, df.columns))
}
val mat_buggy = prepRDD_buggy(df) 
mat_buggy.collect // throws NullPointerException !

// Make RDD[LabeledPoint] from DataFrame
def prepRDD(df: DataFrame): RDD[LabeledPoint] = {
  val cnames = df.columns
  df map (row => makeLP(row, cnames))
}
val mat = prepRDD(df) 
mat.collect // Works fine

这是我在 spark-shell 中运行 mat_buggy.collect 时看到的(非常详细的)错误消息的前几行。

15/12/24 18:09:28 INFO SparkContext: Starting job: collect at <console>:42
15/12/24 18:09:28 INFO DAGScheduler: Got job 0 (collect at <console>:42) with 2 output partitions
15/12/24 18:09:28 INFO DAGScheduler: Final stage: ResultStage 0(collect at <console>:42)
15/12/24 18:09:28 INFO DAGScheduler: Parents of final stage: List()
15/12/24 18:09:28 INFO DAGScheduler: Missing parents: List()
15/12/24 18:09:28 INFO DAGScheduler: Submitting ResultStage 0 (MapPartitionsRDD[3] at map at <console>:38), which has no missing parents
15/12/24 18:09:28 INFO MemoryStore: ensureFreeSpace(11600) called with curMem=0, maxMem=560993402
15/12/24 18:09:28 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 11.3 KB, free 535.0 MB)
15/12/24 18:09:28 INFO MemoryStore: ensureFreeSpace(4540) called with curMem=11600, maxMem=560993402
15/12/24 18:09:28 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 4.4 KB, free 535.0 MB)
15/12/24 18:09:28 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on 10.10.10.98:53386 (size: 4.4 KB, free: 535.0 MB)
15/12/24 18:09:28 INFO SparkContext: Created broadcast 0 from broadcast at DAGScheduler.scala:861
15/12/24 18:09:28 INFO DAGScheduler: Submitting 2 missing tasks from ResultStage 0 (MapPartitionsRDD[3] at map at <console>:38)
15/12/24 18:09:28 INFO YarnScheduler: Adding task set 0.0 with 2 tasks
15/12/24 18:09:28 INFO TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, ip-10-10-10-217.ec2.internal, PROCESS_LOCAL, 2385 bytes)
15/12/24 18:09:28 INFO TaskSetManager: Starting task 1.0 in stage 0.0 (TID 1, ip-10-10-10-213.ec2.internal, PROCESS_LOCAL, 2385 bytes)
15/12/24 18:09:28 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on ip-10-10-10-213.ec2.internal:56642 (size: 4.4 KB, free: 535.0 MB)
15/12/24 18:09:28 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on ip-10-10-10-217.ec2.internal:56396 (size: 4.4 KB, free: 535.0 MB)
15/12/24 18:09:29 WARN TaskSetManager: Lost task 0.0 in stage 0.0 (TID 0, ip-10-10-10-217.ec2.internal): java.lang.NullPointerException
    at org.apache.spark.sql.DataFrame.schema(DataFrame.scala:290)
    at org.apache.spark.sql.DataFrame.columns(DataFrame.scala:306)
    at $line34.$read$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$anonfun$prepRDD_buggy$1.apply(<console>:38)
    at $line34.$read$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$anonfun$prepRDD_buggy$1.apply(<console>:38)
    at scala.collection.Iterator$$anon$11.next(Iterator.scala:328)
    at scala.collection.Iterator$class.foreach(Iterator.scala:727)

【问题讨论】:

    标签: scala apache-spark dataframe apache-spark-sql rdd


    【解决方案1】:

    你的假设是正确的。 columns 需要访问 schema 并且架构取决于 queryExecution,这是暂时的,因此不会发送给工人。因此,您在prepRDD 中所做的事情或多或少是正确的,尽管可以直接从行中提取相同的信息:

    scala> df.rdd.map(_.schema.fieldNames).first
    res14: Array[String] = Array(y, x1, x2, x3)
    

    附注VectorAssembler 加上简单的map 会是更好的选择。

    【讨论】:

    • 非常感谢您的回答,我是 Scala 和 Spark 新手!看了GitHub上的源码,queryExeuction确实是瞬态的。我有 2 个后续问题:(1) 只有 Serializable 部分被运送到工作节点? (2) 有没有从在线 API 文档中弄清楚这些东西?
    • 1) 是的。对象必须序列化才能运送给工作人员,并且瞬态注释明确排除序列化的属性
    • 2) 我不这么认为。最好假设无法从工作人员访问分布式数据结构(RDD、DF、DS)。无论如何,您对这些没有任何用处,除非您正在修补 Spark 内部。上下文(不包括 TaskContext)也是特定于驱动程序的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-10
    • 2016-07-08
    • 1970-01-01
    • 2015-10-27
    • 2015-05-13
    • 1970-01-01
    • 2016-01-21
    相关资源
    最近更新 更多