Scala 如何使用Spark从DBFS目录加载和处理多个csv文件
我想在从DBFS(Databricks文件系统)读取的每个文件上运行以下代码。我对文件夹中的所有文件进行了测试,但我想对文件夹中的每个文件逐一进行类似的计算:Scala 如何使用Spark从DBFS目录加载和处理多个csv文件,scala,csv,apache-spark,dataframe,databricks,Scala,Csv,Apache Spark,Dataframe,Databricks,我想在从DBFS(Databricks文件系统)读取的每个文件上运行以下代码。我对文件夹中的所有文件进行了测试,但我想对文件夹中的每个文件逐一进行类似的计算: // a-e are calculated fields val df2=Seq(("total",a,b,c,d,e)).toDF("file","total","count1","count2","count3","count4") //schema is now an empty dataframe val final1 = sc
// a-e are calculated fields
val df2=Seq(("total",a,b,c,d,e)).toDF("file","total","count1","count2","count3","count4")
//schema is now an empty dataframe
val final1 = schema.union(df2)
可能吗?我想从dbfs中读取它也应该与我现在所做的有所不同:
val df1 = spark
.read
.format("csv")
.option("header", "true")
.option("delimiter",",")
.option("inferSchema", "true")
.load("dbfs:/Reports/*.csv")
.select("lot of ids")
提前非常感谢您的想法:)如前所述,您在这里有3个选项 在我的示例中,我使用了以下3个数据集:
+----+----+----+
|col1|col2|col3|
+----+----+----+
|1 |100 |200 |
|2 |300 |400 |
+----+----+----+
+----+----+----+
|col1|col2|col3|
+----+----+----+
|3 |60 |80 |
|4 |12 |100 |
|5 |20 |10 |
+----+----+----+
+----+----+----+
|col1|col2|col3|
+----+----+----+
|7 |20 |40 |
|8 |30 |40 |
+----+----+----+
首先创建模式(显式定义模式比推断模式更快):
选项1:
使用以下各项一次性加载所有CSV:
val df1 = spark
.read
.option("header", "false")
.option("delimiter", ",")
.option("inferSchema", "false")
.schema(df_schema)
.csv("file:///C:/data/*.csv")
然后根据文件名将逻辑应用于整个数据集分组
前提条件:必须找到一种方法将文件名附加到每个文件中
选项2:
从目录加载csv文件。然后迭代文件,为每个csv创建一个数据帧。在循环内部,将您的逻辑应用于每个csv。最后,在循环结束时,将结果附加(并集)到第二个数据帧中,该数据帧将存储累积的结果
注意:请注意,大量文件可能会导致非常大的DAG,进而导致巨大的执行计划,为了避免这种情况,您可以保留当前结果或呼叫对方付费。在下面的示例中,我假设persist或collect将针对每个bufferSize迭代执行。您可以根据csv文件的数量调整甚至删除此逻辑
这是第二个选项的示例代码:
import java.io.File
import org.apache.spark.sql.Row
import spark.implicits._
val dir = "C:\\data_csv\\"
val csvFiles = new File(dir).listFiles.filter(_.getName.endsWith(".csv"))
val bufferSize = 10
var indx = 0
//create an empty df which will hold the accumulated results
var bigDf = spark.createDataFrame(spark.sparkContext.emptyRDD[Row], df_schema)
csvFiles.foreach{ path =>
var tmp_df = spark
.read
.option("header", "false")
.option("delimiter", ",")
.option("inferSchema", "false")
.schema(df_schema)
.csv(path.getPath)
//execute your custom logic/calculations with tmp_df
if((indx + 1) % bufferSize == 0){
// If buffer size reached then
// 1. call unionDf.persist() or unionDf.collect()
// 2. in the case you use collect() load results into unionDf again
}
bigDf = bigDf.union(tmp_df)
indx = indx + 1
}
bigDf.show(false)
这应该输出:
+----+----+----+
|col1|col2|col3|
+----+----+----+
|1 |100 |200 |
|2 |300 |400 |
|3 |60 |80 |
|4 |12 |100 |
|5 |20 |10 |
|7 |20 |40 |
|8 |30 |40 |
+----+----+----+
选项3:
最后一个选项是使用内置的spark.sparkContext.wholeTextFiles
这是将所有csv文件加载到RDD中的代码:
val data = spark.sparkContext.wholeTextFiles("file:///C:/data_csv/*.csv")
val df = spark.createDataFrame(data)
df.show(false)
以及输出:
+--------------------------+--------------------------+
|_1 |_2 |
+--------------------------+--------------------------+
|file:/C:/data_csv/csv1.csv|1,100,200 |
| |2,300,400 |
|file:/C:/data_csv/csv2.csv|3,60,80 |
| |4,12,100 |
| |5,20,10 |
|file:/C:/data_csv/csv3.csv|7,20,40 |
| |8,30,40 |
+--------------------------+--------------------------+
spark.sparkContext.wholeTextFiles
将返回一个键/值RDD,其中键是文件路径,值是文件数据
这需要额外的代码来提取作为每个csv内容的_2的内容。在我看来,这将包括有关程序的性能和可维护性的开销,因此我会避免它
如果您需要进一步澄清,请告诉我。我将补充@Alexandros Biratsis提供的答案。 可以使用如下第一种方法,将文件名作为一个单独的列连接到具有多个文件的所有数据的同一数据帧中
val df1 = spark
.read
.option("header", "false")
.option("delimiter", ",")
.option("inferSchema", "false")
.schema(df_schema)
.csv("file:///C:/data/*.csv")
.withColumn("FileName",input_file_name())
这里的input\u file\u name()
是一个函数,它将文件名添加到DataFrame
中的每一行。这是spark中的内置函数。要使用此函数,您需要导入以下命名空间。
导入org.apache.spark.sql.functions_ 可以在以下位置找到函数的文档: 我建议不要使用@Alexandros Biratsis建议的第二种方法,即进行联合并持久化临时数据帧,因为它可以处理少量文件,但随着文件数量的增加,速度会变得太慢,有时会超时,驱动程序会意外关闭
我要感谢Alexandros的回答,因为这给了我一个解决问题的方法。hi@Eva您有多少这样的文件?实际上,您在这里有两个选项,按文件名分组并为每个文件执行逻辑。或者遍历您的目录,将文件加载到数据帧中,执行逻辑,然后与另一个进行联合Dataframe@AlexandrosBiratsis谢谢你的评论,我不知道我有多少文件,它总是在变化,因为这个文件夹每周都会用新数据更新。我现在生成了一个函数,我尝试调用每个文件,明天我将在这里共享代码。是的,最后我会尝试把它结合起来。。。我对foreach和语法有问题:D我在上面:)欢迎Eva:)好的luckhello Eva,请注意,随着项目的发展和代码的继续,您不能随意更改初始问题。如果每个人都这样做,那么将充满单个项目,而不是具体的技术问题。我的回答是针对一个特定的问题,现在问题不同了,与第一个问题无关,因此最好创建一个新的问题,并进行新的描述。谢谢:)最后我做了一个函数,我对每个文件都调用它,并将结果作为附加的csv写出来。非常感谢您的努力!!:)嗨,伊娃,太好了,我很高兴它帮助了你@亚历山德罗斯比拉蒂斯。对于您建议的第二个选项。您说过,对于要破坏庞大执行计划的大量文件,我们可以调用persist()或collect()。如果我调用persist(),那么它应该是“仅内存”的默认选项,或者应该是“仅磁盘”。我有大约2500个文件和类似的场景。@Nikunkakadiya默认情况下,缓存或持久化将使用内存和磁盘保存数据。请查看下一页以了解更多详细信息
val df1 = spark
.read
.option("header", "false")
.option("delimiter", ",")
.option("inferSchema", "false")
.schema(df_schema)
.csv("file:///C:/data/*.csv")
.withColumn("FileName",input_file_name())