我有一些数据,看起来像下面这样。它按变量“年份”分组,并且我想提取得分的每个观察值相对于它所属的年份的百分位数,最好作为一个向量。
Year Score
2001 89
2001 70
2001 72
2001 ...
..........
2004 87
2004 90
我该怎么做?使用聚合函数不起作用,我也认为应用函数也不行。
我有一些数据,看起来像下面这样。它按变量“年份”分组,并且我想提取得分的每个观察值相对于它所属的年份的百分位数,最好作为一个向量。
Year Score
2001 89
2001 70
2001 72
2001 ...
..........
2004 87
2004 90
我该怎么做?使用聚合函数不起作用,我也认为应用函数也不行。
在Vince的解决方案之后,你也可以使用plyr
或by
来完成此操作:
ddply(df, .(years), function(x) transform(x, percentile=ecdf(x$scores)(x$scores)))
ave
ave(d1$scores, d1$year, FUN=function(x) ecdf(x)(x))
使用data.table也很直观。只是为了完整性并且作为查找data.table解决方案的简单方式。
library(data.table)
year <- rep(2001:2005, 2)
score <- round(rnorm(10, 35, 3))
dt <- data.table(year, score)
dt[, .(Percentile = ecdf(score)(score)), by = list(year)]
> years = c(2006, 2006, 2006, 2006, 2001, 2001, 2001, 2001, 2001)
> scores = c(13, 65, 23, 34, 78, 56, 89, 98, 100)
> tapply(scores, years, quantile)
$`2001`
0% 25% 50% 75% 100%
56 78 89 98 100
$`2006`
0% 25% 50% 75% 100%
13.00 20.50 28.50 41.75 65.00
这正确吗?
我是指每个观察值的实际百分位数。- Ryan Rosario
编辑:
我认为这可能就可以了:
> tapply(scores, years, function(x) { f = ecdf(x); sapply(x, f) })
$`2001`
[1] 0.4 0.2 0.6 0.8 1.0
$`2006`
[1] 0.25 1.00 0.50 0.75
使用您的数据:
> tapply(scores, years, function(x) { f = ecdf(x); sapply(x, f) })
$`2000`
[1] 0.3333333 0.6666667 1.0000000
$`2008`
[1] 0.5 1.0
编辑2:
这可能更快:
tapply(scores, years, function(x) { f = ecdf(x); f(x) })
f()
是向量化的 :-)
最后,修改一下,我保证 :-). 如果你想要名字:
> tapply(scores, years, function(x) { f = ecdf(x); r = f(x); names(r) <- x; r })
$`2000`
1000 1700 2000
0.3333333 0.6666667 1.0000000
$`2008`
1500 2000
0.5 1.0
# first I'll create two dummy variables (Year, Score)
year <- rep(2001:2005, 2)
score <- round(rnorm(10, 35, 3))
# then coerce variables to data frame
d <- data.frame(year, score)
# then you can use split() function to apply
# function to each stratum of grouping variable
sapply(split(score, year), function(x) quantile(x, probs=seq(.1, .9, .1)))
输出将会是这样的:
2001 2002 2003 2004 2005
10% 34.3 32.1 34.3 29.6 36.1
20% 34.6 32.2 34.6 30.2 36.2
30% 34.9 32.3 34.9 30.8 36.3
40% 35.2 32.4 35.2 31.4 36.4
50% 35.5 32.5 35.5 32.0 36.5
60% 35.8 32.6 35.8 32.6 36.6
70% 36.1 32.7 36.1 33.2 36.7
80% 36.4 32.8 36.4 33.8 36.8
90% 36.7 32.9 36.7 34.4 36.9
如果您喜欢的话,可以使用t()函数来转置行和列。编写一个函数是解决这类问题的好方法。我强烈推荐Hadley Wickam编写的plyr包。
希望这能帮到您!祝一切顺利!
我找到了一个方法,但需要使用循环。
group.pctiles <- function(group.var, comparable) {
unique.vals <- unique(group.var)
pctiles <- vector(length = length(group.var))
for (i in 1:length(unique.vals)) {
slice <- which(group.var == unique.vals[i])
F <- ecdf(comparable[slice])
group.pctiles <- F(comparable[slice])
pctiles[slice] <- group.pctiles
}
return(pctiles)
}
group.var是用来分组数据的变量。在我提出问题的例子中,它是年份。comparable则包含我们想要找到百分位数的值。在我的问题中,comparable应该是分数。
对于以下数据,我得到了下面的结果:
Year,School,Fees
2000,10,1000
2008,1,1050
2008,4,2000
2000,3,1700
2000,1,2000
> group.pctiles(dat, dat$Year, dat$Fees)
[1] 0.3333333 0.5000000 1.0000000 0.6666667 1.0000000
然后,我可以将这些百分位数与原始数据框进行列绑定,以进行分析、报告等。
有没有不需要循环的解决方案?
可以考虑这样做:
Year <- c(2000,2008,2008,2000,2000)
Fees <- c(1000,1050,2000,1700,2000)
dat <- data.frame(Fees,Year,result=NA)
res <- tapply(Fees,Year,function(x) rank(x,ties.method="max")/length(x))
for(i in 1:length(res))
dat[Year==as.numeric(names(res)[i]),"result"] <-res[[i]]
这将产生:
Fees Year result
1 1000 2000 0.3333333
2 1050 2008 0.5000000
3 2000 2008 1.0000000
4 1700 2000 0.6666667
5 2000 2000 1.0000000
ddply(df, .(years), transform, percentile = ecdf(scores)(scores))
的意思是:按照数据框df中的年份进行分组,对每个分组计算分数在该组中的百分位数,并将结果添加为新的一列。其中ecdf函数用于计算累积分布函数。 - hadley