我想在AWS中创建一个数据处理管道,最终将处理后的数据用于机器学习。
我有一个Scala脚本,它从S3获取原始数据,处理它并使用 Spark-CSV 将其写入HDFS甚至S3。我想如果我想使用 AWS Machine Learning 工具来训练预测模型,我可以使用多个文件作为输入。但是如果我想使用别的东西,我认为最好是收到一个CSV输出文件。
目前,由于我不想使用重新分区(1)或合并(1)来达到性能目的,我使用了 hadoop fs -getmerge < / strong>用于手动测试,但因为它只是合并了作业输出文件的内容,所以我遇到了一个小问题。我需要在数据文件中使用单行标题来训练预测模型。
如果我使用.option("header","true")
作为spark-csv,那么它会将标头写入每个输出文件,并且在合并之后我在数据中有与输出文件一样多的标题行。但是如果header选项为false,则它不会添加任何标题。
现在我找到了一个选项,可以将Scala脚本中的文件与Hadoop API FileUtil.copyMerge
合并。我在spark-shell
中尝试了以下代码。
import org.apache.hadoop.fs.FileUtil
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
val configuration = new Configuration();
val fs = FileSystem.get(configuration);
FileUtil.copyMerge(fs, new Path("smallheaders"), fs, new Path("/home/hadoop/smallheaders2"), false, configuration, "")
但是这个解决方案仍然只是将文件连接在一起,并且不处理标题。 如何获得只有一行标题的输出文件?
我甚至尝试添加df.columns.mkString(",")
作为copyMerge
的最后一个参数,但这仍然多次添加标题,而不是一次。
答案 0 :(得分:3)
您可以像这样走动。
通过这种方式,所有分区都没有标题,除了单个分区的内容具有来自headerDF的标题名称行。当所有分区合并在一起时,文件顶部只有一个标头。示例代码如下
//dataFrame is the data to save on disk
//cast types of all columns to String
val dataDF = dataFrame.select(dataFrame.columns.map(c => dataFrame.col(c).cast("string")): _*)
//create a new data frame containing only header names
import scala.collection.JavaConverters._
val headerDF = sparkSession.createDataFrame(List(Row.fromSeq(dataDF.columns.toSeq)).asJava, dataDF.schema)
//merge header names with data
headerDF.union(dataDF).write.mode(SaveMode.Overwrite).option("header", "false").csv(outputFolder)
//use hadoop FileUtil to merge all partition csv files into a single file
val fs = FileSystem.get(sparkSession.sparkContext.hadoopConfiguration)
FileUtil.copyMerge(fs, new Path(outputFolder), fs, new Path("/folder/target.csv"), true, spark.sparkContext.hadoopConfiguration, null)
答案 1 :(得分:1)
答案 2 :(得分:0)
将文件夹中的文件合并到一个文件中:
Models
如果要将所有文件合并到一个文件中,但仍在同一文件夹中(但这会将所有数据都带到驱动程序节点):
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), false, hadoopConfig, null)
}
另一个解决方案是使用解决方案#2然后将文件夹中的一个文件移动到另一个路径(使用我们的CSV文件的名称)。
dataFrame
.coalesce(1)
.write
.format("com.databricks.spark.csv")
.option("header", "true")
.save(out)
答案 3 :(得分:0)
尝试使用选项drop malformed of spark-csv指定标题的架构并从文件夹中读取所有文件。这应该让你读取文件夹中的所有文件,只保留标题(因为你丢弃了格式错误)。 例如:
DT_DIR
在header_DF中,您将只拥有标题行,您可以根据需要对数据帧进行转换。
答案 4 :(得分:0)
按照以下方法获取单个输出文件,我们遇到了类似的问题-
coalesce
或repartition
将数据帧写入具有标题的hdfs。dataframe.write.format("csv").option("header", "true").save(hdfs_path_for_multiple_files)
coalesce(1)
写回hdfs上的其他位置。dataframe = spark.read.option('header', 'true').csv(hdfs_path_for_multiple_files)
dataframe.coalesce(1).write.format('csv').option('header', 'true').save(hdfs_path_for_single_file)
这样,您将避免在执行转换时(步骤1)与合并或重新分区有关的性能问题。 第二步,提供一个标题行的单个输出文件。
答案 5 :(得分:-4)
// Convert JavaRDD to CSV and save as text file
outputDataframe.write()
.format("com.databricks.spark.csv")
// Header => true, will enable to have header in each file
.option("header", "true")
请关注如何编写单个标题的集成测试链接
http://bytepadding.com/big-data/spark/write-a-csv-text-file-from-spark/