Эффективное функциональное программирование (с использованием mapply) в R для «естественной» процедурной задачи - PullRequest
1 голос
/ 29 мая 2011

Типичным случаем использования в R (по крайней мере для меня) является идентификация наблюдений в фрейме данных, которые имеют некоторую характеристику, которая зависит от значений в некотором подмножестве других наблюдений.

Чтобы сделать это более конкретным, предположим, что у меня есть ряд работников (проиндексированных WorkerId), которые есть связанная «Итерация»:

    raw <- data.frame(WorkerId=c(1,1,1,1,2,2,2,2,3,3,3,3),
              Iteration = c(1,2,3,4,1,2,3,4,1,2,3,4))

и я хочу в конечном итоге установить поднабор фрейма данных, чтобы исключить «последнюю» итерацию (путем создания логического «удаления») для каждого работника. Я могу написать функцию для этого:

raw$remove <- mapply(function(wid,iter){
                              iter==max(raw$Iteration[raw$WorkerId==wid])},
                 raw$WorkerId, raw$Iteration)

> raw$remove
  [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE

но это становится очень медленным, так как фрейм данных становится больше (вероятно, потому что я без необходимости вычисляю максимум для каждого наблюдения)

Мой вопрос заключается в том, что является более эффективным (и идиоматическим) способом сделать это в стиле функционального программирования. Сначала создается словарь значений WorkerId to Max, а затем используется в качестве параметра в другой функции, которая работает с каждым наблюдением?

Ответы [ 5 ]

3 голосов
/ 29 мая 2011

Для конкретной поставленной задачи !rev(duplicated(rev(raw$WorkerId))) или лучше, следуя совету Чарльза, !duplicated(raw$WorkerId, fromLast=TRUE)

3 голосов
/ 29 мая 2011

«Самым естественным способом» IMO является метод split-lapply-rbind. Вы начинаете с split () - разбивает список на группы, затем lapply () обрабатывает правило (в данном случае удаляет последнюю строку), а затем rbind () возвращает их вместе. Это все выполнимо как вложенный набор вызовов функций. Здесь проиллюстрированы два внутренних шага, а окончательный однострочник представлен внизу:

> lapply( split(raw, raw$WorkerId), function(x) x[-NROW(x),] )
$`1`
  WorkerId Iteration
1        1         1
2        1         2
3        1         3

$`2`
  WorkerId Iteration
5        2         1
6        2         2
7        2         3

$`3`
   WorkerId Iteration
9         3         1
10        3         2
11        3         3

do.call(rbind,  lapply( split(raw, raw$WorkerId), function(x) x[-NROW(x),] ) ) 

Хэдли Уикхем разработал широкий набор инструментов, пакет plyr, которые расширяют эту стратегию для более широкого круга задач.

2 голосов
/ 30 мая 2011
subset(raw, Iteration != ave(Iteration, WorkerId, FUN=max))
2 голосов
/ 29 мая 2011

Эта ситуация специально разработана для использования пакета plyr.

ddply(raw, .(WorkerId), function(df) df[-NROW(df),])

Он производит вывод

WorkerId Iteration
1        1         1
2        1         2
3        1         3
4        2         1
5        2         2
6        2         3
7        3         1
8        3         2
9        3         3
1 голос
/ 29 мая 2011
remove <- with(raw, as.logical(ave(Iteration, WorkerId, FUN=function(x) c(rep(TRUE, length(x)-1), FALSE)))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...