Apache spark 将Spark数据帧写入CSV
我试图将Spark数据框写入CSV,但由于数据框的某些列具有数组,如下所示:Apache spark 将Spark数据帧写入CSV,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我试图将Spark数据框写入CSV,但由于数据框的某些列具有数组,如下所示: |ID|ArrayOfString|Age|Gender| +--+-------------+---+------+ |1 | [A,B,D] |22 | F | |2 | [A,Y] |42 | M | |3 | [X] |60 | F | +--+-------------+---+------+ 我得到一个错误: CSV数据源不支持数组数据类型 因此,我想
|ID|ArrayOfString|Age|Gender|
+--+-------------+---+------+
|1 | [A,B,D] |22 | F |
|2 | [A,Y] |42 | M |
|3 | [X] |60 | F |
+--+-------------+---+------+
我得到一个错误:
CSV数据源不支持数组数据类型
因此,我想迭代数据帧的列,对于数组类型的列,我想对它们进行字符串化mkString(“,”)
我找到了以下链接,它在Python中做了类似的事情:
我需要在Scala中执行此操作,我的尝试是:\
df.dtypes.map(dtype=>
{
val colName=dtype[0]
val colType=dtype[1]
if(colType.contains(“ArrayType”)){
df=df.withColumn(colName,df.col(colName).mkString(“,”).drop(df[colName])
}
})
但我是Scala的初学者,不知道如何解决这个问题。我做错了什么?您必须创建一个
udf
函数,将数组列更改为字符串列
而且由于您不知道arrayType列名,因此需要一个递归函数来迭代dataframe
列,以检查arrayType
并调用udf
函数
def recursiveFunction(dataFrame: DataFrame, dataTypes: List[Tuple2[String, String]]) : DataFrame = dataTypes match {
case x :: y => if (x._2.contains("ArrayType")) {
recursiveFunction(dataFrame.withColumn(x._1, arrayToStringUdf(col(x._1))), y)
}
else{
recursiveFunction(dataFrame, y)
}
case _ => dataFrame
}
您可以创建要在递归函数中迭代的Tuple2(colName,colType)
列表
因此,完整的解决方案如下所示
import org.apache.spark.sql.functions._
val arrayToStringUdf = udf((array: collection.mutable.WrappedArray[String]) => array.mkString(", "))
def recursiveFunction(dataFrame: DataFrame, dataTypes: List[Tuple2[String, String]]) : DataFrame = dataTypes match {
case x :: y => if (x._2.contains("ArrayType")) {
recursiveFunction(dataFrame.withColumn(x._1, arrayToStringUdf(col(x._1))), y)
}
else{
recursiveFunction(dataFrame, y)
}
case _ => dataFrame
}
val dataTypes = df.dtypes.map(dtype => (dtype._1, dtype._2)).toList
recursiveFunction(df, dataTypes).show(false)
我希望答案有帮助您必须创建一个
udf
函数来将数组列更改为字符串列
而且由于您不知道arrayType列名,因此需要一个递归函数来迭代dataframe
列,以检查arrayType
并调用udf
函数
def recursiveFunction(dataFrame: DataFrame, dataTypes: List[Tuple2[String, String]]) : DataFrame = dataTypes match {
case x :: y => if (x._2.contains("ArrayType")) {
recursiveFunction(dataFrame.withColumn(x._1, arrayToStringUdf(col(x._1))), y)
}
else{
recursiveFunction(dataFrame, y)
}
case _ => dataFrame
}
您可以创建要在递归函数中迭代的Tuple2(colName,colType)
列表
因此,完整的解决方案如下所示
import org.apache.spark.sql.functions._
val arrayToStringUdf = udf((array: collection.mutable.WrappedArray[String]) => array.mkString(", "))
def recursiveFunction(dataFrame: DataFrame, dataTypes: List[Tuple2[String, String]]) : DataFrame = dataTypes match {
case x :: y => if (x._2.contains("ArrayType")) {
recursiveFunction(dataFrame.withColumn(x._1, arrayToStringUdf(col(x._1))), y)
}
else{
recursiveFunction(dataFrame, y)
}
case _ => dataFrame
}
val dataTypes = df.dtypes.map(dtype => (dtype._1, dtype._2)).toList
recursiveFunction(df, dataTypes).show(false)
我希望答案是有帮助的您可以组合所有
ArrayType
列的列表,并使用foldLeft
遍历该列表以字符串化数组列:
val df = Seq(
(1, Seq("A", "B", "C"), 22, "F"),
(2, Seq("A", "Y"), 42, "M"),
(3, Seq("X"), 60, "F")
).toDF("ID", "ArrayOfString", "Age", "Gender")
import org.apache.spark.sql.types._
val arrTypeCols = df.schema.fields.collect{
case StructField(name, ArrayType(_, _), _, _) => name
}
// arrTypeCols: Array[String] = Array(ArrayOfString)
val df2 = arrTypeCols.foldLeft( df )( (acc, c) =>
acc.withColumn( c, concat_ws(", ", df(c)) )
)
df2.show
// +---+-------------+---+------+
// | ID|ArrayOfString|Age|Gender|
// +---+-------------+---+------+
// | 1| A, B, C| 22| F|
// | 2| A, Y| 42| M|
// | 3| X| 60| F|
// +---+-------------+---+------+
您可以组合所有
ArrayType
列的列表,并使用foldLeft
遍历该列表以字符串化数组列:
val df = Seq(
(1, Seq("A", "B", "C"), 22, "F"),
(2, Seq("A", "Y"), 42, "M"),
(3, Seq("X"), 60, "F")
).toDF("ID", "ArrayOfString", "Age", "Gender")
import org.apache.spark.sql.types._
val arrTypeCols = df.schema.fields.collect{
case StructField(name, ArrayType(_, _), _, _) => name
}
// arrTypeCols: Array[String] = Array(ArrayOfString)
val df2 = arrTypeCols.foldLeft( df )( (acc, c) =>
acc.withColumn( c, concat_ws(", ", df(c)) )
)
df2.show
// +---+-------------+---+------+
// | ID|ArrayOfString|Age|Gender|
// +---+-------------+---+------+
// | 1| A, B, C| 22| F|
// | 2| A, Y| 42| M|
// | 3| X| 60| F|
// +---+-------------+---+------+
你能解释一下为什么我们需要递归函数吗?是否要处理项本身中的一个元素可能是数组的情况?@tldr,建议使用递归函数循环遍历列并检查数据类型,最重要的是使用.withColumn api。我使用递归函数来避免使用var。你能解释一下为什么我们需要递归函数吗?是否要处理项本身中的一个元素可能是数组的情况?@tldr,建议使用递归函数循环遍历列并检查数据类型,最重要的是使用.withColumn api。我使用递归函数来避免使用var。谢谢@Ramesh Maharjan。这是一个有趣的问题,奇怪地标记了重复,尽管OP指出了主要区别。谢谢@Ramesh Maharjan。这是一个有趣的问题,奇怪地标记为重复,尽管OP指出了主要的区别。