我正在使用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。
答案 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 是使用UDF添加列
在这里,仅添加1列。 UDF可以返回非null值。但是,如果查找数据很大,则可能无法按要求进行“序列化”以发送给集群中的工作程序。
val result_2_DF = thingsDF.withColumn("AddValues",ThingToColorUDF($"SomeThings"))
哪个给你:
在我的情况下,我有一些超过100万个值的查找数据,因此方法1是我唯一的选择。