我有一个采用以下方案的数据:
sourceip
destinationip
packets sent
我想从这些数据中计算出几个聚合字段,并具有以下模式:
ip
packets sent as sourceip
packets sent as destination
在RDD的快乐日子里,我可以使用aggregate
,定义{ip-> []}的映射,并计算在相应数组位置中的出现次数。
在“数据集/数据框”聚合中不再可用,而是可以使用UDAF,不幸的是,根据我对UDAF的经验,它们是不可变的,意味着它们无法使用(必须在每次地图更新时创建一个新实例) example + explanation here
一方面,从技术上讲,我可以将数据集转换为RDD,进行聚合等,然后返回数据集。我希望这会导致性能下降,因为数据集更加优化。由于复制,UDAF毫无疑问。
还有其他执行聚合的方法吗?
答案 0 :(得分:3)
听起来您需要标准的melt
(How to melt Spark DataFrame?)和pivot
组合:
val df = Seq(
("192.168.1.102", "192.168.1.122", 10),
("192.168.1.122", "192.168.1.65", 10),
("192.168.1.102", "192.168.1.97", 10)
).toDF("sourceip", "destinationip", "packets sent")
df.melt(Seq("packets sent"), Seq("sourceip", "destinationip"), "type", "ip")
.groupBy("ip")
.pivot("type", Seq("sourceip", "destinationip"))
.sum("packets sent").na.fill(0).show
// +-------------+--------+-------------+
// | ip|sourceip|destinationip|
// +-------------+--------+-------------+
// | 192.168.1.65| 0| 10|
// |192.168.1.102| 20| 0|
// |192.168.1.122| 10| 10|
// | 192.168.1.97| 0| 10|
// +-------------+--------+-------------+
答案 1 :(得分:2)
一种无需任何自定义聚合的方法是使用flatMap
(或将explode
用于数据帧),如下所示:
case class Info(ip : String, sent : Int, received : Int)
case class Message(from : String, to : String, p : Int)
val ds = Seq(Message("ip1", "ip2", 5),
Message("ip2", "ip3", 7),
Message("ip2", "ip1", 1),
Message("ip3", "ip2", 3)).toDS()
ds
.flatMap(x => Seq(Info(x.from, x.p, 0), Info(x.to, 0, x.p)))
.groupBy("ip")
.agg(sum('sent) as "sent", sum('received) as "received")
.show
// +---+----+--------+
// | ip|sent|received|
// +---+----+--------+
// |ip2| 8| 8|
// |ip3| 3| 7|
// |ip1| 5| 1|
// +---+----+--------+
就性能而言,我不确定flatMap
与自定义聚合相比是否有所改进。
答案 2 :(得分:1)
这是使用explode
的pyspark版本。它比较冗长,但逻辑与flatMap
版本完全相同,仅使用纯数据帧代码。
sc\
.parallelize([("ip1", "ip2", 5), ("ip2", "ip3", 7), ("ip2", "ip1", 1), ("ip3", "ip2", 3)])\
.toDF(("from", "to", "p"))\
.select(F.explode(F.array(\
F.struct(F.col("from").alias("ip"),\
F.col("p").alias("received"),\
F.lit(0).cast("long").alias("sent")),\
F.struct(F.col("to").alias("ip"),\
F.lit(0).cast("long").alias("received"),\
F.col("p").alias("sent")))))\
.groupBy("col.ip")\
.agg(F.sum(F.col("col.received")).alias("received"), F.sum(F.col("col.sent")).alias("sent"))
// +---+----+--------+
// | ip|sent|received|
// +---+----+--------+
// |ip2| 8| 8|
// |ip3| 3| 7|
// |ip1| 5| 1|
// +---+----+--------+
答案 3 :(得分:-1)
由于您没有提及上下文和聚合,因此您可以执行以下操作,
val df = ??? // your dataframe/ dataset
从Spark来源:
(特定于标度的)通过从列中指定映射来计算聚合 聚合方法的名称。结果数据框还将包含 分组列。可用的汇总方法为avg,max, 最小,总和,计数。
//选择最老员工的年龄和总费用 对于每个部门
df .groupBy("department") .agg(Map( "age" -> "max", "expense" -> "sum" ))