假设df
是Spark中的数据框。将df
写入单个CSV文件的方法是
df.coalesce(1).write.option("header", "true").csv("name.csv")
这会将数据帧写入包含在名为name.csv
的文件夹中的CSV文件中,但实际的CSV文件将被称为part-00000-af091215-57c0-45c4-a521-cd7d9afb5e54.csv
。
我想知道是否可以避开文件夹name.csv
并使实际的CSV文件名为name.csv
而不是part-00000-af091215-57c0-45c4-a521-cd7d9afb5e54.csv
。原因是我需要编写几个CSV文件,稍后我将用Python一起阅读,但我的Python代码使用实际的CSV名称,还需要将所有单个CSV文件放在一个文件夹中(而不是文件夹)文件夹)。
感谢任何帮助。
答案 0 :(得分:3)
可能的解决方案是将Spark数据帧转换为pandas数据帧并将其保存为csv:
df.toPandas().to_csv("<path>/<filename>")
答案 1 :(得分:2)
如果结果大小与Spark驱动程序节点的可用内存相当,则可能无法将数据帧转换为熊猫。
我会告诉spark保存到某个临时位置,然后将各个csv文件复制到所需的文件夹中。像这样:
import os
import shutil
TEMPORARY_TARGET="big/storage/name"
DESIRED_TARGET="/export/report.csv"
df.coalesce(1).write.option("header", "true").csv(TEMPORARY_TARGET)
part_filename = next(entry for entry in os.listdir(TEMPORARY_TARGET) if entry.startswith('part-'))
temporary_csv = os.path.join(TEMPORARY_TARGET, part_filename)
shutil.copyfile(temporary_csv, DESIRED_TARGET)
如果您使用databrick,spark可以处理dbfs:/mnt/...
之类的文件,并且要在它们上使用python的文件操作,则需要将路径更改为/dbfs/mnt/...
或((对于databricks而言更本机)替换{ {1}}和shutil.copyfile
。
答案 2 :(得分:1)
如果只想使用python标准库,这是一个简单的函数,它将写入单个文件。您不必弄乱tempfile或通过另一个目录。
import csv
def spark_to_csv(df, file_path):
""" Converts spark dataframe to CSV file """
with open(file_path, "w") as f:
writer = csv.DictWriter(f, fieldnames=df.columns)
writer.writerow(dict(zip(fieldnames, fieldnames)))
for row in df.toLocalIterator():
writer.writerow(row.asDict())
答案 3 :(得分:1)
更多的databricks'y'解决方案在这里:
TEMPORARY_TARGET="dbfs:/my_folder/filename"
DESIRED_TARGET="dbfs:/my_folder/filename.csv"
spark_df.coalesce(1).write.option("header", "true").csv(TEMPORARY_TARGET)
temporary_csv = os.path.join(TEMPORARY_TARGET, dbutils.fs.ls(TEMPORARY_TARGET)[3][1])
dbutils.fs.cp(temporary_csv, DESIRED_TARGET)
请注意,如果您使用的是Koalas数据框,则可以用koalas_df.to_spark()替换spark_df
答案 4 :(得分:1)
在输出文件夹中创建临时文件夹。将具有文件名的文件part-00000 *复制到输出文件夹。删除临时文件夹。 Python代码段可在Databricks中执行相同的操作。
fpath=output+'/'+'temp'
def file_exists(path):
try:
dbutils.fs.ls(path)
return True
except Exception as e:
if 'java.io.FileNotFoundException' in str(e):
return False
else:
raise
if file_exists(fpath):
dbutils.fs.rm(fpath)
df.coalesce(1).write.option("header", "true").csv(fpath)
else:
df.coalesce(1).write.option("header", "true").csv(fpath)
fname=([x.name for x in dbutils.fs.ls(fpath) if x.name.startswith('part-00000')])
dbutils.fs.cp(fpath+"/"+fname[0], output+"/"+"name.csv")
dbutils.fs.rm(fpath, True)
答案 5 :(得分:0)
由于写入操作,没有数据帧spark API写入/创建单个文件而不是目录。
以下两个选项将在目录中创建一个单独的文件以及标准文件(_SUCCESS,_committed,_started)。
df.coalesce(1).write.mode( “覆盖”)。格式( “com.databricks.spark.csv”)。选项( “头”, “true”)。csv(“PATH / FOLDER_NAME / x.csv”)
df.repartition(1).write.mode( “覆盖”)。格式( “com.databricks.spark.csv”)。选项( “头”, “真”)。CSV( “PATH / FOLDER_NAME / x.csv”)
如果你不使用coalesce(1)或repartition(1)并利用sparks parallelism来编写文件,那么它将在目录中创建多个数据文件。
您需要在驱动程序中编写函数,该函数会在写入操作完成后将所有数据文件部分组合到单个文件(cat part-00000 * singlefilename)。
答案 6 :(得分:0)
我遇到了同样的问题,并使用python的NamedTemporaryFile库来解决此问题。
from tempfile import NamedTemporaryFile
s3 = boto3.resource('s3')
with NamedTemporaryFile() as tmp:
df.coalesce(1).write.format('csv').options(header=True).save(tmp.name)
s3.meta.client.upload_file(tmp.name, S3_BUCKET, S3_FOLDER + 'name.csv')
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html了解有关upload_file()的更多信息
答案 7 :(得分:0)
对于pyspark,您可以将其转换为pandas数据框,然后将其保存。
> +!!replace(a,c(1,3,5),0)
[1] 0 1 0 1 0 1 1 1
答案 8 :(得分:-2)
df.write.mode("overwrite").format("com.databricks.spark.csv").option("header", "true").csv("PATH/FOLDER_NAME/x.csv")
您可以使用此功能,如果您不想在每次编写UDF时创建CSV名称或创建CSV文件名的数组并将其提供给它,它将起作用