Суммировать по всем столбцам

У меня есть данные следующего формата:

gen = function () sample.int(10, replace = TRUE)
x = data.frame(A = gen(), C = gen(), G = gen(), T = gen())

Теперь я хотел бы присоединить к каждой строке общую сумму всех элементов в строке (моя фактическая функция более сложна, но sum иллюстрирует проблему).

Без dplyr я бы написал

cbind(x, Sum = apply(x, 1, sum))

В результате чего:

   A C  G T Sum
1  3 1  6 9  19
2  3 4  3 3  13
3  3 1 10 5  19
4  7 2  1 6  16
…

Но сделать это с помощью dplyr кажется на удивление сложным.

Я пробовал

x %>% rowwise() %>% mutate(Sum = sum(A : T))

Но результат — это не сумма столбцов каждой строки, это нечто неожиданное и (для меня) необъяснимое.

я тоже пробовал

x %>% rowwise() %>% mutate(Sum = sum(.))

Но здесь . — это просто заполнитель для всего x. Предоставление аргумента без, что неудивительно, также не работает (все результаты 0). Излишне говорить, что ни один из этих вариантов не работает без rowwise().

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


person Konrad Rudolph    schedule 22.01.2015    source источник
comment
Если сумма просто для иллюстрации, возможно, это плохой выбор, потому что для этого есть специальная функция, как показано в ответе ниже. Для других функций может быть больше похоже на dplyr собирать интересующие вас столбцы, вычислять интересующие вас значения и затем распространять (используя, например, tyr). По крайней мере, это моя интерпретация..   -  person talat    schedule 22.01.2015
comment
Мне интересно, может ли что-нибудь подобное этому library(data.table) ; setDT(x)[, Sum := Reduce("+", .SD)][] быть полезным...   -  person David Arenburg    schedule 23.01.2015
comment
@DavidArenburg, хороший подход, который работает так же с dplyr. Как бы вы использовали его с другой функцией, например, из моего ответа? (Я думаю, разница в том, что + — это бинарная функция, принимающая 2 входа, которые затем можно применять/уменьшать несколько раз, в то время как f из моего ответа принимает сразу весь вектор..)   -  person talat    schedule 23.01.2015
comment
@docendodiscimus, вот почему я разместил это в комментариях. У нас нет настоящей функции Конрада, так что, возможно, она будет работать и с Reduce. Подождем и посмотрим, что он скажет.   -  person David Arenburg    schedule 23.01.2015
comment
@DavidArenburg, конечно, было бы интересно узнать немного больше о дизайне функций ввода/вывода.   -  person talat    schedule 23.01.2015
comment
@David, я хотел иметь общее решение, так как эта проблема возникает у меня снова и снова с разными функциями. В моем текущем случае функция не поддается Reduce — она вычисляет смещение GC из таблицы частот кодонов. Вот реализация: gist.github.com/klmr/4898c3eb1a5216850134   -  person Konrad Rudolph    schedule 23.01.2015


Ответы (3)


Я когда-то делал что-то подобное, и к тому времени у меня получилось:

x %>%
  rowwise() %>%
  do(data.frame(., res = sum(unlist(.))))
#    A  C G  T res
# 1  3  2 8  6  19
# 2  6  1 7 10  24
# 3  4  8 6  7  25
# 4  6  4 7  8  25
# 5  6 10 7  2  25
# 6  7  1 2  2  12
# 7  5  4 8  5  22
# 8  9  2 3  2  16
# 9  3  4 7  6  20
# 10 7  5 3  9  24

Возможно, ваша более сложная функция прекрасно работает без unlist, но вроде как для sum она необходима. Поскольку . относится к «текущей группе», я сначала подумал, что ., например. первая строка в механизме rowwise будет соответствовать x[1, ], то есть списку, который sum счастливо проглатывает снаружи do

is.list((x[1, ]))
# [1] TRUE

sum(x[1, ])
# [1] 19 

Однако без unlist в do выдается ошибка, и я не уверен, почему:

x %>%
  rowwise() %>%
  do(data.frame(., res = sum(.)))
