私は https://github.com/databricks/spark-csv を使用しています。= 1つのCSVファイルを書き込もうとしていますが、できません。フォルダを作成しています。
パスやファイル名などのパラメータを取り、そのCSVファイルに書き込むScala関数が必要です。
各パーティションは個別に保存されるため、複数のファイルを含むフォルダーを作成しています。単一の出力ファイル(フォルダ内にある)が必要な場合はrepartition
を使用できます(アップストリームデータが大きい場合は推奨されますが、シャッフルが必要です)。
df
.repartition(1)
.write.format("com.databricks.spark.csv")
.option("header", "true")
.save("mydata.csv")
またはcoalesce
:
df
.coalesce(1)
.write.format("com.databricks.spark.csv")
.option("header", "true")
.save("mydata.csv")
保存前のデータフレーム:
すべてのデータはmydata.csv/part-00000
に書き込まれます。このオプションを使う前に、何が起こっているのか、そしてすべてのデータを1人のワーカーに転送するのにかかるコストはいくらか理解しておいてください。レプリケーションで分散ファイルシステムを使用している場合、データは複数回転送されます。最初に単一のワーカーにフェッチされ、続いてストレージノードに分散されます。
あるいは、コードをそのままにして、cat
や HDFS getmerge
のような汎用ツールを使用して、後ですべての部分を単純にマージすることもできます。
あなたがHDFSでSparkを走らせているならば、私はcsvファイルを普通に書き、マージをするためにHDFSを利用することによって問題を解決してきました。私はそれをSpark(1.6)で直接やっています。
import org.Apache.hadoop.conf.Configuration
import org.Apache.hadoop.fs._
def merge(srcPath: String, dstPath: String): Unit = {
val hadoopConfig = new Configuration()
val hdfs = FileSystem.get(hadoopConfig)
FileUtil.copyMerge(hdfs, new Path(srcPath), hdfs, new Path(dstPath), true, hadoopConfig, null)
// the "true" setting deletes the source files once they are merged into the new output
}
val newData = << create your dataframe >>
val outputfile = "/user/feeds/project/outputs/subject"
var filename = "myinsights"
var outputFileName = outputfile + "/temp_" + filename
var mergedFileName = outputfile + "/merged_" + filename
var mergeFindGlob = outputFileName
newData.write
.format("com.databricks.spark.csv")
.option("header", "false")
.mode("overwrite")
.save(outputFileName)
merge(mergeFindGlob, mergedFileName )
newData.unpersist()
私がこのトリックを学んだ場所を覚えていないことができます、しかしそれはあなたのために働くかもしれません。
私はここで少しゲームに遅れるかもしれませんが、coalesce(1)
またはrepartition(1)
を使うことは小さなデータセットのために働くかもしれませんが、大きなデータセットはすべて1つのノードの1つのパーティションに投げられるでしょう。これはOOMエラーをスローするか、せいぜいゆっくりと処理する可能性があります。
Hadoop APIの FileUtil.copyMerge()
関数を使用することを強くお勧めします。これにより、出力が単一のファイルにマージされます。
編集 - これにより、データがエグゼキュータノードではなくドライバに効果的にもたらされます。単一のexecutorがドライバよりもRAMを使用する場合はCoalesce()
は問題ありません。
編集2:copyMerge()
はHadoop 3.0で削除されています。最新バージョンでの作業方法について詳しくは、以下のスタックオーバーフローの記事を参照してください。 Hadoop Hadoop 3.0でCopyMergeを実行する方法
データブリックを使用していて、1人のワーカーですべてのデータをRAMに収めることができる場合(したがって.coalesce(1)
を使用する場合)、dbfsを使用して結果のCSVファイルを見つけて移動できます。
val fileprefix= "/mnt/aws/path/file-prefix"
dataset
.coalesce(1)
.write
//.mode("overwrite") // I usually don't use this, but you may want to.
.option("header", "true")
.option("delimiter","\t")
.csv(fileprefix+".tmp")
val partition_path = dbutils.fs.ls(fileprefix+".tmp/")
.filter(file=>file.name.endsWith(".csv"))(0).path
dbutils.fs.cp(partition_path,fileprefix+".tab")
dbutils.fs.rm(fileprefix+".tmp",recurse=true)
ファイルがワーカーのRAMに収まらない場合は、FileUtils.copyMerge()を使用するという chaotic3quilibriumの提案を検討してください 。私はこれをしていません、そして、それが可能であるかどうかをまだ知りません、例えば、S3で。
この答えは、この質問に対する以前の答え、および提供されたコードスニペットの私自身のテストに基づいています。 私はもともとそれをDatabricks に投稿し、ここで再公開しています。
私が見つけたdbfsのrmの再帰的なオプションのための最もよいドキュメンテーションは Databricksフォーラム にあります。
Minkymorganから変更されたS3用のソリューション。
一時パーティション・ディレクトリー・パスを(最終パスとは異なる名前で)srcPath
として渡し、単一の最終csv/txtをdestPath
として渡すだけです。元のディレクトリーを削除したい場合は、deleteSource
も指定します。
/**
* Merges multiple partitions of spark text file output into single file.
* @param srcPath source directory of partitioned files
* @param dstPath output path of individual path
* @param deleteSource whether or not to delete source directory after merging
* @param spark sparkSession
*/
def mergeTextFiles(srcPath: String, dstPath: String, deleteSource: Boolean): Unit = {
import org.Apache.hadoop.fs.FileUtil
import Java.net.URI
val config = spark.sparkContext.hadoopConfiguration
val fs: FileSystem = FileSystem.get(new URI(srcPath), config)
FileUtil.copyMerge(
fs, new Path(srcPath), fs, new Path(dstPath), deleteSource, config, null
)
}
あなたはrdd.coalesce(1, true).saveAsTextFile(path)
を使うことができます
データを単一ファイルとしてpath/part-00000に保存します。
保存する前に1つのパーティションに再分割/合体します(まだフォルダを取得できますが、その中に1つの部分ファイルがあります)。