如何使用spark-csv包在HDFS上只读取n行大型CSV文件?

时间:2017-05-31 06:15:01

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

我在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中n_rows等效的pandas,比如:

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

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

我想要

df.count()

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

6 个答案:

答案 0 :(得分:13)

您可以使用limit(n)

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

这只会加载20行。

答案 1 :(得分:8)

我的理解是,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")

答案 2 :(得分:2)

在所有方面,不推断架构并使用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解决方案也能很好地运作。 :)

答案 3 :(得分:1)

Jacek Laskowski 给出的解决方案运行良好。在下面展示内存中的变体。

我最近遇到了这个问题。我正在使用数据块并有一个巨大的 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)格式完成。

答案 4 :(得分:0)

自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函数,但是它需要一个架构,因此可能不符合您的要求。

答案 5 :(得分:0)

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

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

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