如何使用Scala使用比较器对数据帧进行排序?

如何使用Scala使用比较器对数据帧进行排序?,scala,sorting,apache-spark,apache-spark-sql,Scala,Sorting,Apache Spark,Apache Spark Sql,我想用我自己的比较器根据列对数据帧进行排序。是否可以在Spark SQL中执行此操作 例如,假设有一个数据帧注册为表“MyTable”,其中有一列“Day”,其类型为“string”: 我想执行这个查询: SELECT * FROM MyTable ORDER BY Day 我想用我自己的比较仪订购“Day”专栏。我曾想过使用UDF,但我不知道这是否可行。请注意,我确实希望在排序/排序操作中使用比较器。我不想将字符串从列Day转换为Datetime或类似的格式。这是使用dataframe进行转

我想用我自己的比较器根据列对数据帧进行排序。是否可以在Spark SQL中执行此操作

例如,假设有一个数据帧注册为表“MyTable”,其中有一列“Day”,其类型为“string”:

我想执行这个查询:

SELECT * FROM MyTable ORDER BY Day

我想用我自己的比较仪订购“Day”专栏。我曾想过使用UDF,但我不知道这是否可行。请注意,我确实希望在排序/排序操作中使用比较器。我不想将字符串从列Day转换为Datetime或类似的格式。

这是使用dataframe进行转换的一般方法

val df = spark.sql("SELECT * FROM MyTable")

df.orderby("yourcolumn")


如果您的数据较少(似乎只有周名)则可以收集为列表并使用scala sortWith函数

sortWith
函数根据比较对该序列进行排序 功能。它需要一个比较器函数并根据它进行排序 可以提供您自己的自定义比较功能

与您不同的示例:

scala> case class Emp(id: Int, name: String, salary: Double)
defined class Emp

scala> val emp1 = Emp(1, "james", 13000.00)
emp1: Emp = Emp(1,james,13000.0)

scala> val emp2 = Emp(2, "michael", 12000.00)
emp2: Emp = Emp(2,michael,12000.0)

scala> val emp3 = Emp(3, "Ram", 15000.00)
emp3: Emp = Emp(3,Ram,15000.0)

scala> val empList = List(emp1,emp2,emp3)
empList: List[Emp] = List(Emp(1,james,13000.0), Emp(2,michael,12000.0), Emp(3,Ram,15000.0))

// sort in descending order on the basis of salary.
scala> empList.sortWith(_.salary > _.salary)
其他选择包括:
要使用此选项,您需要将数据帧转换为PairedRDD,然后使用此处给出的答案进行排序。在SparkSQL中,您没有选择权,需要对一个或多个列使用排序比。有了RDD,如果您愿意,可以使用定制的类似java的比较器。实际上,这是
RDD
()的
排序方法的签名:

这意味着您可以提供自己选择的
排序
,它与java
比较器
完全相同(
排序
实际上继承自
比较器

为简单起见,假设我想按列“x”的绝对值排序(这可以在没有比较器的情况下完成,但假设我需要使用比较器)。我首先定义行上的比较器:

class RowOrdering extends Ordering[Row] {
    def compare(x : Row, y : Row): Int = x.getAs[Int]("x").abs - y.getAs[Int]("x").abs
}
现在让我们定义数据并对其进行排序:

val df = Seq( (0, 1),(1, 2),(2, 4),(3, 7),(4, 1),(5, -1),(6, -2),
    (7, 5),(8, 5), (9, 0), (10, -9)).toDF("id", "x")
val rdd = df.rdd.sortBy(identity)(new RowOrdering(), scala.reflect.classTag[Row])
val sorted_df = spark.createDataFrame(rdd, df.schema)
sorted_df.show
+---+---+
| id|  x|
+---+---+
|  9|  0|
|  0|  1|
|  4|  1|
|  5| -1|
|  6| -2|
|  1|  2|
|  2|  4|
|  7|  5|
|  8|  5|
|  3|  7|
| 10| -9|
+---+---+
另一种解决方案是定义隐式排序,以便在排序时不需要提供它

implicit val ord = new RowOrdering()
df.rdd.sortBy(identity)
最后,请注意,
df.rdd.sortBy(u.getAs[Int](“x”).abs)
将获得相同的结果。此外,还可以使用元组排序来执行更复杂的操作,例如按绝对值排序,如果相等,则将正值放在第一位:

df.rdd.sortBy(x => (x.getAs[Int]("x").abs, - x.getAs[Int]("x"))) //RDD
df.orderBy(abs($"x"), - $"x") //dataframe

在spark中,使用通用java比较器进行排序是不可能的。您需要定义一个排序键(排序键的类型是s.a.long、string、date…),并对数据集使用orderBy,对rdd使用sortBy。如果你告诉我们你的具体逻辑,也许我们可以考虑一个适合你的解决方案。谢谢你的回答。我必须做两件事:1)像本例中那样使用SQL查询;2) 当我必须按执行订单时,使用运算符对某列进行排序。一旦无法在UDF中使用通用比较器,您是否向我推荐其他选项?我考虑使用Spark规则/策略将DF转换为RDD,并将SortBy与我的比较器一起使用。但我不知道该怎么做。事实上,我错了。可以将比较器(scala中的排序)与RDD一起使用。我添加了一个解决方案来解释如何做。我还谈到了其他选择。谢谢你的回答!这有助于我更好地理解这些概念。正如我上面所说的,我需要执行SQL查询,就像我给出的示例一样。您知道是否有任何方法可以使用Spark规则/Spark策略来转换Spark计划、将DataFrame转换为中的DataSet/RDD以及使用比较器?在我看来,这是唯一可能的解决方案。使用纯sql是不可能的。如果我还想对GroupBy操作使用比较器,那么过程基本上是一样的,对吗?将数据帧转换为RDD并使用:groupBy[K](f:(T)⇒ K、 p:Partitioner)(隐式kt:ClassTag[K],order:Ordering[K]=null):RDD[(K,Iterable[T])从未尝试过它,但给出了签名,它似乎是可能的。不过,您需要定义一个兼容的分区器,即始终在同一分区中放置相等的键的分区器。我尝试过,您可以让它工作,但它不会对不真正相等的键进行分组,即使您的排序表明它们是相等的,并且使用一致的分区器。不过,您的密钥将正确排序。根据您的用例,这可能就足够了。我通过示例发布了一个新的for Group。它可以帮助其他开发人员。谢谢你的帮助!
implicit val ord = new RowOrdering()
df.rdd.sortBy(identity)
df.rdd.sortBy(x => (x.getAs[Int]("x").abs, - x.getAs[Int]("x"))) //RDD
df.orderBy(abs($"x"), - $"x") //dataframe