假设以下数据:
GroupId <- c(1,1,1,1,2,2,2,3,3)
IndId <- c(1,1,2,2,3,4,4,5,5)
IndGroupProperty <- c(1,2,1,2,3,3,4,5,6)
PropertyType <- c(1,2,1,2,2,2,1,2,2)
df <- data.frame(GroupId, IndId, IndGroupProperty, PropertyType)
df
这些是多级数据,其中每个组GroupId
由一个或多个个人IndId
组成,可以访问一个或多个属性IndGroupProperty
,这些属性对于他们各自的群组是唯一的(即属性1属于组1而没有其他组)。这些属性均属于PropertyType
类型。
任务是用虚拟变量标记每一行,其中至少有一个属于组中每个个体的第1类属性。
对于我们的样本数据,这只是:
ValidGroup <- c(1,1,1,1,0,0,0,0,0)
df <- data.frame(df, ValidGroup)
df
前四行标记为1,因为组(1)的每个个体(1,2)都可以访问类型1属性(1)。 后续三行属于组(2),其中只有个人(4)可以访问类型1属性(4)。因此这些都没有标记(0)。 最后两行也没有收到任何标志。组(3)仅由一个人(5)组成,可以访问两个第二类属性(5,6)。
我已经研究了几个命令:levels
似乎缺乏团队支持; getGroups
包中的nlme
不喜欢我的真实数据的输入;我想在doBy
中可能会有一些有用的内容,但summaryBy
似乎不会将levels
作为函数。
解决方案编辑:Henrik的dplyr
解决方案包含在一个函数中:
foobar <- function(object, group, ind, type){
groupvar <- deparse(substitute(group))
indvar <- deparse(substitute(ind))
typevar <- deparse(substitute(type))
eval(substitute(
object[, c(groupvar, indvar, typevar)] %.%
group_by(group, ind) %.%
mutate(type1 = any(type == 1)) %.%
group_by(group, add = FALSE) %.%
mutate(ValidGroup = all(type1) * 1) %.%
select(-type1)
))
}
答案 0 :(得分:2)
您也可以尝试ave
:
# for each individual within group, calculate number of 1s in PropertyType
v1 <- with(df, ave(PropertyType, list(GroupId, IndId), FUN = function(x) sum(x == 1)))
# within each group, check if all v1 is 1.
# The boolean result is coerced to 1 and 0 by ave.
df$ValidGroup <- ave(v1, df$GroupId, FUN = function(x) all(x == 1))
# GroupId IndId IndGroupProperty PropertyType ValidGroup
# 1 1 1 1 1 1
# 2 1 1 2 2 1
# 3 1 2 1 1 1
# 4 1 2 2 2 1
# 5 2 3 3 2 0
# 6 2 4 3 2 0
# 7 2 4 4 1 0
# 8 3 5 5 2 0
# 9 3 5 6 2 0
修改为不同大小的数据集添加了dplyr
替代和基准:原始数据,以及比原始数据大10倍和100倍的数据。
首先在功能中包含备选方案:
fun_ave <- function(df){
v1 <- with(df, ave(PropertyType, list(GroupId, IndId), FUN = function(x) sum(x == 1)))
df$ValidGroup <- ave(v1, list(df$GroupId), FUN = function(x) all(x == 1))
df
}
library(dplyr)
fun_dp <- function(df){
df %.%
group_by(GroupId, IndId) %.%
mutate(
type1 = any(PropertyType == 1)) %.%
group_by(GroupId, add = FALSE) %.%
mutate(
ValidGroup = all(type1) * 1) %.%
select(-type1)
}
fun_by <- function(df){
bar <- by(data=df,INDICES=df$GroupId,FUN=function(xx){
foo <- table(xx$IndId,xx$PropertyType)
if ( !("1" %in% colnames(foo)) ) {
return(FALSE) # no PropertyType=1 at all in this group
} else {
return(all(foo[,"1"]>0)) # return whether all IndId have an 1 entry
}})
cbind(df,ValidGroup = as.integer(bar[as.character(df$GroupId)]))
}
基准
原始数据:
microbenchmark(
fun_ave(df),
fun_dp(df),
fun_by(df))
# Unit: microseconds
# expr min lq median uq max neval
# fun_ave(df) 497.964 519.8215 538.8275 563.5355 651.535 100
# fun_dp(df) 851.861 870.6765 931.1170 968.5590 1760.360 100
# fun_by(df) 1343.743 1412.5455 1464.6225 1581.8915 12588.607 100
在一个微小的数据集上ave
的速度约为dplyr
的两倍,比by
的速度快2.5倍。
生成一些更大的数据;团体和个人数量的10倍
GroupId <- sample(1:30, 100, replace = TRUE)
IndId <- sample(1:50, 100, replace = TRUE)
PropertyType <- sample(1:2, 100, replace = TRUE)
df2 <- data.frame(GroupId, IndId, PropertyType)
microbenchmark(
fun_ave(df2),
fun_dp(df2),
fun_by(df2))
# Unit: milliseconds
# expr min lq median uq max neval
# fun_ave(df2) 2.928865 3.185259 3.270978 3.435002 5.151457 100
# fun_dp(df2) 1.079176 1.231226 1.273610 1.352866 2.717896 100
# fun_by(df2) 9.464359 9.855317 10.137180 10.484994 12.445680 100
dplyr
比ave
快三倍,比by
快近10倍。
团体和个人数量的100倍
GroupId <- sample(1:300, 1000, replace = TRUE)
IndId <- sample(1:500, 1000, replace = TRUE)
PropertyType <- sample(1:2, 1000, replace = TRUE)
df2 <- data.frame(GroupId, IndId, PropertyType)
microbenchmark(
fun_ave(df2),
fun_dp(df2),
fun_by(df2))
# Unit: milliseconds
# expr min lq median uq max neval
# fun_ave(df2) 337.889895 392.983915 413.37554 441.58179 549.5516 100
# fun_dp(df2) 3.253872 3.477195 3.58173 3.73378 75.8730 100
# fun_by(df2) 92.248791 102.122733 104.09577 109.99285 186.6829 100
ave
现在真的失去了基础。 dplyr
比by
快近30倍,比ave
快100多倍。
答案 1 :(得分:1)
试试这个:
bar <- by(data=df,INDICES=df$GroupId,FUN=function(xx){
foo <- table(xx$IndId,xx$PropertyType)
if ( !("1" %in% colnames(foo)) ) {
return(FALSE) # no PropertyType=1 at all in this group
} else {
return(all(foo[,"1"]>0)) # return whether all IndId have an 1 entry
}})
cbind(df,bar[as.character(df$GroupId)])
关键是使用by()
通过分组变量应用函数,这里是df$GroupId
。要应用的函数是匿名函数。对于每个块(由分组变量定义),它会创建table
和IndId
条目的PropertyType
。然后看看是否&#34; 1&#34;完全出现在PropertyType
中 - 如果没有,则返回FALSE
,如果是,则查看每个IndId
是否至少有一个&#34; 1&#34;条目(即table
的&#34; 1&#34;列中的所有条目是否都是&gt; 0。
我们将by()
调用的结果存储在结构bar
中,该结构根据分组变量中的级别命名。这反过来允许我们将结果回滚到原始data.frame
。请注意我在这里使用as.character()
来确保整数被解释为条目名称,而不是条目数字。当事物的名称可以被解释为数字时,经常会发生坏事。
如果您真的想要0-1结果而不是TRUE-FALSE
,只需添加as.numeric()
。
EDIT。让我们把它变成一个函数。
foobar <- function(object, group, ind, type) {
bar <- by(data=object,INDICES=object[,group],FUN=function(xx){
foo <- table(xx[,ind],xx[,type])
if ( !("1" %in% colnames(foo)) ) {
return(FALSE) # no PropertyType=1 at all in this group
} else {
return(all(foo[,"1"]>0)) # return whether all IndId have an 1 entry
}})
cbind(object,bar[as.character(object[,group])])
}
foobar(df,"GroupId","IndId","PropertyType")
这仍然要求目标完全是&#34; 1&#34;,但当然这也可以作为参数包含在函数定义中。请务必保持列名和包含列名的变量。