Apache spark 将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数据源不支持数组数据类型 因此,我想

我试图将Spark数据框写入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指出了主要的区别。