我现在重新提出了问题。
我正在学习scala和spark。我知道直接从csv文件创建RDD,而不是创建DF并将其转换为RDD。但是,我正在尝试以下组合。
创建scala ListBuffer,Spark Dataframe并将其转换为RDD:
scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer
scala> var src_policy_final = new ListBuffer[List[Any]]
src_policy_final: scala.collection.mutable.ListBuffer[List[Any]] = ListBuffer()
scala> var src_policy_final = new ListBuffer[List[Any]]
src_policy_final: scala.collection.mutable.ListBuffer[List[Any]] = ListBuffer()
scala> var src_policy_df = spark.read.format("csv").option("header", "true").option("inferSchema", "true").load("sparktest/policy_details.csv")
src_policy_df: org.apache.spark.sql.DataFrame = [policy_id: int, otherdetails: string]
scala> var src_rdd = src_policy_df.rdd.map(_.toSeq.toList)
src_rdd: org.apache.spark.rdd.RDD[List[Any]] = MapPartitionsRDD[40] at map at <console>:26
scala> var src_pol_list = src_rdd.collect.toList
src_pol_list: List[List[Any]] = List(List(10110000, This is the first policy), List(10456200, This is the second policy), List(10345300, This is the third policy))
使用scala进行循环,我正在迭代Spark RDD记录以替换列值(带有surrogateId的policy_id),如下所示-
scala> for(pol_details <- src_pol_list){
| src_policy_final += pol_details.toList.map(e => if(e==10110000) 1345678 else e)
| }
我正在使用.map(e => if(e==orig_pol_id) ref_surr_id else e)
更改记录的特定列值,并将记录添加到ListBuffer[List[Any]]
。一旦完成迭代,将RDD中的所有记录都抛出,我将使用函数ListBuffer[Lis[Any]]
SaveAsTextFile("/sparktest/policy_details")
值作为csv文件写入HDFS文件系统中。
当我执行src_policy_final的println时,输出为:
scala> println(src_policy_final)
ListBuffer(List(1345678, This is the first policy), List(10456200, This is the second policy), List(10345300, This is the third policy))
现在,我通过将ListBuffer [ListAny]]转换为RDD,将修改后的数据写回到HDFS文件系统中:
scala> var src_write = sc.parallelize(src_policy_final.toList)
src_write: org.apache.spark.rdd.RDD[List[Any]] = ParallelCollectionRDD[43] at parallelize at <console>:53
写入HDFS文件系统:
scala> src_write.saveAsTextFile("sparktest/pol_det")
输出数据如下:
List(1345678, This is the first policy)
List(10456200, This is the second policy)
List(10345300, This is the third policy)
想要得到的输出是:
1345678, This is the first policy
10456200, This is the second policy
10345300, This is the third policy
我不确定如何根据我的要求加载输出。
希望,对于我要实现的目标,我给出了更好的解释。请您帮忙?
答案 0 :(得分:0)
我真的不明白你想做什么...
但是,由于您说的是学习,所以我将尝试逐步解释所有内容-希望它能对您有所帮助。
首先,作为几年前从Java转到Scala的同事的建议。尽可能避免所有变异,强迫自己以功能的方式进行思考和编程-因此,使用val
代替var
和 immutable 集合代替可变个。
第二,尽可能避免使用Any
类型的东西,例如此处...
var src_rdd = src_policy_df.rdd.map(_.toSeq.toList)
......您可以通过更类型化的方式从每个Row
中获取所需的值,例如:
val src_rdd = src_policy_df.rdd.map { row =>
(
row.getAs[Int](fieldName = "policy_id"),
row.getAs[String](fieldName = "otherdetails")
)
}
// src_rdd: RDD[(Int, String)]
或更妙的是,使用Dataset
(一种键入数据框)。
import spark.implicits._ // spark is an instance of SparkSession
final case class Policy(policy_id: Int, otherdetails: String)
val src_dataset = src_policy_df.as[Policy] // implicit Encoder needed here, provided by the spark implicits.
在Spark中,您绝对 collect
-除非您是计算管道的最后一步(而且在大多数情况下,这只能在调试阶段完成,因为通常您会将其保存到外部数据存储中,例如HDFS或mongo),或者如果您确定有一个小的RDD
并希望将其作为查询表或其他转换访问,类似于(例如,这在缩减对RDD上非常常见,因此存在reduceByKeyLocally
方法将返回 Map ))。
为什么? -因为collect
将在执行器上分发的所有数据都带到 Driver ,这意味着您不再使用该框架来并行化计算。 br />
您应该做的是使用Spark提供的 Transformations 来构建计算,例如map
。
val orig_pol_id = 10110000
val ref_surr_id = 1345678
// Using RDDs.
val src_policy_final_rdd = src_rdd.map {
case (id, otherdetails) if (id == orig_pol_id) => (ref_surr_id, otherdetails)
case policy => policy // default case, nothing change.
}
// Using Datasets.
val src_policy_final_dataset = src_dataset.map {
case policy if (policy.id == orig_pol_id) => policy.copy(id = ref_surr_id) // the copy method returns a new policy with the provided fields changed.
case policy => policy // default case, nothing change.
}
最后,当将RDD
写入 HDFS 时,它将在每个元素上使用默认的toString
打印每一行。因此,您可能需要先对其进行格式化,然后再保存。
val write_rdd = src_policy_final_rdd.map {
case (id, otherdetails) => s"$id,$otherdetails"
}
// wirte_rdd: RDD[String]
src_write.saveAsTextFile("sparktest/pol_det")
或者,如果您使用的是Dataset
,则可以使用 DataframeWriter API为您处理所有这些事情。 (推荐)
src_policy_final_dataset
.write
.option("header", "true")
.option("sep", ",") // ',' is the default separator, but I prefer to be specific.
.csv("sparktest/pol_det")
这应该解决您所有的问题。
PS:最后两点。
首先,通常在 SO 中要求/回答这个问题是“太板了” -因此,请尝试限制您的范围,下次再清楚一点;)。 br />
而且,您可以尝试先阅读有关Spark的知识,并做一些快速教程以使自己对框架更加满意-顺便说一句,this是几天前我在办公室制作的简短的 workshop ,它是为非Scala开发人员设计的,希望对您也有帮助:)