在一个因子的水平内返回一个向量的等级的最有效方法是什么,作为一个与原始向量具有相同顺序/长度的向量?

时间:2011-01-31 00:22:53

标签: r

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

我有一个非常基本的功能,可以对矢量进行百分位数,并按照我希望的方式工作:

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中必须有一些非常直接,优雅的解决方案。有吗?

提前致谢!

3 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分: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

然后,您可以使用ddply包中的plyr函数来执行“扇区”百分位数(还有其他方法,但我发现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列是您在原始问题中寻找的最终向量。

答案 2 :(得分:2)

当您使用tapply()致电INDEX=f时,您会得到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)