【发布时间】:2016-02-16 15:19:39
【问题描述】:
我已经定义了以下函数来检查数据框是否包含多个列,如果不包含则包含它们。
CheckFullCohorts <- function(df) {
# Checks if year/cohort df contains all necessary columns
# Args:
# df: year/cohort df
# Return:
# df: df, corrected if necessary
foo <- function(mydf, mystring) {
if(!(mystring %in% names(mydf))) {
mydf[mystring] <- 0
}
mydf
}
df <- foo(df, "age.16.20")
df <- foo(df, "age.21.24")
df <- foo(df, "age.25.49")
df <- foo(df, "age.50.57")
df <- foo(df, "age.58.65")
df <- foo(df, "age.66.70")
df
}
我会这样使用这个函数:
test <- data.frame(age.16.20 = rep("x", 5), lorem = rep("y", 5))
test <- CheckFullCohorts(test)
问题:如何通过使用列名向量进行检查,使函数的硬编码部分 (df <- foo(...) 更加灵活?
我试过了:
CheckFullCohorts <- function(df, col.list) {
# Checks if year/cohort df contains all necessary columns
# Args:
# df: year/cohort df
# col.list: named list of columns
# Return:
# df: df, corrected if necessary
foo <- function(mydf, mystring) {
if(!(mystring %in% names(mydf))) {
mydf[mystring] <- 0
}
mydf
}
df <- sapply(df, foo, mystring = col.list)
df
}
...但我得到了错误的结果:
test <- data.frame(age.16.20 = rep("x", 5), lorem = rep("y", 5))
test <- CheckFullCohorts(test, c("age.16.20", "age.20.25"))
Warning messages:
1: In if (!(mystring %in% names(mydf))) { :
the condition has length > 1 and only the first element will be used
2: In `[<-.factor`(`*tmp*`, mystring, value = 0) :
invalid factor level, NA generated
3: In if (!(mystring %in% names(mydf))) { :
the condition has length > 1 and only the first element will be used
4: In `[<-.factor`(`*tmp*`, mystring, value = 0) :
invalid factor level, NA generated
> test
age.16.20 lorem
"x" "y"
"x" "y"
"x" "y"
"x" "y"
"x" "y"
age.16.20 NA NA
age.20.25 NA NA
【问题讨论】:
-
如何将字符串向量
S传递给CheckFullCohort,然后用for(s in S){df <- foo(df, s)}替换相关行? -
当然可以。这是否意味着循环比矢量化解决方案更有效的情况之一?如果是的话,我仍然很想知道我的
sapply做错了什么。 -
循环是否有效取决于每次交互时是否复制数据框,不知道这里会不会是这样。但是关于循环效率不高的讨论往往被夸大了:这一步是你代码中的瓶颈吗?如果没有,那不是您应该花费精力优化的地方。至于
sapply,很好的问题——我倾向于使用plyr来处理这些事情,界面对我来说更有意义。 PS,@Roland 下面的回答也可以,不需要函数! -
你的
sapply尝试的问题是你应该循环col.list而不是df。
标签: r vectorization sapply