Scala中如何通过给定映射集更新DataFrame的多列?
没问题!在Scala的Spark环境里,要借助多个映射集合批量更新DataFrame的多列,其实有几种简洁高效的实现方式,我给你一步步拆解:
首先先明确我们的基础数据和映射示例:
// 原始DataFrame val df = Seq( ("manuj","kumar","CEO","Info"), ("Alice","Beb","Miniger","gogle"), ("Ram","Kumar","Developer","Info Delhi") ).toDF("fname","lname","designation","company") // 各列对应的映射集合 val fnameMap = Map("manuj" -> "Manuj", "Alice" -> "ALICE", "Ram" -> "RAM") val lnameMap = Map("kumar" -> "KUMAR", "Beb" -> "BEB") val designationMap = Map("CEO" -> "Chief Executive Officer", "Miniger" -> "Manager", "Developer" -> "Software Developer") val companyMap = Map("Info" -> "Info Solutions", "gogle" -> "Google", "Info Delhi" -> "Info Solutions Delhi")
方法一:逐个列手动更新(适合列数少的场景)
如果需要更新的列不多,可以直接链式调用withColumn,配合when和otherwise来匹配映射值:
import org.apache.spark.sql.functions._ val updatedDf = df // 更新fname列:匹配到映射则替换,否则保留原值 .withColumn("fname", when(col("fname").isin(fnameMap.keys.toSeq: _*), lit(fnameMap(col("fname").toString))).otherwise(col("fname"))) .withColumn("lname", when(col("lname").isin(lnameMap.keys.toSeq: _*), lit(lnameMap(col("lname").toString))).otherwise(col("lname"))) .withColumn("designation", when(col("designation").isin(designationMap.keys.toSeq: _*), lit(designationMap(col("designation").toString))).otherwise(col("designation"))) .withColumn("company", when(col("company").isin(companyMap.keys.toSeq: _*), lit(companyMap(col("company").toString))).otherwise(col("company")))
不过这种方式在列数多的时候会显得冗余,推荐下面的批量处理方法。
方法二:批量处理多列(推荐,扩展性强)
我们可以封装一个辅助函数,把Scala映射转换成Spark能识别的映射表达式,再通过foldLeft批量应用到所有需要更新的列上:
步骤1:编写辅助转换函数
这个函数会把Scala的Map转换成Spark的create_map结构,并用coalesce确保未匹配到的字段保留原值:
import org.apache.spark.sql.Column import org.apache.spark.sql.functions.{create_map, lit, coalesce} def mapColumn(colName: String, mapping: Map[String, String]): Column = { // 将Scala Map转为Spark的映射表达式 val sparkMap = create_map(mapping.flatMap { case (k, v) => Seq(lit(k), lit(v)) }.toSeq: _*) // 优先取映射值,没匹配到则返回原列值 coalesce(sparkMap(col(colName)), col(colName)) }
步骤2:定义列与映射的对应关系
把需要更新的列和对应的映射放到一个序列里,后续新增列只需要在这里添加即可:
val columnMappings = Seq( "fname" -> fnameMap, "lname" -> lnameMap, "designation" -> designationMap, "company" -> companyMap )
步骤3:批量更新DataFrame
用foldLeft链式处理所有列,避免重复代码:
val updatedDf = columnMappings.foldLeft(df) { case (accDf, (colName, mapping)) => accDf.withColumn(colName, mapColumn(colName, mapping)) }
验证结果
执行updatedDf.show(false)就能看到更新后的DataFrame:
+-----+-----+-----------------------+---------------------+ |fname|lname|designation |company | +-----+-----+-----------------------+---------------------+ |Manuj|KUMAR|Chief Executive Officer|Info Solutions | |ALICE|BEB |Manager |Google | |RAM |KUMAR|Software Developer |Info Solutions Delhi| +-----+-----+-----------------------+---------------------+
额外优化:忽略大小写匹配
如果你的映射键和DataFrame中的值大小写不一致,可以修改辅助函数,统一转成小写(或大写)后再匹配:
def mapColumnIgnoreCase(colName: String, mapping: Map[String, String]): Column = { // 把映射键转成小写,统一匹配规则 val lowerCaseMap = mapping.map { case (k, v) => k.toLowerCase -> v } val sparkMap = create_map(lowerCaseMap.flatMap { case (k, v) => Seq(lit(k), lit(v)) }.toSeq: _*) // 把原列值转小写后去匹配映射 coalesce(sparkMap(lower(col(colName))), col(colName)) }
内容的提问来源于stack exchange,提问作者Manoj Kumar Dhakad
相关产品推荐
相关产品推荐

