【问题标题】:What is the most efficient way to return ranks of a vector within levels of a factor, as a vector having the same order/length as the original vector?作为与原始向量具有相同顺序/长度的向量,返回因子级别内向量等级的最有效方法是什么?
【发布时间】:2011-01-31 00:22:52
【问题描述】:

还有一个要求 - 结果向量与原始向量的顺序相同。

我有一个非常基本的函数,它可以对向量进行百分位计算,并且按照我想要的方式工作:

ptile <- function(x) {
  p <- (rank(x) - 1)/(length(which(!is.na(x))) - 1)
  p[p > 1] <- NA
  p 
}

data <- c(1, 2, 3, 100, 200, 300)

例如,ptile(data) 生成:

[1] 0.0 0.2 0.4 0.6 0.8 1.0

我真正想做的是使用相同的功能 (ptile) 并让它在一个因子的级别内工作。所以假设我有一个“因素” f 如下:

f <- as.factor(c("a", "a", "b", "a", "b", "b"))

我希望能够将“数据”转换为一个向量,该向量告诉我,对于每个观察,其对应的百分位数相对于同一级别内的其他观察是多少,如下所示:

0.0 0.5 0.0 1.0 0.5 1.0

作为在黑暗中的镜头,我尝试了:

tapply(data,f,ptile)

并看到它确实成功地进行了排名/百分比,但这样做的方式是我不知道哪些观察结果与其在原始向量中的索引相匹配:

[1] a a b a b b
Levels: a b
> tapply(data,f,ptile)
$a
[1] 0.0 0.5 1.0

$b
[1] 0.0 0.5 1.0

这很重要,因为我正在处理的实际数据可以有 1000-3000 个观察值(股票)和 10-55 个级别(例如行业、按其他股票特征分组等),我需要得到的向量是与它进入的顺序相同,以便在我的矩阵中逐行排列。

是否有一些“应用”变体可以满足我的要求?或者几行可以解决问题的快速行?我已经在 C# 和 F# 中使用更多代码行编写了此功能,但我认为在 R 中必须有一些非常直接、优雅的解决方案。有吗?

提前致谢!

【问题讨论】:

    标签: r


    【解决方案1】:

    ave 函数非常有用。主要问题是要记住,您始终需要使用 FUN= 命名函数:

     dt <- data.frame(data, f)
     dt$rank <-  with(dt, ave(data, list(f), FUN=rank))
         dt
        #---
          data f rank
        1    1 a    1
        2    2 a    2
        3    3 b    1
        4  100 a    3
        5  200 b    2
        6  300 b    3
    

    编辑:我以为我在回答标题中的问题,但被要求包含使用“ptile”功能的代码:

    > dt$ptile <-  with(dt, ave(data, list(f), FUN=ptile))
    > dt
      data f rank ptile
    1    1 a    1   0.0
    2    2 a    2   0.5
    3    3 b    1   0.0
    4  100 a    3   1.0
    5  200 b    2   0.5
    6  300 b    3   1.0
    

    【讨论】:

    • 确实很方便。您可能想添加他正在寻找的答案:ave(data, f, FUN = ptile)
    • 非常感谢 - 我需要一些时间来筛选所有这些好主意,因为我是学习 R 的新手。希望到今晚我能够对所有这些进行测试并分配积分!
    • 这是完美的 - 简洁,并且正是我所希望的。谢谢!
    【解决方案2】:

    对于您要执行的操作,我首先将股票、行业、价值作为列放在数据框中。例如,一些虚构的数据:

    > set.seed(1)
    > df <- data.frame(stock = 1:10,
    +                  sector = sample(letters[1:2], 10, repl = TRUE),
    +                  val = sample(1:10))
    > df
       stock sector val
    1      1      a   3
    2      2      a   2
    3      3      b   6
    4      4      b  10
    5      5      a   5
    6      6      b   7
    7      7      b   8
    8      8      b   4
    9      9      b   1
    10    10      a   9
    

    然后你可以使用plyr包中的ddply函数来做“扇区”百分位数(还有其他方法,但我发现plyr非常有用,建议你看看在它):

    require(plyr)
    df.p <- ddply(df, .(sector), transform, pct = ptile(val))
    

    当然在df.p中,行将按因子排列(即sector),将其恢复到原始顺序是一件简单的事情,例如:

    > df.p[ order(df.p$stock),]
       stock sector val       pct
    1      1      a   3 0.3333333
    2      2      a   2 0.0000000
    5      3      b   6 0.4000000
    6      4      b  10 1.0000000
    3      5      a   5 0.6666667
    7      6      b   7 0.6000000
    8      7      b   8 0.8000000
    9      8      b   4 0.2000000
    10     9      b   1 0.0000000
    4     10      a   9 1.0000000
    

    特别是 pct 列是您在原始问题中寻找的最终向量。

    【讨论】:

      【解决方案3】:

      当您使用INDEX=f 调用tapply() 时,您会得到一个由f 子集的结果,并按照f 的级别顺序分解为一个列表。要扭转这个过程,只需:

      unlist(tapply(data, f, ptile))[order(order(f))]
      

      您的示例 data 向量恰好已经按数字顺序排列,但即使数据是随机顺序的,这也有效...

      ptile <- function(x) {
        p <- (rank(x) - 1)/(length(which(!is.na(x))) - 1)
        p[p > 1] <- NA
        # concatenated with the original data to make the match clear
        paste(round(p * 100, 2), x, sep="% ") 
      }
      
      data <- sample(c(1:5, (1:5)*100), 10)
      f <- sample(letters[1:2], 10, replace=TRUE)
      result <- unlist(tapply(data, f, ptile))[order(order(f))]
      
      data.frame(result, data, f)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-15
        • 1970-01-01
        • 1970-01-01
        • 2018-05-12
        相关资源
        最近更新 更多