在Spark数据帧中查找

时间:2016-12-22 03:55:20

标签: scala apache-spark apache-spark-sql user-defined-functions

我正在使用Spark 1.6,我想知道如何在数据帧中实现查找。

我有两个数据框员工&部。

  • 员工数据框

    -------------------
    Emp Id | Emp Name
    ------------------
    1 | john
    2 | David
    
  • 部门数据框

    --------------------
    Dept Id | Dept Name | Emp Id
    -----------------------------
    1 | Admin | 1
    2 | HR | 2
    

我想从employee表中查找emp id到department表并获取dept名称。所以,结果集将是

Emp Id | Dept Name
-------------------
1 | Admin
2 | HR

如何在SPARK中实现此查找UDF功能。我不想在两个数据帧上使用JOIN。

3 个答案:

答案 0 :(得分:6)

正如评论中已经提到的,加入数据帧是可行的方法。

您可以使用查找,但我认为没有“分布式”解决方案,即您必须将查找表收集到驱动程序内存中。另请注意,此方法假定EmpID是唯一的:

import org.apache.spark.sql.functions._
import sqlContext.implicits._
import scala.collection.Map

val emp = Seq((1,"John"),(2,"David"))
val deps = Seq((1,"Admin",1),(2,"HR",2))

val empRdd = sc.parallelize(emp)
val depsDF = sc.parallelize(deps).toDF("DepID","Name","EmpID")


val lookupMap = empRdd.collectAsMap()
def lookup(lookupMap:Map[Int,String]) = udf((empID:Int) => lookupMap.get(empID))

val combinedDF = depsDF
  .withColumn("empNames",lookup(lookupMap)($"EmpID"))

我最初的想法是将empRdd传递给UDF并使用lookup上定义的PairRDD方法,但这当然不起作用,因为你不能有火花动作(即转换中的lookup}(即UDF)。

编辑:

如果您的empDf有多个列(例如姓名,年龄),您可以使用此

val empRdd = empDf.rdd.map{row =>
      (row.getInt(0),(row.getString(1),row.getInt(2)))}


    val lookupMap = empRdd.collectAsMap()
    def lookup(lookupMap:Map[Int,(String,Int)]) =
         udf((empID:Int) => lookupMap.lift(empID))

    depsDF
      .withColumn("lookup",lookup(lookupMap)($"EmpID"))
      .withColumn("empName",$"lookup._1")
      .withColumn("empAge",$"lookup._2")
      .drop($"lookup")
      .show()

答案 1 :(得分:2)

正如您所说,您已经拥有Dataframes,然后很容易按照以下步骤操作:

1)创建一个sqlcontext

val sqlContext = new org.apache.spark.sql.SQLContext(sc)

2)为所有3例创建临时表:

EmployeeDataframe.createOrReplaceTempView("EmpTable")

3)使用MySQL查询查询

val MatchingDetails = sqlContext.sql("SELECT DISTINCT E.EmpID, DeptName FROM EmpTable E inner join DeptTable G on " +
  "E.EmpID=g.EmpID")

答案 2 :(得分:1)

从一些“查找”数据开始,有两种方法:

方法1-使用查找数据框

// use a DataFrame (via a join)
val lookupDF = sc.parallelize(Seq(
  ("banana",   "yellow"),
  ("apple",    "red"),
  ("grape",    "purple"),
  ("blueberry","blue")
)).toDF("SomeKeys","SomeValues")

方法2 –在UDF中使用地图

// turn the above DataFrame into a map which a UDF uses
val Keys = lookupDF.select("SomeKeys").collect().map(_(0).toString).toList
val Values = lookupDF.select("SomeValues").collect().map(_(0).toString).toList
val KeyValueMap = Keys.zip(Values).toMap

def ThingToColor(key: String): String = {
  if (key == null) return ""
  val firstword = key.split(" ")(0) // fragile!
  val result: String = KeyValueMap.getOrElse(firstword,"not found!")
  return (result)
}

val ThingToColorUDF = udf( ThingToColor(_: String): String )

对将要查找的内容进行采样的数据框:

val thingsDF = sc.parallelize(Seq(
  ("blueberry muffin"),
  ("grape nuts"),
  ("apple pie"),
  ("rutabaga pudding")
)).toDF("SomeThings")

方法1 将要加入到查询数据框

在这里, rlike 正在进行匹配。并且 null 出现在不起作用的地方。查找DataFrame的两列都被添加。

val result_1_DF = thingsDF.join(lookupDF, expr("SomeThings rlike SomeKeys"), 
                     "left_outer")

2 columns are added and nulls where applicable

方法2 是使用UDF添加列

在这里,仅添加1列。 UDF可以返回非null值。但是,如果查找数据很大,则可能无法按要求进行“序列化”以发送给集群中的工作程序。

val result_2_DF = thingsDF.withColumn("AddValues",ThingToColorUDF($"SomeThings"))

哪个给你:

1 column is added with values provided by the UDF

在我的情况下,我有一些超过100万个值的查找数据,因此方法1是我唯一的选择。