Spark - 如何在没有文件夹的情况下编写单个csv文件?

时间:2017-04-27 15:12:44

标签: csv apache-spark export-to-csv

假设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文件放在一个文件夹中(而不是文件夹)文件夹)。

感谢任何帮助。

9 个答案:

答案 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)。

  1. df.coalesce(1).write.mode( “覆盖”)。格式( “com.databricks.spark.csv”)。选项( “头”, “true”)。csv(“PATH / FOLDER_NAME / x.csv”)

  2. df.repartition(1).write.mode( “覆盖”)。格式( “com.databricks.spark.csv”)。选项( “头”,     “真”)。CSV( “PATH / FOLDER_NAME / x.csv”)

  3. 如果你不使用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文件名的数组并将其提供给它,它将起作用