想象以下输入:
val data = Seq (("1::Alice"), ("2::Bob"))
val dfInput = data.toDF("input")
val dfTwoColTypeString = dfInput.map(row => row.getString(0).split("::")).map{ case Array(id, name) => (id, name) }.toDF("id", "name")
现在,我有一个带有所需列的DataFrame:
scala> dfTwoColTypeString.show
+---+-----+
| id| name|
+---+-----+
| 1|Alice|
| 2| Bob|
+---+-----+
我当然希望具有int类型的列ID,但它的类型为String:
scala> dfTwoColTypeString.printSchema
root
|-- id: string (nullable = true)
|-- name: string (nullable = true)
因此,我定义了以下架构:
val mySchema = StructType(Array(
StructField("id", IntegerType, true),
StructField("name", StringType, true)
))
将DataFrame dfTwoColTypeString转换或转换为给定目标架构的最佳方法是什么。
奖金:如果不能将给定的输入强制转换或转换为目标模式,我希望获得一个空行,并带有一个额外的列“ bad_record”,其中包含错误的输入数据。也就是说,我想完成与PERMISSIVE模式下的CSV解析器相同的操作。
任何帮助都很感激。
答案 0 :(得分:1)
如果在读取数据时需要转换,则可以使用以下代码:
val resultDF = mySchema.fields.foldLeft(dfTwoColTypeString)((df, c) => df.withColumn(c.name, col(c.name).cast(c.dataType)))
resultDF.printSchema()
输出:
root
|-- id: integer (nullable = true)
|-- name: string (nullable = true)
要检查值的匹配类型,可以使用以下代码:
val dfTwoColTypeString = dfInput.map(
row =>
row.getString(0).split("::"))
.map {
case Array(id, name) =>
if (ConvertUtils.canBeCasted((id, name), mySchema))
(id, name, null)
else (null, null, id + "::" + name)}
.toDF("id", "name", "malformed")
可以在自定义类(此处为ConvertUtils)中创建两个新的静态函数:
def canBeCasted(values: Product, mySchema: StructType): Boolean = {
mySchema.fields.zipWithIndex.forall(v => canBeCasted(values.productElement(v._2).asInstanceOf[String], v._1.dataType))
}
import scala.util.control.Exception.allCatch
def canBeCasted(value: String, dtype: DataType): Boolean = dtype match {
case StringType => true
case IntegerType => (allCatch opt value.toInt).isDefined
// TODO add other types here
case _ => false
}
输出错误的“ cc :: Bob”值:
+----+-----+---------+
|id |name |malformed|
+----+-----+---------+
|1 |Alice|null |
|null|null |cc::Bob |
+----+-----+---------+
答案 1 :(得分:0)
val cols = Array(col("id").cast(IntegerType),col("name"))
dfTwoColTypeString.select(cols:_*).printSchema
根 |-id:整数(nullable = true) |-名称:字符串(nullable = true)
//另一种方法
import org.apache.spark.sql.types.{StringType,IntegerType,StructType,StructField}
val mySchema = StructType(Array(StructField("id", IntegerType, true),StructField("name", StringType, true)))
val df = spark.createDataFrame(dfTwoColTypeString.rdd,mySchema)
df.printSchema
根 |-id:整数(nullable = true) |-名称:字符串(nullable = true)
答案 2 :(得分:0)
如果需要CSV读取并且知道模式,则可以在读取过程中进行分配:
spark.read.schema(mySchema).csv("filename.csv")
答案 3 :(得分:0)
考虑将dfTwoColTypeString
用作数据框,您还可以如下转换其模式类型。
dfTwoColTypeString.withColumn("id", col("id").cast("Int"))