subset() вектора в R

Я написал следующую функцию на основе 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, которое я мог пропустить. Что-нибудь уже есть?


person Ken Williams    schedule 19.01.2012    source источник
comment
Кажется, он все еще работает, если мы изменим data.frame(.=x) на list(.=x), и я подозреваю, что производительность улучшится.   -  person IRTFM    schedule 20.01.2012
comment
Чтобы уточнить - я только что использовал myDataFrame$MyVariableName в качестве примера очень длинного имени вектора. Вероятно, мне следовало использовать myVeryVeryVeryLongVariableName или что-то в этом роде, но если я отредактирую это сейчас, ответ @joran не будет иметь особого смысла. знак равно   -  person Ken Williams    schedule 20.01.2012


Ответы (2)


Я понимаю, что решение, которое предлагает Кен, является более общим, чем просто выбор элементов в пределах диапазонов (поскольку оно должно работать с любым логическим выражением), но это напомнило мне, что у Грега Сноу есть инфиксные операторы сравнения в его пакете Teaching Demos:

library(TeachingDemos)
x0 <- rnorm(100)
x0[ 0 %<% x0 %<% 1.5 ]
person IRTFM    schedule 19.01.2012
comment
Прохладный! Я тоже всегда хотел конструкцию x < y < z. - person Ken Williams; 20.01.2012
comment
Помню, когда в Perl появилась такая штука, они поняли, что у них уже есть фатальная ошибка, говорящая о том, что вы использовали 'x ‹ y ‹ z', которую нужно переписать как 'x ‹ y && y ‹ z', и поняли, как глупо это было не просто DWIM. - person Ken Williams; 20.01.2012
comment
Обратите внимание, что a < x < b эквивалентно abs(x-mean(c(a,b))) < (b-mean(c(a,b))) - person James; 20.01.2012
comment
@James удобно, когда вы хотите оценить x только один раз. - person Ken Williams; 20.01.2012

Спасибо, что поделились Кеном.

Вы можете использовать:

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
person Tyler Rinker    schedule 19.01.2012
comment
Да, я тоже об этом думал - по сути, это то, что я делаю внутри моей функции ss(), конечно, но моя x называется .. - person Ken Williams; 20.01.2012
comment
Что касается скорости, я принял предложение @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) test replications elapsed relative user.self sys.self user.child sys.child 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 - person Ken Williams; 20.01.2012
comment
ахаха, это вообще некрасиво! - person Ken Williams; 20.01.2012
comment
Еще одна вещь, которую я изменил в этом тесте, заключалась в том, чтобы включить присвоение x в код tyler. - person Ken Williams; 20.01.2012
comment
Кен, просто добавьте код в свой исходный пост или в качестве ответа внизу. - person Tyler Rinker; 20.01.2012
comment
Я определенно согласен с вашим мнением о невозможности совместного использования, поэтому я задался вопросом, было ли что-то уже в ядре. - person Ken Williams; 20.01.2012
comment
Я повторно запускаю код теста в R 3.0.3. ................................................. ................................................. ... test replications elapsed relative user.self sys.self user.child sys.child 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 - person hs3180; 03.04.2014