Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 使用布尔值标记spark数据帧中的错误行_Scala_Hadoop_Apache Spark_Dataframe_Spark Dataframe - Fatal编程技术网

Scala 使用布尔值标记spark数据帧中的错误行

Scala 使用布尔值标记spark数据帧中的错误行,scala,hadoop,apache-spark,dataframe,spark-dataframe,Scala,Hadoop,Apache Spark,Dataframe,Spark Dataframe,我在试着用一只手去拿spark数据框。利用来自级联框架的先前知识,该框架具有一个陷阱机制,可以将错误行(具有空值的行)过滤到一个称为陷阱的单独抽头中。那些不知道的人让我说清楚。当您从文本文件中读取错误的行时。框架要么从整个数据中删除坏行,要么停止执行。现在在ApacheSpark中,我注意到坏行并没有妨碍执行。这很好,但当涉及到从数据中获取业务见解时,数据的质量确实很重要 因此,我有一个文本文件,其中有一堆行(您可以选择任何数据集,您喜欢),其中很少有记录包含空值。现在,我将文本文件加载到带有

我在试着用一只手去拿spark数据框。利用来自级联框架的先前知识,该框架具有一个陷阱机制,可以将错误行(具有空值的行)过滤到一个称为陷阱的单独抽头中。那些不知道的人让我说清楚。当您从文本文件中读取错误的行时。框架要么从整个数据中删除坏行,要么停止执行。现在在ApacheSpark中,我注意到坏行并没有妨碍执行。这很好,但当涉及到从数据中获取业务见解时,数据的质量确实很重要

因此,我有一个文本文件,其中有一堆行(您可以选择任何数据集,您喜欢),其中很少有记录包含空值。现在,我将文本文件加载到带有spark.read.csv的数据框中。现在,我要做的是分析数据帧并动态创建一个名为“isMyRowBad”的列,其中逻辑将一次分析每一行,如果逻辑发现有空值的行,它会将该特定行上的isMyRowBad列标记为true,并将没有空值的列标记为,对于干净的purticular行,相应的列isMyRowBad应该有false
为您提供传入和传出数据集的概述
传入数据帧

fname,lname,age
will,smith,40
Dwayne,Nunn,36
Aniruddha,Sinha,
Maria,,22
fname,lname,age,isMyRowBad
 will,smith,40,false
 Dwayne,Nunn,36,false
 Aniruddha,Sinha,,true
 Maria,,22,true
传出数据帧

fname,lname,age
will,smith,40
Dwayne,Nunn,36
Aniruddha,Sinha,
Maria,,22
fname,lname,age,isMyRowBad
 will,smith,40,false
 Dwayne,Nunn,36,false
 Aniruddha,Sinha,,true
 Maria,,22,true
上面对好行和坏行进行分类的方法可能看起来有点愚蠢,但它确实有意义,因为我不需要多次运行筛选操作。让我们来看看,怎么样

假设我有一个名为inDf的数据帧作为inputDf,而AnalysedDf:(Dataframe,Dataframe)作为输出Df元组

现在,我尝试了这部分代码

val analyzedDf: (DataFrame, DataFrame) = (inputDf.filter(_.anyNull),inputDf.filter(!_.anyNull))
此代码分隔好行和坏行。我同意!但这会降低性能,因为过滤器运行两次,这意味着过滤器将在整个数据集上迭代两次!(如果您认为在考虑50个字段和至少584000行(即250 mb的数据)时,运行两次筛选器是有意义的,那么您可以反驳这一点!)

还有这个

