Как применить функцию для каждой группы в dplyr без необходимости определять функцию? - PullRequest
0 голосов
/ 05 августа 2020

Давайте рассмотрим следующий фрейм данных

library(dplyr)

d <- data.frame(v1 = c("a","a","b","b"), v2 = c("X","Y","Y","X"))

Для группы «a» столбец v2 находится в порядке (X, Y), который я считаю правильным. В противоположность группе «b» порядок неверен (Y, X).

Используя dplyr и функцию do (), я могу проверить для каждой группы, правильный порядок или нет:

filter_fn <- function(my_row){
  iX <- filter(my_row, v2 == "X")$i
  iY <- filter(my_row, v2 == "Y")$i
  res <-  as.logical(iX < iY)
  return(data.frame(res))
}

d %>%
  group_by(v1) %>%
  dplyr::mutate(i = row_number()) %>%
  do(filter_fn(.)) %>%
  ungroup()

Но чтобы избежать умножения функций, я хочу, чтобы logi c записывался непосредственно в цепочку dplyr. Я пробовал использовать group_map и group_modify:

d %>%
  group_by(v1) %>%
  dplyr::mutate(i = row_number()) %>%
  group_map( ~ {
    filter(.$v2 == "X")$i < filter(.$v2 == "Y")$i
  })

Но, видимо, мое понимание group_map неверно. В документации я не вижу, как функцию можно использовать в do (.) Без предварительного определения как функции как таковой.

Ожидаемым результатом будет следующий фрейм данных

v1 res
a   TRUE
b   FALSE

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Вы можете определить правильный порядок, используйте match, чтобы получить позицию v2, и diff, чтобы вычислить разницу их появления в каждом v1. Сделайте res как TRUE, если порядок совпадает.

library(dplyr)
correct_order = c('X', 'Y')

d %>%
 group_by(v1) %>%
 summarise(res = all(diff(match(correct_order, v2)) > 0))

#  v1    res  
#  <chr> <lgl>
#1 a     TRUE 
#2 b     FALSE
0 голосов
/ 05 августа 2020

Мы можем либо изменить форму до «широкого» формата, а затем выполнить поэлементное сравнение для каждого из столбцов

library(stringr)
library(dplyr)
library(tidyr)
library(data.table)
d %>% 
    mutate(rn = str_c('col', rowid(v1))) %>% 
    pivot_wider(names_from = rn, values_from = v2) %>% 
    transmute(v1, res = col1 < col2)
# A tibble: 2 x 2
#  v1    res  
#  <chr> <lgl>
#1 a     TRUE 
#2 b     FALSE

Или другой вариант - иметь переменную ordered, затем сгруппированную по 'v1', проверить, равно ли all levels переменной значениям unique при поэлементном сравнении

d %>%
   mutate(v2 = ordered(v2, c('X', 'Y'))) %>%
   group_by(v1) %>%
   summarise(res = all(levels(v2) == unique(v2)))
# A tibble: 2 x 2
#  v1    res  
#  <chr> <lgl>
#1 a     TRUE 
#2 b     FALSE
...