簡體   English   中英

如何在磁盤上將火花DataFrame保存為csv?

[英]How to save a spark DataFrame as csv on disk?

例如,結果如下:

df.filter("project = 'en'").select("title","count").groupBy("title").sum()

會返回一個數組。

如何將火花DataFrame保存為磁盤上的csv文件?

Apache Spark不支持磁盤上的本機CSV輸出。

您有四種可用的解決方案:

  1. 您可以將Dataframe轉換為RDD:

     def convertToReadableString(r : Row) = ??? df.rdd.map{ convertToReadableString }.saveAsTextFile(filepath) 

    這將創建一個文件夾文件路徑。 在文件路徑下,您將找到分區文件(例如part-000 *)

    如果我想將所有分區附加到一個大的CSV中,我通常會這樣做

     cat filePath/part* > mycsvfile.csv 

    有些人會使用coalesce(1,false)從RDD創建一個分區。 這通常是一種不好的做法 ,因為它可能會通過將您收集的所有數據提取到驅動程序來壓倒驅動程序。

    請注意, df.rdd將返回RDD[Row]

  2. 使用Spark <2 ,您可以使用databricks spark-csv

    • Spark 1.4+:

       df.write.format("com.databricks.spark.csv").save(filepath) 
    • Spark 1.3:

       df.save(filepath,"com.databricks.spark.csv") 
  3. 使用Spark 2.x時 ,不需要spark-csv包,因為它包含在Spark中。

     df.write.format("csv").save(filepath) 
  4. 您可以轉換為本地Pandas數據框並使用to_csv方法(僅限PySpark)。

注意:解決方案1,2和3將導致由您調用save時Spark調用的基礎Hadoop API生成的CSV格式文件( part-* )。 每個分區都有一個part-文件。

我有類似的問題,我必須將數據幀的內容保存到我定義的名稱的csv文件。 df.write("csv").save("<my-path>")創建的目錄不是文件。 所以必須提出以下解決方案。 大多數代碼取自以下數據幀到csv ,對邏輯幾乎沒有修改。

def saveDfToCsv(df: DataFrame, tsvOutput: String, sep: String = ",", header: Boolean = false): Unit = {
    val tmpParquetDir = "Posts.tmp.parquet"

    df.repartition(1).write.
        format("com.databricks.spark.csv").
        option("header", header.toString).
        option("delimiter", sep).
        save(tmpParquetDir)

    val dir = new File(tmpParquetDir)
    val newFileRgex = tmpParquetDir + File.separatorChar + ".part-00000.*.csv"
    val tmpTsfFile = dir.listFiles.filter(_.toPath.toString.matches(newFileRgex))(0).toString
    (new File(tmpTsvFile)).renameTo(new File(tsvOutput))

    dir.listFiles.foreach( f => f.delete )
    dir.delete
    }

將數據幀寫入磁盤作為csv類似於從csv讀取。 如果您希望將結果作為一個文件,則可以使用coalesce。

df.coalesce(1)
      .write
      .option("header","true")
      .option("sep",",")
      .mode("overwrite")
      .csv("output/path")

如果您的結果是數組,則應使用特定於語言的解決方案,而不是spark dataframe api。 因為所有這些結果都會返回驅動程序機器。

我有類似的問題。 當我在客戶端模式下連接到群集時,我需要在驅動程序上寫下csv文件。

我想重用與Apache Spark相同的CSV解析代碼,以避免潛在的錯誤。

我檢查了spark-csv代碼並找到了負責將數據幀轉換為com.databricks.spark.csv.CsvSchemaRDD原始csv RDD[String]com.databricks.spark.csv.CsvSchemaRDD

遺憾的是,它是用sc.textFile和相關方法結束的硬編碼。

我復制粘貼該代碼並使用sc.textFile刪除最后一行並直接返回RDD。

我的代碼:

/*
  This is copypasta from com.databricks.spark.csv.CsvSchemaRDD
  Spark's code has perfect method converting Dataframe -> raw csv RDD[String]
  But in last lines of that method it's hardcoded against writing as text file -
  for our case we need RDD.
 */
object DataframeToRawCsvRDD {

  val defaultCsvFormat = com.databricks.spark.csv.defaultCsvFormat

  def apply(dataFrame: DataFrame, parameters: Map[String, String] = Map())
           (implicit ctx: ExecutionContext): RDD[String] = {
    val delimiter = parameters.getOrElse("delimiter", ",")
    val delimiterChar = if (delimiter.length == 1) {
      delimiter.charAt(0)
    } else {
      throw new Exception("Delimiter cannot be more than one character.")
    }

    val escape = parameters.getOrElse("escape", null)
    val escapeChar: Character = if (escape == null) {
      null
    } else if (escape.length == 1) {
      escape.charAt(0)
    } else {
      throw new Exception("Escape character cannot be more than one character.")
    }

    val quote = parameters.getOrElse("quote", "\"")
    val quoteChar: Character = if (quote == null) {
      null
    } else if (quote.length == 1) {
      quote.charAt(0)
    } else {
      throw new Exception("Quotation cannot be more than one character.")
    }

    val quoteModeString = parameters.getOrElse("quoteMode", "MINIMAL")
    val quoteMode: QuoteMode = if (quoteModeString == null) {
      null
    } else {
      QuoteMode.valueOf(quoteModeString.toUpperCase)
    }

    val nullValue = parameters.getOrElse("nullValue", "null")

    val csvFormat = defaultCsvFormat
      .withDelimiter(delimiterChar)
      .withQuote(quoteChar)
      .withEscape(escapeChar)
      .withQuoteMode(quoteMode)
      .withSkipHeaderRecord(false)
      .withNullString(nullValue)

    val generateHeader = parameters.getOrElse("header", "false").toBoolean
    val headerRdd = if (generateHeader) {
      ctx.sparkContext.parallelize(Seq(
        csvFormat.format(dataFrame.columns.map(_.asInstanceOf[AnyRef]): _*)
      ))
    } else {
      ctx.sparkContext.emptyRDD[String]
    }

    val rowsRdd = dataFrame.rdd.map(row => {
      csvFormat.format(row.toSeq.map(_.asInstanceOf[AnyRef]): _*)
    })

    headerRdd union rowsRdd
  }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM