如何从Spark中的CSV文件中跳过标题?


68

假设我给出了三个要读取的Spark上下文的文件路径,并且每个文件的第一行都有一个架构。我们如何从标题中跳过模式行?

val rdd=sc.textFile("file1,file2,file3")

现在,我们如何跳过此rdd的标题行?

Answers:


70

如果第一条记录中只有一个标题行,那么最有效的过滤方法是:

rdd.mapPartitionsWithIndex {
  (idx, iter) => if (idx == 0) iter.drop(1) else iter 
}

如果里面有很多带有很多标题行的文件,这将无济于事。的确,您可以用这种方法合并三个RDD。

您也可以编写filter只与可能是标题的行匹配的。这很简单,但是效率较低。

相当于Python:

from itertools import islice

rdd.mapPartitionsWithIndex(
    lambda idx, it: islice(it, 1, None) if idx == 0 else it 
)

4
过滤方法仍然比zipWithIndex 其他答案中提出的方法更有效。
maasg'1

不,不仅有单行,每个文件可能都有一行。
哈菲兹·穆贾迪德

是的,我的意思是您可以为每个文件创建一个RDD,并以此方式剥离其单个标头,然后进行合并。
肖恩·欧文

这里缺少和drop(n)方法
Julio

1
rdd.mapPartitionsWithIndex { (idx, iter) => if (idx == 0) iter.drop(1) else iter }您如何说如果索引值为0,那么它将是标头?这无济于事,它可以是CSV的标头或其他值,也可以是具有该值的标头
Shubham Agrawal

97
data = sc.textFile('path_to_data')
header = data.first() #extract header
data = data.filter(row => row != header)   #filter out header

6
该问题询问有关如何跳过csv文件中的标头的信息,如果标头曾经存在,它们将出现在第一行中。
吉米

3
这并非总是如此。如果用Spark写出一个csv,则可能有多个文件,每个文件都有自己的标头。将其用作另一个Spark程序的输入将为您提供多个标头。另外,您可以使用Spark将多个文件合在一起。
2016年

直观的方法
jack AKA karthik

错误:递归值数据需要类型。将最后一行更改为dataFiltered = data.filter(row => row!= header)
Amit Sadafule,

此解决方案是否扫描整个rdd并检查每一行,只是将标题弹出到最顶部?这真的是最有效的方法吗?
iLikeKFC

59

在Spark 2.0中,Spark内置了CSV阅读器,因此您可以轻松地按以下方式加载CSV文件:

spark.read.option("header","true").csv("filePath")

您确定从2.0开始可以使用吗?我正在使用v2.0.1,并收到“ AttributeError:'SparkContext'对象没有属性'read'”。
ciri

10
@ciri spark不是其SparkSession对象的SparkContext对象,因此,如果您想使用csv阅读器,则需要SparkSession对象
Sandeep Purohit

14

Spark 2.0开始,您可以使用SparkSession作为一个衬套完成此任务:

val spark = SparkSession.builder.config(conf).getOrCreate()

然后就像@SandeepPurohit所说的:

val dataFrame = spark.read.format("CSV").option("header","true").load(csvfilePath)

希望它能解决您的问题!

PS:SparkSession是Spark 2.0中引入的新入口点,可以在spark_sql包找到


7

在PySpark中,您可以使用数据框并将标头设置为True:

df = spark.read.csv(dataPath, header=True)

或者context = new org.apache.spark.sql.SQLContext(sc); var data = context.read.option(“ header”,“ true”)。csv(“ <path>”);
Sahan Jayasumana '18

5

您可以分别加载每个文件,使用它们进行过滤file.zipWithIndex().filter(_._2 > 0),然后合并所有文件RDD。

如果文件数太大,则联合会抛出StackOverflowExeption


4

使用filter()PySpark中的方法,过滤掉第一列名称以删除标题:

# Read file (change format for other file formats)
contentRDD = sc.textfile(<filepath>)

