我有一个循环,在每次迭代中生成行。我的目标是创建一个具有给定模式的数据框,该数据框仅包含那些行。我想到了一系列要遵循的步骤,但我无法在每次循环迭代中向Row
添加新的List[Row]
我正在尝试以下方法:
var listOfRows = List[Row]()
val dfToExtractValues: DataFrame = ???
dfToExtractValues.foreach { x =>
//Not really important how to generate here the variables
//So to simplify all the rows will have the same values
var col1 = "firstCol"
var col2 = "secondCol"
var col3 = "thirdCol"
val newRow = RowFactory.create(col1,col2,col3)
//This step I am not able to do
//listOfRows += newRow -> Just for strings
//listOfRows.add(newRow) -> This add doesnt exist, it is a addString
//listOfRows.aggregate(1)(newRow) -> This is not how aggreage works...
}
val rdd = sc.makeRDD[RDD](listOfRows)
val dfWithNewRows = sqlContext.createDataFrame(rdd, myOriginalDF.schema)
有人能告诉我我做错了什么,或者我可以改变从我生成的行生成数据帧的方法中做些什么?
也许有更好的方法来收集行而不是List [Row]。但后来我需要将其他类型的集合转换为数据帧。
答案 0 :(得分:1)
有人可以告诉我,我做错了什么
<强>闭包强>:
首先,您似乎跳过了编程指南中的Understanding Closures。任何修改通过闭包传递的变量的尝试都是徒劳的。您所能做的就是修改副本,更改不会全局反映。
变量不会使对象变为可变:
继
var listOfRows = List[Row]()
创建一个变量。已分配的List
与其一样不可变。如果它不在Spark上下文中,您可以创建一个新的List
并重新分配:
listOfRows = newRow :: listOfRows
请注意,我们不会追加 - 您不希望在循环中附加到列表中。
当您想要共享数据时,具有不可变对象的变量很有用(例如,它是Akka中的常见模式),但Spark中没有很多应用程序。
分发内容:
最后,永远不要将数据提取给驱动程序,只是为了再次分发它。您还应避免在RDDs
和DataFrames
之间进行不必要的转换。最好一直使用DataFrame
运算符:
dfToExtractValues.select(...)
但如果您需要更复杂的map
:
import org.apache.spark.sql.catalyst.encoders.RowEncoder
dfToExtractValues.map(x => ...)(RowEncoder(schema))