Переопределить группы в пользовательском DSL в R - PullRequest
0 голосов
/ 11 октября 2018

У меня есть небольшой DSL, позволяющий группировать переменные по их именам:

group <- function(.data, ...) {
  dots <- quos(...)
  for (i in 1:length(dots)) {
    in_group <- as.character(dots[[i]])[2]
    vec <- trimws(unlist(strsplit(in_group, "[+]")))
    .data <- cbind(.data, TRUE)
      names(.data) <- c(names(.data)[-length(names(.data))], paste0("group_", names(dots[i])))
      .data[, ncol(.data)] <- .data$vars %in% vec
  }
  return(.data)
}

library(magrittr)
# Some data
df <- data.frame(
  vars = c("one", "two", "three", "four"), stringsAsFactors = FALSE
)

# Define a group called abc containing elements two, three and four:
df %>% group(abc = two + three + four)
   vars group_abc
1   one     FALSE
2   two      TRUE
3 three      TRUE
4  four      TRUE

# Define multiple groups
df %>% group(odd = one + three, even = two + four, prime = one + two + three)
   vars group_odd group_even group_prime
1   one      TRUE      FALSE        TRUE
2   two     FALSE       TRUE        TRUE
3 three      TRUE      FALSE        TRUE
4  four     FALSE       TRUE       FALSE

Однако это не позволяет переопределять группы:

df %>% group(abc = two + three + four) %>% group(abc = two)
   vars group_abc group_abc
1   one     FALSE     FALSE
2   two      TRUE      TRUE
3 three      TRUE     FALSE
4  four      TRUE     FALSE

Группа abc определяется два разавместо того, чтобы перезаписываться.

Я пытался:

group2 <- function(.data, ...) {
  dots <- quos(...)
  for (i in 1:length(dots)) {
    in_group <- as.character(dots[[i]])[2]
    vec <- trimws(unlist(strsplit(in_group, "[+]")))
    if (any(grepl(names(dots[i]), names(.data)))) {
      .data[, grepl(names(dots[i]), names(.data))] <- .data$vars %in% vec
    } else {
      .data <- cbind(.data, TRUE)
      names(.data) <- c(names(.data)[-length(names(.data))], paste0("group_", names(dots[i])))
      .data[, ncol(.data)] <- .data$vars %in% vec
    }
  }
  return(.data)
}

df %>% group2(abc = two + three + four) %>% group2(abc = two)
   vars group_abc
1   one     FALSE
2   two      TRUE
3 three     FALSE
4  four     FALSE

Этот вид работает, но выглядит крайне некрасиво ..

Так что мой вопрос: Что хорошегоспособ переопределить группы в моем group DSL?

Спасибо за любые подсказки.


Еще немного контекста:

Вот другой мой вопрос, касающийся этой общей темы моего DSL

1 Ответ

0 голосов
/ 30 октября 2018

Это действительно забавный вопрос.Вы можете использовать dplyr::mutate для «перезаписи» существующих переменных.Мы также можем оптимизировать ваши циклы с помощью purrr::map.Основная идея состоит в том, чтобы токенизировать предоставленные выражения и создавать новые, которые выглядят как vars %in% c( "token1", "token2", etc. ).Полученные выражения затем передаются в mutate:

library( tidyverse )

group <- function(.data, ...) {
  dots  <- enexprs(...) %>% map(rlang::expr_text)
  nms   <- str_c( "group_", names(dots) )
  elems <- dots %>% str_split("[+]") %>% map(str_trim) %>%
                    map( ~expr(vars %in% !!.x) ) %>% set_names(nms)
  .data %>% mutate( !!!elems )
}

df %>% group(odd = one + three, even = two + four, prime = one + two + three)
#    vars group_odd group_even group_prime
# 1   one      TRUE      FALSE        TRUE
# 2   two     FALSE       TRUE        TRUE
# 3 three      TRUE      FALSE        TRUE
# 4  four     FALSE       TRUE       FALSE

df %>% group( abc = two + three + four ) %>% group( abc = two )
#    vars group_abc
# 1   one     FALSE
# 2   two      TRUE
# 3 three     FALSE
# 4  four     FALSE
...