Есть функция, такая как переключатель, который работает внутри dplyr:: mutate?

Я не могу использовать переключатель внутри mutate, потому что он возвращает весь вектор, а не только строку. В качестве хака я использую:

pick <- function(x, v1, v2, v3, v4) {
    ifelse(x == 1, v1,
           ifelse(x == 2, v2,
                  ifelse(x == 3, v3,
                         ifelse(x == 4, v4, NA))))
}

Это работает внутри mutate, и сейчас это нормально, потому что я обычно выбираю среди 4 вещей, но это может измениться. Можете ли вы порекомендовать альтернативу?

Например:

library(dplyr)
df.faithful <- tbl_df(faithful)
df.faithful$x  <- sample(1:4, 272, rep=TRUE)
df.faithful$y1 <- rnorm(n=272, mean=7, sd=2)
df.faithful$y2 <- rnorm(n=272, mean=5, sd=2)
df.faithful$y3 <- rnorm(n=272, mean=7, sd=1)
df.faithful$y4 <- rnorm(n=272, mean=5, sd=1)

Использование pick:

mutate(df.faithful, y = pick(x, y1, y2, y3, y4))
Source: local data frame [272 x 8]

   eruptions waiting x        y1        y2       y3       y4        y
1      3.600      79 1  8.439092 5.7753006 8.319372 5.078558 8.439092
2      1.800      54 2 13.515956 6.1971512 6.343157 4.962349 6.197151
3      3.333      74 4  7.693941 6.8973365 5.406684 5.425404 5.425404
4      2.283      62 4 12.595852 6.9953995 7.864423 3.730967 3.730967
5      4.533      85 3 11.952922 5.1512987 9.177687 5.511899 9.177687
6      2.883      55 3  7.881350 1.0289711 6.304004 3.554056 6.304004
7      4.700      88 4  8.636709 6.3046198 6.788619 5.748269 5.748269
8      3.600      85 1  8.027371 6.3535056 7.152698 7.034976 8.027371
9      1.950      51 1  5.863370 0.1707758 5.750440 5.058107 5.863370
10     4.350      85 1  7.761653 6.2176610 8.348378 1.861112 7.761653
..       ...     ... .       ...       ...      ...      ...      ...

Мы видим, что я копирую значение из y1 в y, если x == 1, и так далее. Это то, что я ищу, но хочу иметь возможность сделать это, есть ли у меня список из 4 или 400 столбцов.

Пытаясь использовать switch:

mutate(df.faithful, y = switch(x, y1, y2, y3, 4))

