我正在应用一个函数,让我们说f(),通过map方法到DataFrame的行(称之为df
)但是如果{{1} {}我在调用生成RDD上的collect时看到NullPointerException }作为参数传递给f()。
以下Scala代码(可以粘贴在spark-shell中)显示了该问题的最小示例(请参阅函数df.columns
)。我还在函数prepRDD_buggy()
中发布了此问题的当前解决方法,其中列名称作为prepRDD()
而不是val
传递的唯一区别。
请问一些Spark专家请指出发生这种情况的确切原因或确认我们的假设,即从属节点没有得到DataFrame列名?
df.columns
以下是我在spark-shell中运行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
时看到的(非常详细)错误消息的前几行。
mat_buggy.collect
答案 0 :(得分:4)
您的假设是正确的。 columns
需要访问schema
,并且架构取决于queryExecution
,这是暂时性的,因此无法运送给工作人员。因此,您在prepRDD
中所做的事情或多或少是正确的,尽管可以直接从行中提取相同的信息:
scala> df.rdd.map(_.schema.fieldNames).first
res14: Array[String] = Array(y, x1, x2, x3)
旁注VectorAssembler
加简单map
将是更好的选择。