我有如下记录。如果第3个属性为EXTERNAL
,我想将单个记录转换为两个值INTERNAL
和All
的记录。
输入数据集:
Surender,cts,INTERNAL
Raja,cts,EXTERNAL
Ajay,tcs,All
预期产出:
Surender,cts,INTERNAL
Raja,cts,EXTERNAL
Ajay,tcs,INTERNAL
Ajay,tcs,EXTERNAL
我的火花代码:
case class Customer(name:String,organisation:String,campaign_type:String)
val custRDD = sc.textFile("/user/cloudera/input_files/customer.txt")
val mapRDD = custRDD.map(record => record.split(","))
.map(arr => (arr(0),arr(1),arr(2))
.map(tuple => {
val name = tuple._1.trim
val organisation = tuple._2.trim
val campaign_type = tuple._3.trim.toUpperCase
Customer(name, organisation, campaign_type)
})
mapRDD.toDF().registerTempTable("customer_processed")
sqlContext.sql("SELECT * FROM customer_processed").show
有人可以帮我解决这个问题吗?
答案 0 :(得分:3)
您可以使用udf
转换包含Seq
字符串的campaign_type列,以将其映射到广告系列类型,然后explode
:
val campaignType_ : (String => Seq[String]) = {
case s if s == "ALL" => Seq("EXTERNAL", "INTERNAL")
case s => Seq(s)
}
val campaignType = udf(campaignType_)
val df = Seq(("Surender", "cts", "INTERNAL"),
("Raja", "cts", "EXTERNAL"),
("Ajay", "tcs", "ALL"))
.toDF("name", "organisation", "campaign_type")
val step1 = df.withColumn("campaign_type", campaignType($"campaign_type"))
step1.show
// +--------+------------+--------------------+
// | name|organisation| campaign_type|
// +--------+------------+--------------------+
// |Surender| cts| [INTERNAL]|
// | Raja| cts| [EXTERNAL]|
// | Ajay| tcs|[EXTERNAL, INTERNAL]|
// +--------+------------+--------------------+
val step2 = step1.select($"name", $"organisation", explode($"campaign_type"))
step2.show
// +--------+------------+--------+
// | name|organisation| col|
// +--------+------------+--------+
// |Surender| cts|INTERNAL|
// | Raja| cts|EXTERNAL|
// | Ajay| tcs|EXTERNAL|
// | Ajay| tcs|INTERNAL|
// +--------+------------+--------+
修改强>
你实际上并不需要一个udf,你可以在step1
上使用when()。否则谓词如下:
val step1 = df.withColumn("campaign_type",
when(col("campaign_type") === "ALL", array("EXTERNAL", "INTERNAL")).otherwise(array(col("campaign_type")))
答案 1 :(得分:3)
因为它是斯卡拉......
如果你想编写一个更惯用的Scala代码(并且可能由于缺乏优化来交换一些性能以获得更惯用的代码),你可以使用flatMap运算符(删除隐式参数):
flatMap [U](func:(T)⇒TraversableOnce[U]):数据集[U] 首先将函数应用于此数据集的所有元素,然后展平,返回新的数据集结果。
注意:flatMap
相当于explode
功能,但您不必注册UDF(如另一个答案)。
解决方案如下:
// I don't care about the names of the columns since we use Scala
// as you did when you tried to write the code
scala> input.show
+--------+---+--------+
| _c0|_c1| _c2|
+--------+---+--------+
|Surender|cts|INTERNAL|
| Raja|cts|EXTERNAL|
| Ajay|tcs| All|
+--------+---+--------+
val result = input.
as[(String, String, String)].
flatMap { case r @ (name, org, campaign) =>
if ("all".equalsIgnoreCase(campaign)) {
Seq("INTERNAL", "EXTERNAL").map { cname =>
(name, org, cname)
}
} else Seq(r)
}
scala> result.show
+--------+---+--------+
| _1| _2| _3|
+--------+---+--------+
|Surender|cts|INTERNAL|
| Raja|cts|EXTERNAL|
| Ajay|tcs|INTERNAL|
| Ajay|tcs|EXTERNAL|
+--------+---+--------+
比较两个查询的性能,即基于flatMap
的基于explode
的查询,我认为基于explode
- 可能会稍快一点,并且更好地优化,因为一些代码在Spark& #39; s控制(在逻辑运算符映射到物理couterpart之前使用它们)。在flatMap
中,整个优化是您作为Scala开发人员的责任。
以下红色区域对应基于flatMap
的代码,警告标志是非常昂贵的DeserializeToObject
和SerializeFromObject
运营商。
有趣的是每个查询的Spark作业数量及其持续时间。基于explode
的查询似乎需要2个Spark作业和200 ms,而基于flatMap
的查询只需要1个Spark作业和43 ms。
让我感到惊讶,并建议基于flatMap
的查询可以更快(!)