在R中,subset()函数用于从向量中选择子集。

6

我基于subset()编写了以下函数,我觉得它很方便:

ss <- function (x, subset, ...) 
{
    r <- eval(substitute(subset), data.frame(.=x), parent.frame())
    if (!is.logical(r)) 
        stop("'subset' must be logical")
    x[r & !is.na(r)]
}

所以,我可以写出:
ss(myDataFrame$MyVariableName, 500 < . & . < 1500)

替换为
myDataFrame$MyVariableName[ 500 < myDataFrame$MyVariableName 
                                & myDataFrame$MyVariableName < 1500]

这似乎是其他人可能已经开发出解决方案的东西,包括我可能错过的核心R中的某些东西。有什么已经存在的吗?

如果我们将 data.frame(.=x) 更改为 list(.=x),它似乎仍然可以工作,我认为性能会有所提高。 - IRTFM
澄清一下 - 我只是举例说明了一个非常长的向量名称 myDataFrame$MyVariableName。我可能应该使用 myVeryVeryVeryLongVariableName 或其他名称,但如果我现在编辑它,@joran 的答案就没有太多意义了。=) - Ken Williams
2个回答

4

我知道Ken提供的解决方案比仅在范围内选择项目更通用(因为它可以适用于任何逻辑表达式),但是这提醒我Greg Snow在他的Teaching Demos软件包中有比较中缀运算符:

library(TeachingDemos)
x0 <- rnorm(100)
x0[ 0 %<% x0 %<% 1.5 ]

太酷了!我一直想要一个 x < y < z 的结构。 - Ken Williams
我记得 Perl 也有这样的事情 - 他们意识到已经出现了致命错误,提示“您已使用 'x < y < z',必须将其重写为 'x < y && y < z'”,并意识到不仅如此 DWIM 是多么愚蠢。 - Ken Williams
请注意,a < x < b 等同于 abs(x-mean(c(a,b))) < (b-mean(c(a,b))) - James
@James,当你只想评估 x 一次时很方便。 - Ken Williams

2
感谢分享,Ken。
你可以使用以下方法:
x <- myDataFrame$MyVariableName; x[x > 100 & x < 180] 

你的代码可能需要输入更少的内容,但如果你要分享代码,那么代码的通用性会降低。我自己也有一些类似的节省时间的函数,但要谨慎使用它们,因为它们可能会减慢你的代码速度(额外的步骤),并且在与他人共享文件时还需要包含该函数的代码。
比较编写长度,两者几乎相同:
ss(mtcars$hp, 100 < . & . < 180)
x <- mtcars$hp; x[x > 100 & x < 180] 

比较1000次重复的时间。

library(rbenchmark)
benchmark(
       tyler = x[x > 100 & x < 180],
       ken = ss(mtcars$hp, 100 <. & . < 180),
 replications=1000)

   test replications elapsed relative user.self sys.self user.child sys.child
2   ken         1000    0.56 18.66667      0.36     0.03         NA        NA
1 tyler         1000    0.03  1.00000      0.03     0.00         NA        NA

所以,我想这取决于您是否需要速度和/或可共享性与方便性相比。如果只是针对您的小数据集,我认为它很有价值。

编辑:新的基准测试

> benchmark(
+     tyler = {x <- mtcars$hp; x[x > 100 & x < 180]}, 
+     ken = ss(mtcars$hp, 100 <. & . < 180), 
+     ken2 = ss2(mtcars$hp, 100 <. & . < 180),
+     joran = with(mtcars,hp[hp>100 & hp< 180 ]), 
+  replications=10000)

   test replications elapsed  relative user.self sys.self user.child sys.child
4 joran        10000    0.83  2.677419      0.69     0.00         NA        NA
2   ken        10000    3.79 12.225806      3.45     0.02         NA        NA
3  ken2        10000    0.67  2.161290      0.35     0.00         NA        NA
1 tyler        10000    0.31  1.000000      0.20     0.00         NA        NA

是的,我也考虑过 - 当然,在我的 ss() 函数内本质上就是这样做的,但我的 x 被命名为 . - Ken Williams
关于速度,我采纳了@DWin的建议,并在此基准测试中看到了约10倍的提升。 benchmark(tyler = {x <- mtcars$hp; x[x > 100 & x < 180]}, ken = ss(mtcars$hp, 100 <. & . < 180), ken2 = ss2(mtcars$hp, 100 <. & . < 180),+ replications=10000) 测试 复制次数 花费时间 相对值 用户自身 系统自身 子进程用户 子进程系统 2 ken 10000 2.26 15.06667 2.25 0 NA NA 3 ken2 10000 0.24 1.60000 0.25 0 NA NA 1 tyler 10000 0.15 1.00000 0.15 0 NA NA - Ken Williams
哎呀,这一点也不优美! - Ken Williams
我完全同意你对于不可共享的看法,这也是我想知道核心代码中是否已经有了相关功能的原因。 - Ken Williams
我重新在R 3.0.3中运行了基准测试代码。测试 次数 花费时间 相对值 用户CPU时间 系统CPU时间 子进程用户CPU时间 子进程系统CPU时间 4 joran 10000 0.19 1.357 0.18 0 NA NA 2 ken 10000 1.56 11.143 1.56 0 NA NA 3 ken2 10000 0.19 1.357 0.19 0 NA NA 1 tyler 10000 0.14 1.000 0.14 0 NA NA - hs3180
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接