# Error in sum(.) : invalid 'type' (list) of argument
person Henrik    schedule 22.01.2015
comment
Хороший и лаконичный подход! (+1) OTOH Я думаю, что вызов do и data.frame для каждой строки также может быть дорогостоящим (возможно, с data_frame было бы немного эффективнее?) - person talat; 23.01.2015
comment
Спасибо за ваш отзыв! Для моего первоначального использования мне не нужно было считать микросекунды, этого было достаточно, чтобы в конечном итоге это сработало... Да, я пробовал data_frame, но он не возвращает желаемого результата. - person Henrik; 23.01.2015
comment
Хм. Это работает. Таким образом, . — это что-то для do (= текущая группа/строка), чем для mutate (= вся таблица). Удивительно. В моем случае мне тоже не нужен unlist, но моя функция тоже принимает однострочный data.frame, наверное поэтому. И да, в моем случае это довольно медленно, но таков и подход apply, и моя фактическая функция просто адски неэффективен и совсем не оптимизирован. - person Konrad Rudolph; 23.01.2015
comment
@KonradRudolph Я пытался найти официальный текст об использовании . в дополнение к тому, что написано в ?do и ?rowwise. Возможно, комментарий @hadley здесь (Местоимение задается либо %›%, либо do(), поэтому оно не будет использоваться с произвольными функциями dplyr.) предполагает, что . наиболее счастлив вместе с do? Однако между версиями происходит довольно много всего, поэтому я не уверен в текущем статусе друга и врага .. - person Henrik; 23.01.2015

Это делает то, что вы хотите?

Data %>%
   mutate(SumVar=rowSums(.))
person Andrew Taylor    schedule 22.01.2015
comment
Вероятно, да, но фактическая функция, которую я использую, не векторизована. - person Konrad Rudolph; 22.01.2015

Я постараюсь показать пример того, что я написал в своем комментарии. Предположим, у вас есть пользовательская функция f:

f <- function(vec) sum(vec)^2

И вы хотите применить эту функцию к каждой строке вашего data.frame x. Одним из вариантов в базе R будет использование apply, как вы показываете в своем вопросе:

> transform(x, z = apply(x, 1, f))
#   A  C  G T   z
#1  5  7 10 7 841
#2  1  9  5 9 576
#3  7 10  2 4 529
#4  1  4 10 1 256
#5  4  4  5 2 225
#6  9  1  6 8 576
#7  9  3  7 1 400
#8  5  2  7 5 361
#9  6  3 10 4 529
#10 5 10  1 6 484

Небольшой недостаток здесь заключается в том, что, поскольку вы используете apply в data.frame, весь data.frame сначала преобразуется в matrix, и это, конечно, означает, что все столбцы преобразуются в один и тот же тип.

С dplyr (и tidyr) вы могли бы решить проблему со сбором/плавлением и последующим распространением/литьем.

library(dplyr)
library(tidyr)
x %>% 
  mutate(n = row_number()) %>%    # add row numbers for grouping 
  gather(key, value, A:T) %>%
  group_by(n) %>% 
  mutate(z = f(value)) %>%
  ungroup() %>%
  spread(key, value) %>%
  select(-n)

#Source: local data frame [10 x 5]
#
#     z A  C  G T
#1  841 5  7 10 7
#2  576 1  9  5 9
#3  529 7 10  2 4
#4  256 1  4 10 1
#5  225 4  4  5 2
#6  576 9  1  6 8
#7  400 9  3  7 1
#8  361 5  2  7 5
#9  529 6  3 10 4
#10 484 5 10  1 6

Это, очевидно, немного более длинный код, чем использование apply, но как только данные станут немного больше, я ожидаю, что это будет намного быстрее, чем любой apply по строкам data.frame.

В качестве альтернативы вы можете использовать rowwise, если вы указываете столбцы вручную:

x %>%
  rowwise %>%
  mutate(z = f(c(A,C,G,T)))  # manual column specification

#Source: local data frame [10 x 5]
#Groups: <by row>
# 
#  A  C  G T   z
#1  5  7 10 7 841
#2  1  9  5 9 576
#3  7 10  2 4 529
#4  1  4 10 1 256
#5  4  4  5 2 225
#6  9  1  6 8 576
#7  9  3  7 1 400
#8  5  2  7 5 361
#9  6  3 10 4 529
#10 5 10  1 6 484

Я пока не понял, можно ли изменить решение rowwise так, чтобы оно работало с символьным вводом имен столбцов - возможно, как-то с лазиевом.

данные:

set.seed(16457)
gen = function () sample.int(10, replace = TRUE)
x = data.frame(A = gen(), C = gen(), G = gen(), T = gen())
person talat    schedule 22.01.2015
comment
Честно говоря, решение с плавлением выглядит невероятно неэффективным: плавление и распространение не предоставляются бесплатно, оно включает в себя много (в данном случае) бесплатного копирования. Ручное указание столбцов в моем случае также невозможно, так как мои фактические данные имеют 61 столбец, а не четыре (ну, я, очевидно, мог бы использовать mutate_, чтобы обойти это). У меня сложилось впечатление, что в dplyr отсутствует глагол… тем не менее, спасибо за этот подробный ответ. - person Konrad Rudolph; 22.01.2015
comment
@KonradRudolph, вы правы насчет эффективности (я сделал небольшой тест). Возможно, кто-то предложит более чистую и эффективную альтернативу - мне тоже будет интересно узнать, что - person talat; 22.01.2015