val analyzedDf: DataFrame = inputDf.select("*").withColumn("isMyRowBad", <this point, I am not able to analyze row>
输入

fname,lname,age,pan,married
aniruddha,sinha,23,0AA22,no
balajee,venkatesh,23,0b96,no
warren,shannon,72,,
wes,borland,63,0b22,yes
Rohan,,32,0a96,no
james,bond,66,007,no
+---------+---------+---+-----+-------+--------+
|    fname|    lname|age|  pan|married|isRowBad|
+---------+---------+---+-----+-------+--------+
|aniruddha|    sinha| 23|0AA22|     no|   false|
|  balajee|venkatesh| 23| 0b96|     no|   false|
|   warren|  shannon| 72| null|   null|    true|
|      wes|  borland| 63| 0b22|    yes|   false|
|    Rohan|     null| 32| 0a96|     no|    true|
|    james|     bond| 66|  007|     no|   false|
+---------+---------+---+-----+-------+--------+
输出

fname,lname,age,pan,married
aniruddha,sinha,23,0AA22,no
balajee,venkatesh,23,0b96,no
warren,shannon,72,,
wes,borland,63,0b22,yes
Rohan,,32,0a96,no
james,bond,66,007,no
+---------+---------+---+-----+-------+--------+
|    fname|    lname|age|  pan|married|isRowBad|
+---------+---------+---+-----+-------+--------+
|aniruddha|    sinha| 23|0AA22|     no|   false|
|  balajee|venkatesh| 23| 0b96|     no|   false|
|   warren|  shannon| 72| null|   null|    true|
|      wes|  borland| 63| 0b22|    yes|   false|
|    Rohan|     null| 32| 0a96|     no|    true|
|    james|     bond| 66|  007|     no|   false|
+---------+---------+---+-----+-------+--------+

代码运行良好,但我对when函数有一个问题。我们不能选择所有列而不进行硬编码吗?

据我所知,您无法使用内置的csv解析器进行此操作。如果解析器遇到错误(failFast模式),您可以让它停止,但不能进行注释

但是,您可以使用定制的csv解析器来实现这一点,该解析器可以在一次传递中处理数据。除非我们想做一些聪明的类型内省,否则创建一个助手类来注释文件的结构是最简单的:

case class CSVColumnDef(colPos: Int, colName: String, colType: String)

val columns = List(CSVColumnDef(0,"fname","String"),CSVColumnDef(1,"lname","String"),CSVColumnDef(2,"age", "Int"))
接下来,我们需要一些函数来a)拆分输入,b)从拆分数据中提取数据,c)检查行是否错误:

import scala.util.Try
def splitToSeq(delimiter: String) = udf[Seq[String],String](_.split(delimiter))

def extractColumnStr(i: Int) = udf[Option[String],Seq[String]](s => Try(Some(s(i))).getOrElse(None))
def extractColumnInt(i: Int) = udf[Option[Int],Seq[String]](s => Try(Some(s(i).toInt)).getOrElse(None))

def isRowBad(delimiter: String) = udf[Boolean,String](s => {
   (s.split(delimiter).length != columns.length) || (s.split(delimiter).exists(_.length==0))
})
要使用这些,我们首先需要读取文本文件(因为我没有文本文件,为了允许人们复制这个答案,我将创建一个rdd):

有了这个输入,我们可以创建一个带有单列的数据框,即原始行,并将拆分列添加到其中:

val delimiter = ","
val raw = "raw"
val delimited = "delimited"
val compDF = input.toDF(raw).withColumn(delimited, splitToSeq(delimiter)(col(raw)))
最后,我们可以提取前面定义的所有列,并检查行是否错误:

val df = columns.foldLeft(compDF){case (acc,column) => column.colType match {
   case "Int" => acc.withColumn(column.colName, extractColumnInt(column.colPos)(col(delimited)))
   case _ => acc.withColumn(column.colName, extractColumnStr(column.colPos)(col(delimited)))
}}.
withColumn("isMyRowBad", isRowBad(delimiter)(col(raw))).
drop(raw).drop(delimited)

df.show
df.printSchema

此解决方案的好处在于spark execution planner足够智能,可以将所有
.withColumn
操作构建到数据上的单个过程(
map
)中,而无需进行零洗牌。令人恼火的是,与使用漂亮的csv库相比,开发人员的工作要多得多,我们需要以某种方式定义列。如果您想更聪明一点,可以从文件的第一行获取列名(提示:
.mapPartitionsWithIndex
),然后将所有内容解析为字符串。我们也不能定义一个case类来描述整个DF,因为您有太多的列,无法以这种方式处理解决方案。希望这有帮助…

据我所知,您无法使用内置的csv解析器来完成这项工作。如果解析器遇到错误(failFast模式),您可以让它停止,但不能进行注释

