Использование mutate_at для вставки относительных значений после каждого столбца (относительно второго столбца таблицы)

У меня есть фрейм данных (тиббл) с несколькими столбцами, и для каждого столбца после первых двух я хотел бы сохранить абсолютное значение, но также вставить значение относительно второго столбца. Например, я начинаю со следующего фрейма данных (имена столбцов могут отличаться!):

df = tibble(val1 = 5:10, val2 = 10:15, val3 = 15:20); df
# A tibble: 6 x 3
   val1  val2  val3
  <int> <int> <int>
1     5    10    15
2     6    11    16
3     7    12    17
4     8    13    18
5     9    14    19
6    10    15    20

Теперь для столбцов val2 и val3 я также хочу вставить столбец сразу после val2 и после val3, показывающий значение относительно val1. Как мне это сделать???

Полученный тиббл должен выглядеть так:

dfrel = tibble(val1 = 5:10, val2 = 10:15, rel2 = val2/val1, val3 = 15:20, rel3 = val3/val1)
dfrel
# A tibble: 6 x 5
   val1  val2  rel2  val3  rel3
  <int> <int> <dbl> <int> <dbl>
1     5    10  2.00    15  3.00
2     6    11  1.83    16  2.67
3     7    12  1.71    17  2.43
4     8    13  1.62    18  2.25
5     9    14  1.56    19  2.11
6    10    15  1.50    20  2.00

К сожалению, я не могу написать правильный вызов mutate_at для вставки этого относительного столбца сразу после каждого столбца значений. Фактически, я не могу написать mutate_at, используя funs (), который изменяет столбец, обращаясь к другим столбцам (по положению, а не по имени).

Замена val2 и val3 относительными значениями работает (с использованием лямбда-функции вместо funs), но не сохраняет исходные столбцы val2 и val3, как требуется:

df %>%
     mutate_at(vars(-1), function(v) v/.[[1]])
# A tibble: 6 x 3
   val1  val2  val3
  <int> <dbl> <dbl>
1     5  2.00  3.00
2     6  1.83  2.67
3     7  1.71  2.43
4     8  1.62  2.25
5     9  1.56  2.11
6    10  1.50  2.00

Все мои попытки использовать funs() терпят неудачу:

df %>%
     mutate_at(vars(-1), funs(./.tbl[[1]]))
Error in mutate_impl(.data, dots) : 
  Evaluation error: object '.tbl' not found.

df %>%
     mutate_at(vars(-1), funs(function(v) v/.[[1]]))
Error in mutate_impl(.data, dots) : 
  Column `val2` is of unsupported type function

Одна сложность по сравнению с Использование функций нескольких столбцов в вызов dplyr mutate_at заключается в том, что у моего столбца val1 нет фиксированного имени (т.е. он не всегда называется val1), поэтому я не могу использовать его по имени в аргументах funs. Другая сложность заключается в том, что тиббл создается на лету (с использованием множества операторов конвейера) и обычно не сохраняется в переменной, поэтому я не могу просто разделить на df [[1]] ...

Итак, каков правильный подход dplyr для вставки относительных столбцов (то есть процента от первого столбца) после каждого столбца?


person Reinhold Kainhofer    schedule 12.03.2018    source источник


Ответы (2)


Дайте вашей функции имя, заключив ее в список, чтобы mutate_at создавал новые столбцы. Что-то вроде следующего (имена столбцов могут быть не такими идеальными, поэтому вам может потребоваться переименовать их при необходимости):

df %>% mutate_at(vars(-1), list(rel = function(v) v / .[[1]]))

# A tibble: 6 x 5
#   val1  val2  val3 val2_rel val3_rel
#  <int> <int> <int>    <dbl>    <dbl>
#1     5    10    15     2.00     3.00
#2     6    11    16     1.83     2.67
#3     7    12    17     1.71     2.43
#4     8    13    18     1.62     2.25
#5     9    14    19     1.56     2.11
#6    10    15    20     1.50     2.00
person Psidom    schedule 12.03.2018
comment
Спасибо, добавление постфикса имени в список - это то, о чем я бы никогда не подумал. Именование не является проблемой, так как я все равно обработаю имена столбцов в kable. К сожалению, ваше решение добавляет все относительные столбцы в конец. Есть ли способ вставить новые столбцы сразу после каждого столбца значений? Количество столбцов не фиксировано, так как это значения из разных таблиц смертности, которые мне нужно учитывать при публикации, поэтому перегруппировка вручную не является простой задачей. - person Reinhold Kainhofer; 12.03.2018
comment
Как насчет сортировки имен столбцов? Цепочка %>% select(sort(names(.))) после mutate_at? При этом может не сохраняться исходный порядок столбцов, но он присоединяет столбец rel после соответствующего столбца val. - person Psidom; 12.03.2018
comment
Или, если вы хотите сохранить порядок всех столбцов, вы можете сделать что-то вроде этого: select(1, { mut_cols = tail(names(.), -1); cbind(mut_cols[c(T,F)], mut_cols[c(F,T)]) }) - person Psidom; 12.03.2018
comment
Спасибо, теперь я сам придумал способ переупорядочить столбцы с помощью вспомогательной функции для генерации индексов: interleaveColumns = function(v) { c(1, unlist(split(2:length(v), 1:((length(v)-1)/2)), use.names = FALSE)) } Тогда простой выбор сделает %>% select(interleaveColumns(.)) - person Reinhold Kainhofer; 12.03.2018

С помощью Псидома это мое окончательное решение проблемы:

interleaveColumns = function(v) { 
    c(1, unlist(split(2:length(v), 1:((length(v)-1)/2)), use.names = FALSE)) 
}

df = tibble(val1 = 5:10, val2 = 10:15, val3 = 15:20, val4 = 25:30, val5 = 1:6);

# mutate_at can be given a named list to create a new column 
# for each existing columnt (appended to the end => we need 
# to reorder the columns and interleave the new columns with 
# the old columns using the interleaveColumns function)

df %>%
     mutate_at(vars(-1), list(rel = function(v) v/.[[1]])) %>% 
     select(interleaveColumns(.))

# A tibble: 6 x 9
   val1  val2 val2_rel  val3 val3_rel  val4 val4_rel  val5 val5_rel
  <int> <int>    <dbl> <int>    <dbl> <int>    <dbl> <int>    <dbl>
1     5    10     2.00    15     3.00    25     5.00     1    0.200
2     6    11     1.83    16     2.67    26     4.33     2    0.333
3     7    12     1.71    17     2.43    27     3.86     3    0.429
4     8    13     1.62    18     2.25    28     3.50     4    0.500
5     9    14     1.56    19     2.11    29     3.22     5    0.556
6    10    15     1.50    20     2.00    30     3.00     6    0.600
person Reinhold Kainhofer    schedule 12.03.2018