如何在 Deedle 中执行 "ranking"(如 SAS 或 "dense rank",如 SQL)

How to perform "ranking" (as in SAS or a "dense rank" as in SQL) in Deedle

Sas 有一个名为 rank 的过程,它根据变量的有序集合中的位置将 "rank" 分配给数据帧中的每一行,有点;但排名不仅仅是位置:必须告诉程序有多少组在排名中使用。等级实际上是该行所属的组。

在 SQL 术语中,这称为 dense ranking

示例(包含salary变量是为了通用,但在本示例中未使用):

假设我们有这个数据框:

如果我们使用 4 个组按年龄排名,sas 会给我们这个:

如果我们按照排名的变量对数据进行排序,就更容易理解发生了什么:

现在我们可以明白为什么排名给了我们在有序集合中的位置,有点像

排名过程非常有用和酷,但我在 Deedle 的文档中找不到如何执行它。在 Deedle 中有直接的方法吗?还是我需要创建自己的扩展?

我想我可以使用这些函数来完成:

SortRows(frame, key)
chunk size series

我写了自己的扩展:

type Frame<'TRowKey, 'TColumnKey
        when 'TRowKey : equality
        and 'TColumnKey : equality> with
    static member denseRank column (groups:int) rankName frame =
        let frameLength = Frame.countRows frame |> float
        let chunkSize =  frameLength / (float groups) |> Math.Ceiling |> int64

        let sorted =
            frame
            |> Frame.sortRows column

        let ranks =
            Frame.getCol column frame
            |> Series.map(fun k _ ->
                int ((Frame.indexForKey k sorted) / chunkSize)
            )

        let clone = frame.Clone()
        clone.AddColumn(rankName, ranks)
        clone

其中 indexForKey 是另一个自定义扩展:

    // index for row with key
    // index starting at 0
    static member indexForKey (key:'K) (frame:Frame<'K,_>) : int64 =
        frame.RowIndex.Locate key
        |> frame.RowIndex.AddressOperations.OffsetOf

我尝试了这个其他定义,希望它 运行 更快。它稍微快一点,但不是很多;欢迎对性能问题提出任何意见:

static member denseRank column (groups:int) rankName frame =
        let frameLength = Frame.countRows frame
        let chunkSize =  (float frameLength) / (float groups) |> Math.Ceiling

        let sorted =
            frame
            |> Frame.sortRows column

        let sortedKeys = Frame.getRowKeys sorted

        let ranksArr = Array.zeroCreate frameLength

        sortedKeys
        |> Seq.iteri (fun index _ -> ranksArr.[index] <- index / (int chunkSize))

        let ranks = Series(sortedKeys, ranksArr)
        let clone = frame.Clone()
        clone.AddColumn(rankName, ranks)
        clone