但是,您可以使用定制的csv解析器来实现这一点,该解析器可以在一次传递中处理数据。除非我们想做一些聪明的类型内省,否则创建一个助手类来注释文件的结构是最简单的:

case class CSVColumnDef(colPos: Int, colName: String, colType: String)

val columns = List(CSVColumnDef(0,"fname","String"),CSVColumnDef(1,"lname","String"),CSVColumnDef(2,"age", "Int"))
接下来,我们需要一些函数来a)拆分输入,b)从拆分数据中提取数据,c)检查行是否错误:

import scala.util.Try
def splitToSeq(delimiter: String) = udf[Seq[String],String](_.split(delimiter))

def extractColumnStr(i: Int) = udf[Option[String],Seq[String]](s => Try(Some(s(i))).getOrElse(None))
def extractColumnInt(i: Int) = udf[Option[Int],Seq[String]](s => Try(Some(s(i).toInt)).getOrElse(None))

def isRowBad(delimiter: String) = udf[Boolean,String](s => {
   (s.split(delimiter).length != columns.length) || (s.split(delimiter).exists(_.length==0))
})
要使用这些,我们首先需要读取文本文件(因为我没有文本文件,为了允许人们复制这个答案,我将创建一个rdd):

有了这个输入,我们可以创建一个带有单列的数据框,即原始行,并将拆分列添加到其中:

val delimiter = ","
val raw = "raw"
val delimited = "delimited"
val compDF = input.toDF(raw).withColumn(delimited, splitToSeq(delimiter)(col(raw)))
最后,我们可以提取前面定义的所有列,并检查行是否错误:

val df = columns.foldLeft(compDF){case (acc,column) => column.colType match {
   case "Int" => acc.withColumn(column.colName, extractColumnInt(column.colPos)(col(delimited)))
   case _ => acc.withColumn(column.colName, extractColumnStr(column.colPos)(col(delimited)))
}}.
withColumn("isMyRowBad", isRowBad(delimiter)(col(raw))).
drop(raw).drop(delimited)

df.show
df.printSchema

此解决方案的好处在于spark execution planner足够智能,可以将所有
.withColumn
操作构建到数据上的单个过程(
map
)中,而无需进行零洗牌。令人恼火的是,与使用漂亮的csv库相比,开发人员的工作要多得多,我们需要以某种方式定义列。如果您想更聪明一点,可以从文件的第一行获取列名(提示:
.mapPartitionsWithIndex
),然后将所有内容解析为字符串。我们也不能定义一个case类来描述整个DF,因为您有太多的列,无法以这种方式处理解决方案。希望这有帮助…

这可以使用udf完成。虽然Ben Horsburgh给出的答案肯定非常出色,但我们可以做到这一点,而无需深入了解数据帧背后的内部架构
下面的代码可以给您一个想法

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.{StringType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

/**
  * Created by vaijnath on 10/4/17.
  */
object DataQualityCheck extends App {
  val spark = SparkSession.builder().master("local[*]").getOrCreate()
  import spark.implicits._

  val schema: StructType = StructType(List(
    StructField("fname", StringType, nullable = true),
    StructField("lname", StringType, nullable = true),
    StructField("married", StringType, nullable = true)
  ))

  val inputDataFrame: DataFrame = spark
    .read
    .schema(schema)
    .option("header",false)
    .option("delimiter",",")
    .csv("hydrograph.engine.spark/testData/inputFiles/delimitedInputFile.txt")

  //inputDataFrame.show()

  def isBad(row:Row):Boolean={
    row.anyNull
  }
  val simplefun=udf(isBad(_:Row))
  val cols=struct(inputDataFrame.schema.fieldNames.map(e=> col(e)):_*)
  //      println(cols+"******************") //for debugging
  val analysedDataFrame: DataFrame = inputDataFrame.withColumn("isRowBad", simplefun(cols))

  analysedDataFrame.show
}
如果你遇到任何问题,请给我回电话。我相信这个解决方案是合适的,因为您似乎在寻找使用dataframe的代码


谢谢。

这可以使用udf完成。虽然Ben Horsburgh给出的答案