R: кадр данных подмножества/группы с максимальным значением?

Учитывая такой фрейм данных:

  gid set  a  b
1   1   1  1  9
2   1   2 -2 -3
3   1   3  5  6
4   2   2 -4 -7
5   2   6  5 10
6   2   9  2  0

Как я могу подмножить / сгруппировать фрейм данных уникального gid с максимальным значением set и 1/0, если его значение a больше, чем его значение b?

Так вот, это было бы...

1,3,0
2,9,1

Какая-то глупая простая вещь в SQL, но я хотел бы немного лучше контролировать свой R, так что...


person Wells    schedule 21.01.2015    source источник


Ответы (3)


Кусочек пирога с dplyr:

dat <- read.table(text="gid set  a  b
1   1  1  9
1   2 -2 -3
1   3  5  6
2   2 -4 -7
2   6  5 10
2   9  2  0", header=TRUE)

library(dplyr)

dat %>%
  group_by(gid) %>%
  filter(row_number() == which.max(set)) %>%
  mutate(greater=a>b) %>%
  select(gid, set, greater)

## Source: local data frame [2 x 3]
## Groups: gid
## 
##   gid set greater
## 1   1   3   FALSE
## 2   2   9    TRUE

Если вам действительно нужны 1 и 0, а dplyr группы вызывают беспокойство:

dat %>%
  group_by(gid) %>%
  filter(row_number() == which.max(set)) %>%
  mutate(greater=ifelse(a>b, 1, 0)) %>%
  select(gid, set, greater) %>%
  ungroup

## Source: local data frame [2 x 3]
## 
##   gid set greater
## 1   1   3       0
## 2   2   9       1

Вы можете сделать то же самое без труб:

ungroup(
  select(
    mutate(
      filter(row_number() == which.max(set)), 
      greater=ifelse(a>b, 1, 0)), gid, set, greater))

но… но… почему?! :-)

person hrbrmstr    schedule 21.01.2015
comment
Это довольно популярная идиома конвейера, впервые появившаяся в пакете magrittr, но широко используемая в Hadleyverse (например, dplyr, ggvis, tidyr и т. д.). Вместо запутанных вложенных скобок эта идиома передает данные функциям для обработки, подобно тому, как работают цепочки функций javascript D3. - person hrbrmstr; 21.01.2015
comment
Спасибо за подробности! - person Wells; 21.01.2015
comment
Это может быть проблематично, если есть ничья для максимального набора. filter(row_number() == which.max(set)) может быть безопаснее - person Rich Scriven; 21.01.2015
comment
#ty @RichardScriven. оплошность с моей стороны. ответ: обновлено. что интересно, это не было проблемой в данных сетевого потока, которые я обрабатывал в последнее время. определенно крайний случай, на который следует обратить внимание. - person hrbrmstr; 21.01.2015
comment
Как вы можете отсортировать результат, например. установить номер по возрастанию? - person Wells; 21.01.2015
comment
Кроме того, этот %>%, кажется, достигает максимум десяти элементов, а затем я получаю строку .. ... ..., по-видимому, указывающую на большее? Почему ограничение и как его избежать? - person Wells; 21.01.2015
comment
Возможный вариант ответа: dat %>% group_by(gid) %>% slice(which.max(set)) %>% transmute(set, greater = as.integer(a>b)) - person talat; 21.01.2015
comment
@RichardScriven, повторите свой комментарий к фильтру. mtcars %>% filter(row_number() == which.max(cyl)) вернет только первый максимум, и imo будет проще с mtcars %>% slice(which.max(cyl)). Если вы хотите вернуть все максимальные значения, вы можете использовать, например, mtcars %>% filter(cyl %in% max(cyl)) или mtcars %>% filter(min_rank(desc(cyl)) == 1). Я думаю, что некоторые уточняющие комментарии отсутствуют, поэтому не уверен, что именно вы имели в виду. - person talat; 21.01.2015
comment
@wells, это просто dplyr печатает разумно (вы можете добавить %>% print(n=1000) в конец цепочки для просмотра. Я обновлю ответ, чтобы преобразовать его в обычный data.frame, а также добавлю альтернативный ответ @docendo к примеру. - person hrbrmstr; 21.01.2015

Вот вариант data.table, если ваши исходные данные называются df.

library(data.table)

setDT(df)[, .(set = max(set), b = as.integer(a > b)[set == max(set)]), gid]
#    gid set b
# 1:   1   3 0
# 2:   2   9 1

Обратите внимание, что для учета нескольких max(set) строк я использовал set == max(set) в качестве подмножества, так что это вернет то же количество строк, для которых есть связи для максимума (если это вообще имеет смысл).

И любезно предоставлен @thelatemail, еще один вариант таблицы данных:

setDT(df)[, list(set = max(set), ab = (a > b)[which.max(set)] + 0), by = gid]
#    gid set ab
# 1:   1   3  0
# 2:   2   9  1
person Rich Scriven    schedule 21.01.2015

В base R вы можете использовать ave

indx <- with(df, ave(set, gid, FUN=max)==set)
#in cases of ties
#indx <- with(df, !!ave(set, gid, FUN=function(x) 
#                  which.max(x) ==seq_along(x)))


transform(df[indx,], greater=(a>b)+0)[,c(1:2,5)]
#   gid set greater
# 3   1   3       0
# 6   2   9       1
person akrun    schedule 21.01.2015