如何按数量排序并保留价值中的唯一项目

How to sort by count and retain unique items in value

我有一个包含 2 列的数据框,形式为

col1    col2

k1      'a'
k2      'b'
k1      'a'
k1      'c'
k2      'c'
k1      'b' 
k1      'b'
k2      'c'
k1      'b'

我希望输出为

k1    ['b', 'a', 'c']
k2    ['c', 'b']

因此唯一的一组条目,按每个条目出现的次数排序(降序)。在上面的示例中,'b' 与 k1 相关联三次,'a' 两次,'c' 一次。

我该怎么做?

groupBy($"col1").count()

只查看 col1 中的条目出现的次数,但这不是我要找的。

您可以执行以下操作:

  1. 对于每个键和列值,计算计数
  2. 对于每个键,计算一个包含所有相关列值及其计数的列表
  3. 使用 udf 对列表进行排序并删除计数

就像那样(在 Scala 中):

import scala.collection.mutable
import org.apache.spark.sql.{Row}

val sort_by_count_udf = udf {
  arr: mutable.WrappedArray[Row] =>
    arr.map {
      case Row(count: Long, col2: String) => (count, col2)
    }.sortBy(-_._1).map { case (count, col2) => col2 }
}

val df = List(("k1", "a"), 
  ("k1", "a"), ("k1", "c"), ("k1", "b"), 
  ("k2", "b"), ("k2", "c"), ("k2", "c"), 
  ("k1", "b"), ("k1", "b"))
  .toDF("col1", "col2")

val grouped = df
  .groupBy("col1", "col2")
  .count()
  .groupBy("col1")
  .agg(collect_list(struct("count", "col2")).as("list"))

grouped.withColumn("list_ordered", sort_by_count_udf(col("list"))).show

这是一个仅使用内置函数的(不太漂亮的解决方案):

df.groupBy($"col1" , $"col2")
  .agg(count($"col2").alias("cnt") )
  .groupBy($"col1")
  .agg(sort_array(collect_list(struct(-$"cnt", $"col2"))).as("list"))
  .withColumn("x" , $"list".getItem("col2") )
  .show(false)

由于 sort_array 根据自然顺序对元素进行升序排序 -$"cnt" 有助于我们根据元素的数量对元素进行降序排序。 getItem 用于从结构中获取 col2 的值。

输出:

+----+------------------------+---------+
|col1|list                    |x        |
+----+------------------------+---------+
|k2  |[[-2,c], [-1,b]]        |[c, b]   |
|k1  |[[-3,b], [-2,a], [-1,c]]|[b, a, c]|
+----+------------------------+---------+