Spark DataFrame列名可被SQL识别但withColumn调用报错求助
解决Spark DataFrame修改列类型时的列访问报错问题
先复盘下你的问题场景:
你有一个Spark DataFrame(变量名df),列名包含Age,用SQL查询Age列完全正常:
scala> spark.sql(""" select Age from df limit 2 """).show() +---+ |Age| +---+ | 30| | 33| +---+
但尝试用withColumn修改Age列类型时,却报错提示value Age is not a member of org.apache.spark.sql.DataFrame:
scala> val dfTemp = df.withColumn("temp", df.Age.cast(DoubleType)) .drop("Age").withColumnRenamed("temp", "Age") <console>:38: error: value Age is not a member of org.apache.spark.sql.DataFrame
问题原因
这是Scala API操作Spark DataFrame的常见小坑:
- Spark SQL是大小写不敏感的,所以你用SQL语句能正常访问
Age列; - 但Scala代码里的
df.Age这种点语法,只适用于由Scala case类生成的DataFrame(比如通过spark.createDataFrame(Seq(...))基于case类创建)——因为此时编译时会为列生成对应的类属性。如果你的DataFrame是从外部数据源(CSV、JSON、数据库等)读取的,或是动态生成的,Spark不会为列生成编译时属性,用.访问自然会报错。
解决方案
改用通用的列引用方式:括号语法df("Age") 或者 col("Age")函数,这两种方式不受DataFrame创建方式限制,能稳定生效。修正后的代码如下:
import org.apache.spark.sql.types.DoubleType // 方式1:使用df("列名")引用列 val dfTemp = df.withColumn("temp", df("Age").cast(DoubleType)) .drop("Age") .withColumnRenamed("temp", "Age") // 方式2:使用col("列名")引用列(需先导入函数) import org.apache.spark.sql.functions.col val dfTemp = df.withColumn("temp", col("Age").cast(DoubleType)) .drop("Age") .withColumnRenamed("temp", "Age")
额外小技巧
如果之后想长期用点语法访问列,可以把DataFrame转换成强类型Dataset,先定义对应列的case类:
// 按你的列名和实际类型定义case类 case class Customer(Age: Int, Job: String, Marital: String, Education: String, Default: String, Balance: Int, Housing: String, Loan: String, Contact: String, Day: Int, Month: String, Duration: Int, Campaign: Int, pdays: Int, previous: Int, poutcome: String, Approved: String) val ds = df.as[Customer]
转换后就能用ds.Age这种点语法访问列,还能享受编译时类型检查的好处。
内容的提问来源于stack exchange,提问作者shanlodh
相关产品推荐
相关产品推荐

