【问题标题】:How to read only n rows of large CSV file on HDFS using spark-csv package?如何使用 spark-csv 包在 HDFS 上仅读取 n 行大型 CSV 文件?
【发布时间】:2017-10-31 19:13:25
【问题描述】:

我在 HDFS 上有一个大的分布式文件,每次我使用带有 spark-csv 包的 sqlContext 时,它都会首先加载整个文件,这需要相当长的时间。

df = sqlContext.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load("file_path")

现在我有时只是想快速检查一下,我只需要整个文件的几行/任意 n 行。

df_n = sqlContext.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load("file_path").take(n)
df_n = sqlContext.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load("file_path").head(n)

但所有这些都在文件加载完成后运行。我不能在读取文件本身时限制行数吗?我指的是 spark-csv 中与 pandas 等效的 n_rows,例如:

pd_df = pandas.read_csv("file_path", nrows=20)

或者可能是spark实际上并没有加载文件,第一步,但是在这种情况下,为什么我的文件加载步骤花费了太多时间呢?

我想要

df.count()

只给我n 而不是所有行,这可能吗?

【问题讨论】:

    标签: apache-spark pyspark hdfs apache-spark-sql spark-csv


    【解决方案1】:

    您可以使用limit(n)

    sqlContext.format('com.databricks.spark.csv') \
              .options(header='true', inferschema='true').load("file_path").limit(20)
    

    这只会加载 20 行。

    【讨论】:

    • 这会排除inferSchema,即如果我启用inferSchema会怎样?
    • 如果你推断Schema,无论如何都会扫描所有数据。已扫描但未加载。
    • 扫描与加载有何不同?
    • “扫描作为传递数据”==“加载就像将数据放入内存”在 Spark 执行器上是一样的。
    • @eliasah 提供的解决方案首先加载文件然后限制它。如果我有一个 PB 文件,这有什么帮助?无论如何,这将加载所有行,然后显示有限的记录 n.
    【解决方案2】:

    我的理解是 spark-csv 模块不支持直接读取几行,作为 解决方法,您可以将文件作为文本文件读取,尽可能多地读取想要并将其保存到某个临时位置。保存行后,您可以使用 spark-csv 读取行,包括 inferSchema 选项(如果您处于探索模式,您可能希望使用该选项)。

    val numberOfLines = ...
    spark.
      read.
      text("myfile.csv").
      limit(numberOfLines).
      write.
      text(s"myfile-$numberOfLines.csv")
    val justFewLines = spark.
      read.
      option("inferSchema", true). // <-- you are in exploration mode, aren't you?
      csv(s"myfile-$numberOfLines.csv")
    

    【讨论】:

    • +1 虽然 .read() 不会被多个执行程序读取,所以 .limit() 的顺序不能保证?换句话说,是的,你得到了numberOfLines 行,但这些不是原始“myfile.csv”中的第一行,不是吗?
    • 我试过了,但 text().limit(2) 似乎是先读取整个文件,在我的例子中是几个 GB。
    • @Vaibhav 由于 HDFS 上的分割大小很大,您的文件似乎没有被分割,并且只是适合一个分割(?)只是猜测。您应该提出一个单独的问题并提供更多信息,例如查询计划和 HDFS 配置。
    • @Tagar 我的理解是保证订单。将 CSV 文件上传到 HDFS 后,拆分将按顺序进行,这就是 Spark 对文件的了解。
    • 我注意到 csv 阅读器有选项 samplingRatio – defines fraction of rows used for schema inferring. If None is set, it uses the default value, 1.0. 也许这是一种在探索中推断模式的高效方法,并帮助您明确指定模式以实现生产中的性能和可重复性。
    【解决方案3】:

    不推断架构并使用limit(n) 在所有方面都对我有用。

    f_schema = StructType([
    StructField("col1",LongType(),True),
    StructField("col2",IntegerType(),True),
    StructField("col3",DoubleType(),True)
    ...
    ])
    
    df_n = sqlContext.read.format('com.databricks.spark.csv').options(header='true').schema(f_schema).load(data_path).limit(10)
    

    注意:如果我们使用inferschema='true',它又是同一时间,因此可能是同样的旧东西。

    但如果我们不了解架构,Jacek Laskowski 解决方案也很有效。 :)

    【讨论】:

    • 明确提供模式总是会更好,如果你有的话。您可以在第一次阅读时使用inferSchema,然后保存并使用df.schema 以备后用,但如果文件太大以至于即使执行一次也太多,那么建议的解决方法是正确的。正确答案是这个和stackoverflow.com/a/44277219/1335793的组合
    【解决方案4】:

    从 PySpark 2.3 开始,您可以简单地将数据加载为文本、限制并在结果上应用 csv 阅读器:

    (spark
      .read
      .options(inferSchema="true", header="true")
      .csv(
          spark.read.text("/path/to/file")
              .limit(20)                   # Apply limit
              .rdd.flatMap(lambda x: x)))  # Convert to RDD[str]
    

    Scala 对应版本从 Spark 2.2 开始可用:

    spark
      .read
      .options(Map("inferSchema" -> "true", "header" -> "true"))
      .csv(spark.read.text("/path/to/file").limit(20).as[String])
    

    在 Spark 3.0.0 或更高版本中,也可以应用限制并使用 from_csv 函数,但它需要一个架构,因此它可能不符合您的要求。

    【讨论】:

    • 原始问题并不排除了解架构。我认为值得添加 Spark 3.0.0 解决方案并提到 如果您知道架构,那么您应该提供它而不是使用 inferSchema。这将作为正确答案完成。
    • 奇怪的是没有显示标题。也许 limit 不会保留行的顺序,因为它是一个 RDD
    【解决方案5】:

    Jacek Laskowski 给出的解决方案效果很好。下面展示一个内存中的变体。

    我最近遇到了这个问题。我正在使用 databricks 并且有一个巨大的 csv 目录(200 个文件,每个文件 200MB)

    我原来有

    val df = spark.read.format("csv")
    .option("header", true)
    .option("sep", ",")
    .option("inferSchema", true)
    .load("dbfs:/huge/csv/files/in/this/directory/")
    
    display(df)
    
    

    这花了很多时间(10 多分钟),但后来我把它改成下面,它立即运行(2 秒)

    val lines = spark.read.text("dbfs:/huge/csv/files/in/this/directory/").as[String].take(1000)
    
    val df = spark.read
    .option("header", true)
    .option("sep", ",")
    .option("inferSchema", true)
    .csv(spark.createDataset(lines))
    
    display(df)
    
    

    推断文本格式的架构很困难,对于 csv 和 json(但如果是多行 json)格式,则可以这样做。

    【讨论】:

      【解决方案6】:

      由于我没有在答案中看到该解决方案,因此纯 SQL 方法对我有用:

      df = spark.sql("SELECT * FROM csv.`/path/to/file` LIMIT 10000")
      

      如果没有标题,列将命名为 _c0、_c1 等。不需要架构。

      【讨论】:

        【解决方案7】:

        这可能会对在 java 中工作的人有所帮助。 应用限制无助于减少时间。您必须从文件中收集 n 行。

                DataFrameReader frameReader = spark
                  .read()
                  .format("csv")
                  .option("inferSchema", "true");
            //set framereader options, delimiters etc
        
            List<String> dataset = spark.read().textFile(filePath).limit(MAX_FILE_READ_SIZE).collectAsList();
            return frameReader.csv(spark.createDataset(dataset, Encoders.STRING()));
        

        【讨论】:

          猜你喜欢
          • 2018-11-02
          • 1970-01-01
          • 2015-11-11
          • 2013-06-13
          • 2020-08-05
          • 2016-01-03
          • 1970-01-01
          • 2014-11-15
          相关资源
          最近更新 更多