4

我对 r(来自 sas)比较陌生,我需要在每个组中选择不同数量的观察值。组由两个变量的值标识

ToSelect <- data.frame(
                           key1=c(1,1,1,1,1,2,2,2,2,2,2,2),
                           key2=c("a","a","b","b","b","a","a","a","a","b","b","b"),
                           var1=c(2,3,4,6,2,7,8,5,7,1,8,5)
                          )
NumObs <- data.frame(
                           key1=c(1,1,2,2),
                           key2=c("a","b","a","b"),
                           NumObs=c(1,2,2,1)
                       )

我试过(来自问题“为 R 中的每个级别选择前 80 个观察值”)

ToSelect <- merge(x=ToSelect,y=NumObs,by=c("key1","key2"))
library(plyr)
Selected <- ddply(ToSelect, .(key1,key2), head, n = NumObs)

这使

错误:长度(n)== 1L 不是 TRUE

这对专家来说可能是一个明显的错误(na scalar, NumObs a vector?)

从同一个问题,我试过:

Selected <- do.call(
                     rbind, 
                     lapply(split(ToSelect, c(ToSelect$key1,ToSelect$key2)), head, NumObs)
                    )

这使

错误:长度(n)== 1L 不正确。另外:警告消息:在 split.default(x = seq_len(nrow(x)), f = f, drop = drop, ...) 中:数据长度不是拆分变量的倍数

所以,和以前一样的错误,加上多个东西,如果组的长度不同,我不能使用拆分?

然后我发现了“按组观察编号”的问题,我无法使 rle/sequence 答案在我的情况下起作用,但调整了 ddply 答案:

ToSelect <- ddply(ToSelect, .(key1, key2), function(z){
                                                         cbind(var1=z$var1,NumObs=z$NumObs,
                                                         data.frame(
                                                                       SeqNum = seq_along(z$key2)
                                                                    )
                                                               )
                                                       }
                 )
Selected <- ToSelect[ToSelect$SeqNum<=ToSelect$NumObs,c("key1","key2","var1")]

哪个有效。

显然我的真实数据要大得多,那么有没有更好的替代方法?谢谢!

4

3 回答 3

2

如果您正在寻找效率,我建议您研究一下这个data.table包。这个问题的一个相当直接的解决方案可能是:

#Convert objects to data.table
require("data.table")
ToSelect <- data.table(ToSelect)
NumObs <- data.table(NumObs)

#Merge data
ToSelect <- merge(ToSelect,NumObs,by=c("key1","key2"),all.x=T)

#Provide intra-group ordering variable
ToSelect[,Grp.Seq:=seq(1:.N),by=c("key1","key2")]
Selected <- ToSelect[NumObs>=Grp.Seq]
Selected

   key1 key2 var1 NumObs Grp.Seq
1:    1    a    2      1       1
2:    1    b    4      2       1
3:    1    b    6      2       2
4:    2    a    7      2       1
5:    2    a    8      2       2
6:    2    b    1      1       1

如果您还是新手,并且经常使用大型数据集,那么从头开始R学习可能是有意义的。data.table我为我的工作使用非常大的数据集,而 data.frame 类对于我所做的大部分工作并不实用。如果需要,可以很容易地在两者之间切换回来data.framedata.table

于 2014-05-20T11:43:18.967 回答
1

如果您正在尝试做我认为您正在尝试做的事情,那么data.table将非常有帮助。var1根据您NumObs表中给出的所需观察次数,这个小班轮基本上随机抽样分组的值。尝试:

# Load package
require(data.table)
# Make your data.frames into data.tables                       
ts <- data.table( ToSelect , key = c( "key1","key2"))
no <- data.table( NumObs , key = c( "key1","key2") )

# Join together based on key columns and sample by group
no[ts][ , sample( var1 , NumObs , TRUE ) , by = c("key1","key2") ]
#   key1 key2 V1
#1:    1    a  2 
#2:    1    b  6 #|_ Two observations of group 1b
#3:    1    b  6 #|
#4:    2    a  5 #|_ Two observations of group 2a
#5:    2    a  8 #|
#6:    2    b  5

如果您不想进行替换采样(如上所述,请查看组 1b 的值 - 它们是相同的)然后删除TRUE参数 of sample(因此您不能选择比每组中的观察总数更多的观察)。

于 2014-05-20T11:44:41.447 回答
0

这是一种方法,您可以使用它从ToSelect每组key1key2-pair 中选择 n 个观察值。在这种情况下,n 由data.frameNumObs中指定。NumObs

为此,我使用包dplyr,但肯定有其他方法可以做同样的事情。

安装包并将其加载到您的库中:

require(dplyr)

我首先将两个data.frames加入(=合并)在一起:

df <- left_join(ToSelect, NumObs, by=c("key1", "key2"))

然后我将结果 data.frame df 按key1and分组,key2并选择每组的第一个观测NumObs值:

df <- df %.% group_by(key1, key2) %.% filter(1:n() <= NumObs)

>df
#  key1 key2 var1 NumObs
#1    1    a    2      1
#2    1    b    4      2
#3    1    b    6      2
#4    2    a    7      2
#5    2    a    8      2
#6    2    b    1      1

如果您想要没有列的结果 data.frame NumObs,您可以将其调整为:

df <- df %.% group_by(key1, key2) %.% filter(1:n() <= NumObs) %.% select(-NumObs)
于 2014-05-20T11:14:05.237 回答