# Filter out first column of the header
filterDD = contentRDD.filter(lambda l: not l.startswith(<first column name>)

# Check your result
for i in filterDD.take(5) : print (i)

这与给定的答案有何不同?您的答案将需要您提前知道第一列的名称。
OneCricketeer

@ cricket_007,因为它将过滤掉其他用户指向的多个标题列。
Abdul Mannan

4

在2018年工作(Spark 2.3)

蟒蛇

df = spark.read
    .option("header", "true")
    .format("csv")
    .schema(myManualSchema)
    .load("mycsv.csv")

斯卡拉

val myDf = spark.read
  .option("header", "true")
  .format("csv")
  .schema(myManualSchema)
  .load("mycsv.csv")

PD1:myManualSchema是我编写的预定义架构,您可以跳过该部分代码


1
那么-1在这里是什么?
thebluephantom

1
@Antonio Cachuan,您应该提供有效的代码,而您的个人示例“ schema(myManualSchema)”根本不在解决方案中。
恩里克·贝尼托·卡萨多

1

您可以将以下选项传递给read()命令:

context = new org.apache.spark.sql.SQLContext(sc)

var data = context.read.option("header","true").csv("<path>")

0

或者,您可以使用spark-csv软件包(或在Spark 2.0中,或多或少可以以CSV的形式提供)。请注意,这需要每个文件的标头(根据需要):

schema = StructType([
        StructField('lat',DoubleType(),True),
        StructField('lng',DoubleType(),True)])

df = sqlContext.read.format('com.databricks.spark.csv'). \
     options(header='true',
             delimiter="\t",
             treatEmptyValuesAsNulls=True,
             mode="DROPMALFORMED").load(input_file,schema=schema)

-2
//Find header from the files lying in the directory
val fileNameHeader = sc.binaryFiles("E:\\sss\\*.txt",1).map{
    case (fileName, stream)=>
        val header = new BufferedReader(new InputStreamReader(stream.open())).readLine()
        (fileName, header)
}.collect().toMap

val fileNameHeaderBr = sc.broadcast(fileNameHeader)

// Now let's skip the header. mapPartition will ensure the header
// can only be the first line of the partition
sc.textFile("E:\\sss\\*.txt",1).mapPartitions(iter =>
    if(iter.hasNext){
        val firstLine = iter.next()
        println(s"Comparing with firstLine $firstLine")
        if(firstLine == fileNameHeaderBr.value.head._2)
            new WrappedIterator(null, iter)
        else
            new WrappedIterator(firstLine, iter)
    }
    else {
        iter
    }
).collect().foreach(println)

class WrappedIterator(firstLine:String,iter:Iterator[String]) extends Iterator[String]{
    var isFirstIteration = true
    override def hasNext: Boolean = {
        if (isFirstIteration && firstLine != null){
            true
        }
        else{
            iter.hasNext
        }
    }

    override def next(): String = {
        if (isFirstIteration){
            println(s"For the first time $firstLine")
            isFirstIteration = false
            if (firstLine != null){
                firstLine
            }
            else{
                println(s"Every time $firstLine")
                iter.next()
            }
        }
        else {
          iter.next()
        }
    }
}

-2

对于python开发人员。我已经测试了spark2.0。假设您要删除前14行。

sc = spark.sparkContext
lines = sc.textFile("s3://folder_location_of_csv/")
parts = lines.map(lambda l: l.split(","))
parts.zipWithIndex().filter(lambda tup: tup[1] > 14).map(lambda x:x[0])

withColumn是df函数。因此,下面将无法以上面使用的RDD样式工作。

parts.withColumn("index",monotonically_increasing_id()).filter(index > 14)

1
您好kartik,我想您的解决方案是处理单个文件,但问题有所不同。
哈菲兹·穆贾迪德

仅代码的第一部分是正确的。单调增加的id不能保证连续的数字。请如此和修改。
Alper t。Turker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.