Error in switch(c(1L, 2L, 4L, 4L, 3L, 3L, 4L, 1L, 1L, 1L, 4L, 3L, 1L,  : 
EXPR must be a length 1 vector

Пытаясь использовать list:

mutate(df.faithful, y = list(y1, y2, y3, y4)[[x]])
Error in list(c(8.43909205142925, 13.5159559591257, 7.69394050059568,  : 
recursive indexing failed at level 2

Попытка использовать c:

mutate(df.faithful, y = c(y1, y2, y3, y4)[x])
Source: local data frame [272 x 8]

   eruptions waiting x        y1        y2       y3       y4         y
1      3.600      79 1  8.439092 5.7753006 8.319372 5.078558  8.439092
2      1.800      54 2 13.515956 6.1971512 6.343157 4.962349 13.515956
3      3.333      74 4  7.693941 6.8973365 5.406684 5.425404 12.595852
4      2.283      62 4 12.595852 6.9953995 7.864423 3.730967 12.595852
5      4.533      85 3 11.952922 5.1512987 9.177687 5.511899  7.693941
6      2.883      55 3  7.881350 1.0289711 6.304004 3.554056  7.693941
7      4.700      88 4  8.636709 6.3046198 6.788619 5.748269 12.595852
8      3.600      85 1  8.027371 6.3535056 7.152698 7.034976  8.439092
9      1.950      51 1  5.863370 0.1707758 5.750440 5.058107  8.439092
10     4.350      85 1  7.761653 6.2176610 8.348378 1.861112  8.439092
..       ...     ... .       ...       ...      ...      ...       ...

Ошибок не возникает, но поведение не так, как предполагалось.

Ответ 1

Eons слишком поздно для OP, но в случае, если это отображается в поиске...

dplyr v0.5 имеет recode(), векторный вариант switch(), поэтому

data_frame(
  x = sample(1:4, 10, replace=TRUE),
  y1 = rnorm(n=10, mean=7, sd=2),
  y2 = rnorm(n=10, mean=5, sd=2),
  y3 = rnorm(n=10, mean=7, sd=1),
  y4 = rnorm(n=10, mean=5, sd=1)
) %>%
mutate(y = recode(x,y1,y2,y3,y4))

производит, как и ожидалось:

# A tibble: 10 x 6
       x        y1       y2       y3       y4        y
   <int>     <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
1      2  6.950106 6.986780 7.826778 6.317968 6.986780
2      1  5.776381 7.706869 7.982543 5.048649 5.776381
3      2  7.315477 2.213855 6.079149 6.070598 2.213855
4      3  7.461220 5.100436 7.085912 4.440829 7.085912
5      3  5.780493 4.562824 8.311047 5.612913 8.311047
6      3  5.373197 7.657016 7.049352 4.470906 7.049352
7      2  6.604175 9.905151 8.359549 6.430572 9.905151
8      3 11.363914 4.721148 7.670825 5.317243 7.670825
9      3 10.123626 7.140874 6.718351 5.508875 6.718351
10     4  5.407502 4.650987 5.845482 4.797659 4.797659

(Также работает с именованными args, включая символ и коэффициент x.)

Ответ 2

Выполняйте операцию по каждому значению x. Это версия data.table, я предполагаю, что аналогичный smth можно сделать в dplyr:

library(data.table)

dt = data.table(x = c(1,1,2,2), a = 1:4, b = 4:7)

dt[, newcol := switch(as.character(x), '1' = a, '2' = b, NA), by = x]
dt
#   x a b newcol
#1: 1 1 4      1
#2: 1 2 5      2
#3: 2 3 6      6
#4: 2 4 7      7

Ответ 3

Вы можете изменить свою функцию следующим образом:

map <- data.frame(i=1:2,v=10:11)
#   i  v
# 1 1 10
# 2 2 11

set.seed(1)
x <- sample(1:3,10,rep=T)
#  [1] 1 2 2 3 1 3 3 2 2 1

i <- match(x,map$i)
ifelse(is.na(i),x,map$v[i])
# [1] 10 11 11  3 10  3  3 11 11 10

Идея состоит в том, чтобы сохранить значения, которые вы ищете, и значения замены в отдельном фрейме данных map, а затем используйте match для соответствия x и map.

[Обновление]

Вы можете обернуть это решение в функцию, которая может использоваться в mutate:

multipleReplace <- function(x, what, by) {
  stopifnot(length(what)==length(by))               
  ind <- match(x, what)
  ifelse(is.na(ind),x,by[ind])
}

# Create a sample data set
d <- structure(list(x = c(1L, 2L, 2L, 3L, 1L, 3L, 3L, 2L, 2L, 1L), y = c(1L, 2L, 2L, 3L, 3L, 1L, 3L, 2L, 2L, 1L)), .Names = c("x", "y"), row.names = c(NA, -10L), class = "data.frame")

d %>% 
  mutate(z = multipleReplace(x, what=c(1,3), by=c(101,103)))
#    x y   z
# 1  1 1 101
# 2  2 2   2
# 3  2 2   2
# 4  3 3 103
# 5  1 3 101
# 6  3 1 103
# 7  3 3 103
# 8  2 2   2
# 9  2 2   2
# 10 1 1 101

Ответ 4

Здесь другой способ: data.table. Идея состоит в том, чтобы в принципе создать ключевую таблицу данных с комбинациями, а затем выполнить соединение следующим образом:

Я буду использовать таблицу data.table из ответа @eddi.

require(data.table)
key = data.table(x = 1:2, col = c("a", "b"))

setkey(dt, x)
dt[key, new_col := get(i.col), by=.EACHI]
#    x a b new_col
# 1: 1 1 4       1
# 2: 1 2 5       2
# 3: 2 3 6       6
# 4: 2 4 7       7

Соединение выполняется в столбце x. Для каждой строки ключа найдены соответствующие совпадающие строки в dt. Для ex: x = 1 из ключевых совпадений с строками 1 и 2 из dt. И в этих строках мы обращаемся к столбцу, который хранится в col ключа, который является "a". get("a") возвращает значения столбца a для соответствующих строк, которые равны 1 и 2. Надеюсь, что это поможет.

by=.EACHI гарантирует, что выражение new_col := get(i.col) оценивается для каждой строки в key. Вы можете узнать об этом здесь.

Ответ 5

Теперь вы можете использовать dplyr функцию case_when с mutate().

Чтобы следовать вашему примеру в создании данных:

library(dplyr)

df.faithful <- tbl_df(faithful)
df.faithful$x  <- sample(1:4, 272, rep=TRUE)
df.faithful$y1 <- rnorm(n=272, mean=7, sd=2)
df.faithful$y2 <- rnorm(n=272, mean=5, sd=2)
df.faithful$y3 <- rnorm(n=272, mean=7, sd=1)
df.faithful$y4 <- rnorm(n=272, mean=5, sd=1)

Теперь мы определяем новую функцию pick() используя case_when:

pick2 <- function(x, v1, v2, v3, v4) {
  out = case_when(
    x == 1 ~ v1,
    x == 2 ~ v2,
    x == 3 ~ v3,
    x == 4 ~ v4
  )
  return(out)
}

И вы видите, что можете прекрасно использовать его в mutate():

df.faithful %>% 
  mutate(y = pick2(x, y1, y2, y3, y4))

И вывод:

# A tibble: 272 x 8
   eruptions waiting     x    y1    y2    y3    y4     y
       <dbl>   <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
 1      3.6       79     3  8.73  7.23  8.89  4.04  8.89
 2      1.8       54     3  9.97  4.31  7.06  5.05  7.06
 3      3.33      74     1  6.65  7.23  4.46  6.49  6.65
 4      2.28      62     1  6.40  4.39  5.41  3.49  6.40
 5      4.53      85     4  3.96  8.85  7.43  6.51  6.51
 6      2.88      55     4  6.36  8.08  5.82  5.06  5.06
 7      4.7       88     1  5.91  6.47  6.43  5.88  5.91
 8      3.6       85     1  7.77  4.55  6.56  5.05  7.77
 9      1.95      51     4  5.74  6.46  6.95  4.26  4.26
10      4.35      85     1  7.04  1.73  5.71  2.53  7.04
# ... with 262 more rows

Ответ 6

Альтернативный (более задействованный) маршрут включает использование tidyr:

df %>%
  mutate(row = row_number()) %>%
  gather(n, y, y1:y4) %>%
  mutate(n = as.integer(str_extract(n, "[0-9]+"))) %>%
  filter(x == n) %>%
  arrange(row) %>%
  select(-c(row, n))

Ответ 7

Я немного опоздал, но вот мое решение с использованием mapply.

vswitch <- function(x, ...) {
  mapply(FUN = function(x, ...) {
           switch(x, ...)
         }, x, ...)
}

mutate(df.faithful, y = vswitch(x, y1, y2, y3, y4))

Ответ 8

Если вы хотите использовать switch в mutate, вы должны выполнить rowwise до того, как

iris %>%
  rowwise() %>%
  mutate(
    x = switch(
      as.character(Species),
      'setosa' = 'ss',
      'versicolor' = 'vc',
      'virginica' = 'vg'
    )
  ) %>%
  ungroup()