Scala 在Spark中的groupby之后跨列收集最常出现的唯一值
我有以下数据帧Scala 在Spark中的groupby之后跨列收集最常出现的唯一值,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我有以下数据帧 val input=Seq((“ZZ”、“a”、“a”、“b”、“b”), (“ZZ”、“a”、“b”、“c”、“d”), (“YY”,“b”,“e”,空,“f”), (“YY”,“b”,“b”,空,“f”), (“XX”,“j”,“i”,“h”,空)) .toDF(“主要”、“价值1”、“价值2”、“价值3”、“价值4”) input.show() +----+------+------+------+------+ |主要|值1 |值2 |值3 |值4| +----+----
val input=Seq((“ZZ”、“a”、“a”、“b”、“b”),
(“ZZ”、“a”、“b”、“c”、“d”),
(“YY”,“b”,“e”,空,“f”),
(“YY”,“b”,“b”,空,“f”),
(“XX”,“j”,“i”,“h”,空))
.toDF(“主要”、“价值1”、“价值2”、“价值3”、“价值4”)
input.show()
+----+------+------+------+------+
|主要|值1 |值2 |值3 |值4|
+----+------+------+------+------+
|ZZ | a | a | b | b|
|ZZ | a | b | c | d|
|YY | b | e | null | f|
|YY | b | b | null | f|
|XX | j | i | h |空|
+----+------+------+------+------+
我需要按main
列进行分组,并从其余列中为每个main
值选择两个最常出现的值
我做了以下几件事
val newdf = input.select('main,array('value1,'value2,'value3,'value4).alias("values"))
val newdf2 = newdf.groupBy('main).agg(collect_set('values).alias("values"))
val newdf3 = newdf2.select('main, flatten($"values").alias("values"))
以下面的形式获取数据
+----+--------------------+
|main| values|
+----+--------------------+
| ZZ|[a, a, b, b, a, b...|
| YY|[b, e,, f, b, b,, f]|
| XX| [j, i, h,]|
+----+--------------------+
现在,我需要从列表中选择出现次数最多的两项作为两列。不知道怎么做
因此,在这种情况下,预期输出应该是
+----+------+------+
|main|value1|value2|
+----+------+------+
| ZZ| a| b|
| YY| b| f|
| XX| j| i|
+----+------+------+
null
不应计数,只有在没有其他值可填充时,最终值才应为null
这是做事情的最好方式吗?有更好的方法吗?您可以使用从数组中选择最常出现的两个值
input.withColumn(“值”、数组(“value1”、“value2”、“value3”、“value4”))
.groupBy(“主”).agg(展平(收集列表(“值”)).as(“值”))
.withColumn(“max”,maxUdf(“值))/(1)
.cache()/(2)
.withColumn(“value1”,“max.getItem(0))
.withColumn(“值2”,“最大获取项(1))
.drop(“值”、“最大值”)
.show(假)
将maxUdf
定义为
def getMax[T](array: Seq[T]) = {
array
.filter(_ != null) //remove null values
.groupBy(identity).mapValues(_.length) //count occurences of each value
.toSeq.sortWith(_._2 > _._2) //sort (3)
.map(_._1).take(2) //return the two (or one) most common values
}
val maxUdf = udf(getMax[String] _)
备注:
main
的所有条目的整个数组必须装入一个Spark executor的内存中缓存
,否则将调用udf两次,一次用于value1
,一次用于value2
sortWith
是,但是如果两个元素的出现次数相同,则可能需要添加一些额外的逻辑来处理这种情况(例如主值XX
的i
、j
和h
)这是我没有udf的尝试
import org.apache.spark.sql.expressions.Window
val w = Window.partitionBy('main).orderBy('count.desc)
newdf3.withColumn("values", explode('values))
.groupBy('main, 'values).agg(count('values).as("count"))
.filter("values is not null")
.withColumn("target", concat(lit("value"), lit(row_number().over(w))))
.filter("target < 'value3'")
.groupBy('main).pivot('target).agg(first('values)).show
+----+------+------+
|main|value1|value2|
+----+------+------+
| ZZ| a| b|
| YY| b| f|
| XX| j| null|
+----+------+------+
import org.apache.spark.sql.expressions.Window
val w=Window.partitionBy('main.).orderBy('count.desc)
newdf3.withColumn(“值”,explode(“值))
.groupBy('main',value.).agg(count('value.).as('count'))
.filter(“值不为空”)
.withColumn(“target”,concat(lit(“value”),lit(row_number(),over(w)))
.filter(“目标<'value3'))
.groupBy('main).pivot('target).agg(first('values)).show
+----+------+------+
|主|值1 |值2|
+----+------+------+
|ZZ | a | b|
|YY | b | f|
|XX | j |空|
+----+------+------+
最后一行的值为空,因为我以这种方式修改了您的数据帧
+----+--------------------+
|main| values|
+----+--------------------+
| ZZ|[a, a, b, b, a, b...|
| YY|[b, e,, f, b, b,, f]|
| XX| [j,,,]| <- For null test
+----+--------------------+
+----+--------------------+
|主值|
+----+--------------------+
|ZZ |[a,a,b,b,a,b|
|YY |[b,e,f,b,b,f]|
|XX |[j,,,]|