Например, результат этого:
df.filter("project = 'en'").select("title","count").groupBy("title").sum()
вернет массив.
Как сохранить искровой DataFrame в виде CSV файла на диске?
Например, результат этого:
df.filter("project = 'en'").select("title","count").groupBy("title").sum()
вернет массив.
Как сохранить искровой DataFrame в виде CSV файла на диске?
Apache Spark не поддерживает собственный вывод CSV на диск.
У вас есть четыре доступных решения:
Вы можете преобразовать свой Dataframe в RDD:
def convertToReadableString(r : Row) = ???
df.rdd.map{ convertToReadableString }.saveAsTextFile(filepath)
Это создаст папку filepath. Под путем к файлу вы найдете файлы разделов (например, part-000 *)
Что я обычно делаю, если хочу добавить все разделы в большой CSV, так это
cat filePath/part* > mycsvfile.csv
Некоторые будут использовать coalesce(1,false)
для создания одного раздела из RDD. Как правило, это плохая практика, поскольку он может перегружать драйвер, вытягивая в него все данные, которые вы собираете.
Обратите внимание, что df.rdd
вернет RDD[Row]
.
С Spark & lt; 2 вы можете использовать библиотеку данных spark-csv library:
Искра 1. 4+:
df.write.format("com.databricks.spark.csv").save(filepath)
Искра 1.3:
df.save(filepath,"com.databricks.spark.csv")
В Spark 2.x пакет spark-csv
не нужен, поскольку он включен в Spark.
df.write.format("csv").save(filepath)
Вы можете преобразовать в локальный фрейм данных Pandas и использовать метод to_csv
(только PySpark).
Примечание: Решения 1, 2 и 3 приведут к файлам формата CSV (part-*
), сгенерированным базовым Hadoop API, который Spark вызывает при вызове save
. У вас будет один файл part-
на раздел.
У меня была аналогичная проблема, когда мне пришлось сохранять содержимое фрейма данных в файл csv, который я определил. df.write("csv").save("<my-path>")
создавал каталог, чем файл. Так что приходите к следующим решениям.
Большая часть кода взята из следующего dataframe-to-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. Если вы хотите, чтобы ваш результат был одним файлом, вы можете использовать объединение.
df.coalesce(1)
.write
.option("header","true")
.option("sep",",")
.mode("overwrite")
.csv("output/path")
Если ваш результат является массивом, вы должны использовать решение, зависящее от языка, а не искры dataframe api. Потому что все эти результаты возвращают водителю машины.
У меня была аналогичная проблема. Мне нужно было записать файл csv на драйвер, когда я подключался к кластеру в клиентском режиме.
Я хотел повторно использовать тот же код синтаксического анализа CSV, что и Apache Spark, чтобы избежать потенциальных ошибок.
Я проверил код spark-csv и нашел код, отвечающий за преобразование фрейма данных в исходный код csv RDD[String]
в com.databricks.spark.csv.CsvSchemaRDD
.
К сожалению, он жестко закодирован с помощью sc.textFile
и конца соответствующего метода.
Я скопировал этот код и удалил последние строки с помощью sc.textFile
и вместо этого вернул RDD.
Мой код:
/*
This is copypasta from com.databricks.spark.csv.CsvSchemaRDD
Spark code has perfect method converting Dataframe -> raw csv RDD[String]
But in last lines of that method it 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
}
}