mutate_at для нескольких наборов столбцов с разными функциями

Я определил функции, которые должны применяться к разным наборам столбцов фрейма данных. Например, mtcars Я хочу применить as.integer() функцию к столбцам c("mpg", "cyl") и as.logical() к c("vs", "am")

library(dplyr)

mtcars %>% 
  mutate_at(c("mpg", "cyl"), as.integer) %>% 
  mutate_at(c("vs", "am"), as.logical)

Какова практика, желательно с tidyverse, чтобы сохранить эти наборы столбцов с соответствующими функциями и применить их, не используя mutate_at несколько раз.


person mihagazvoda    schedule 12.11.2019    source источник
comment
Не совсем уверен, как выглядит ваш желаемый подход. Вы хотите создать список, содержащий переменные и соответствующие функции?   -  person TimTeaFan    schedule 12.11.2019
comment
Я не уверен, что вы хотите. Вы просто ищете способ сохранить набор данных mtcars с этими изменениями (то есть без применения функций mutate_at), чтобы вы могли продолжить работу, например, с mpg & cyl и vs & am как целочисленные и логические значения соответственно?   -  person larsoevlisen    schedule 12.11.2019
comment
@TimTeaFan Это может быть список, содержащий переменные и соответствующие функции, или что-то еще (список мне кажется наиболее подходящим). Главное, что в конце мне не нужно писать несколько mutate_at.   -  person mihagazvoda    schedule 12.11.2019
comment
@larsoevlisen Мне нужен тот же результат, что и в приведенном выше коде, но без многократного использования mutate_at - мне пришлось бы написать почти 20 таких операторов в моем реальном коде (это всего лишь пример). Надеюсь, я был ясен, иначе дайте мне знать.   -  person mihagazvoda    schedule 12.11.2019


Ответы (2)


Я бы подошел к этому так. Результат - это список матриц, которые можно использовать для дальнейшей перезаписи существующих столбцов или создания новых, либо использовать как автономный объект данных.

vars <- list(van = c("mpg", "cyl"),
             tu = c("vs", "am"))
funk <- list(van = as.integer,
             tu = as.logical)

mapply(FUN = function(v, f) {
  sapply(mtcars[, v], FUN = f)
}, v = vars, f = funk, SIMPLIFY = FALSE)

$van
      mpg cyl
 [1,]  21   6
 [2,]  21   6
 [3,]  22   4
 [4,]  21   6
 [5,]  18   8
 ...
$tu
         vs    am
 [1,] FALSE  TRUE
 [2,] FALSE  TRUE
 [3,]  TRUE  TRUE
 [4,]  TRUE FALSE
 [5,] FALSE FALSE
 ...

Чтобы перезаписать существующие столбцы, вы можете использовать «ужасный» цикл for. :)

mtcars[colnames(out$van)] <- out$van
mtcars[colnames(out$tu)] <- out$tu
# in generalized form
for (i in seq_along(out)) {
  mtcars[colnames(out[[i]])] <- out[[i]]
}

> head(mtcars)
                  mpg cyl disp  hp drat    wt  qsec    vs    am gear carb
Mazda RX4          21   6  160 110 3.90 2.620 16.46 FALSE  TRUE    4    4
Mazda RX4 Wag      21   6  160 110 3.90 2.875 17.02 FALSE  TRUE    4    4
Datsun 710         22   4  108  93 3.85 2.320 18.61  TRUE  TRUE    4    1
Hornet 4 Drive     21   6  258 110 3.08 3.215 19.44  TRUE FALSE    3    1
Hornet Sportabout  18   8  360 175 3.15 3.440 17.02 FALSE FALSE    3    2
Valiant            18   6  225 105 2.76 3.460 20.22  TRUE FALSE    3    1

Или делать все в одну петлю (короче).

for (i in seq_along(vars)) {
  cls <- vars[[i]]
  f <- funk[[i]]

  mtcars[, cls] <- sapply(mtcars[, cls], FUN = f)
}

> head(mtcars)
                  mpg cyl disp  hp drat    wt  qsec    vs    am gear carb
Mazda RX4          21   6  160 110 3.90 2.620 16.46 FALSE  TRUE    4    4
Mazda RX4 Wag      21   6  160 110 3.90 2.875 17.02 FALSE  TRUE    4    4
Datsun 710         22   4  108  93 3.85 2.320 18.61  TRUE  TRUE    4    1
Hornet 4 Drive     21   6  258 110 3.08 3.215 19.44  TRUE FALSE    3    1
Hornet Sportabout  18   8  360 175 3.15 3.440 17.02 FALSE FALSE    3    2
Valiant            18   6  225 105 2.76 3.460 20.22  TRUE FALSE    3    1
person Roman Luštrik    schedule 12.11.2019
comment
Не могли бы вы добавить, как бы вы перезаписали существующие столбцы? Спасибо. - person mihagazvoda; 12.11.2019
comment
@Miha готово. Также добавлено другое решение, которое пропускает часть mapply. - person Roman Luštrik; 12.11.2019

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

for (i in colnames(mtcars)) {
    mtcars[, i] = switch(i,
                         mpg = as.integer(mtcars[, i]),
                         cyl = as.integer(mtcars[, i]),
                         vs = as.logical(mtcars[, i]),
                         am = as.logical(mtcars[, i]))
}

> head(mtcars)
                  mpg cyl    vs    am
Mazda RX4          21   6 FALSE  TRUE
Mazda RX4 Wag      21   6 FALSE  TRUE
Datsun 710         22   4  TRUE  TRUE
Hornet 4 Drive     21   6  TRUE FALSE
Hornet Sportabout  18   8 FALSE FALSE
Valiant            18   6  TRUE FALSE

РЕДАКТИРОВАТЬ:

Поскольку функция switch () имеет побочный эффект удаления столбцов, если не задано значение по умолчанию, и OP просит сохранить все столбцы ... вот решение:

for (i in colnames(mtcars)) {
    mtcars[, i] = switch(i,
                         mpg = as.integer(mtcars[, i]),
                         cyl = as.integer(mtcars[, i]),
                         vs = as.logical(mtcars[, i]),
                         am = as.logical(mtcars[, i]),
                         mtcars[, i]) # just add a default option
}

> head(mtcars)
                  mpg cyl disp  hp drat    wt  qsec    vs    am gear carb
Mazda RX4          21   6  160 110 3.90 2.620 16.46 FALSE  TRUE    4    4
Mazda RX4 Wag      21   6  160 110 3.90 2.875 17.02 FALSE  TRUE    4    4
Datsun 710         22   4  108  93 3.85 2.320 18.61  TRUE  TRUE    4    1
Hornet 4 Drive     21   6  258 110 3.08 3.215 19.44  TRUE FALSE    3    1
Hornet Sportabout  18   8  360 175 3.15 3.440 17.02 FALSE FALSE    3    2
Valiant            18   6  225 105 2.76 3.460 20.22  TRUE FALSE    3    1
person MarChers    schedule 12.11.2019