将Spark DataFrame写入CSV

时间:2018-02-08 00:55:20

标签: 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 data source does not support array<string> data type

所以,我想迭代数据框的列,对于类型为数组的列,我想对它们进行字符串化mkString(", ")

我找到了以下链接,它在Python中做了类似的事情: https://hadoopist.wordpress.com/2016/08/05/spark-data-frame-check-for-any-column-values-with-n-and-y-and-convert-the-corresponding-column-to-boolean-using-pyspark/

我需要在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的初学者,无法弄清楚如何解决这个问题。我在这里做错了什么?

2 个答案:

答案 0 :(得分:2)

您可以汇编所有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|
// +---+-------------+---+------+

答案 1 :(得分:1)

您必须创建circular = TRUE函数才能将数组列更改为字符串列

udf

并且因为您不知道 arrayType列名,所以您需要一个递归函数来遍历import org.apache.spark.sql.functions._ val arrayToStringUdf = udf((array: collection.mutable.WrappedArray[String]) => array.mkString(", ")) 列以检查{ {1}}并调用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)

我希望答案很有帮助