dplyr изменяет вычисления сумм строк или пользовательские функции

#r

Вопрос:

Я пытаюсь изменить новую переменную из расчета строки, скажем rowSums , как показано ниже

 iris %>% 
  mutate_(sumVar = 
            iris %>% 
            select(Sepal.Length:Petal.Width) %>%
            rowSums)
 

в результате «sumVar» усекается до своего первого значения(10.2):

 Source: local data frame [150 x 6]
Groups: <by row>

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species sumVar
1           5.1         3.5          1.4         0.2  setosa   10.2
2           4.9         3.0          1.4         0.2  setosa   10.2
3           4.7         3.2          1.3         0.2  setosa   10.2
4           4.6         3.1          1.5         0.2  setosa   10.2
5           5.0         3.6          1.4         0.2  setosa   10.2
6           5.4         3.9          1.7         0.4  setosa   10.2
..
Warning message:
Truncating vector to length 1 
 

Следует ли его rowwise применять? Или какой правильный глагол использовать в такого рода расчетах.

Редактировать:

Более конкретно, есть ли какой-либо способ реализовать встроенную пользовательскую функцию с dplyr помощью ?

Мне интересно, можно ли сделать что-то вроде:

 iris %>% 
  mutate(sumVar = colsum_function(Sepal.Length:Petal.Width))
 

Комментарии:

1. Действительно странно, что iris %>% select(Sepal.Length:Petal.Width) %>% rowSums() это работает нормально, но iris %>% mutate(sumVar = iris %>% select(Sepal.Length:Petal.Width) %>% rowSums()) выдает сообщение «Ошибка: плохие индексы 1» предупреждение.

2. Я пытаюсь работать над этим с разными подходами, но эта ошибка появляется очень часто . (иногда я тоже делаю что-то глупое).

3.Для подобных операций sum , у которых уже есть эффективная векторизованная альтернатива по строкам, в настоящее время правильный способ: df %>% mutate(total = rowSums(across(where(is.numeric)))) across может взять все, что select может (например rowSums(across(Sepal.Length:Petal.Width)) , также работает). Смотрите полный рассказ о том, как по рядам и поперек


Ответ №1:

Это скорее обходной путь, но его можно использовать

 iris %>% mutate(sumVar = rowSums(.[1:4]))
 

Как написано в комментариях, вы также можете использовать select внутри mutate, чтобы получить столбцы, которые вы хотите подвести итог, например

 iris %>% 
  mutate(sumVar = rowSums(select(., contains("Sepal")))) %>% 
  head 
 

или

 iris %>% 
  mutate(sumVar = select(., contains("Sepal")) %>% rowSums()) %>% 
  head
 

Комментарии:

1. Какую версию dplyr вы используете? Когда я пытаюсь привести ваш пример dplyr_0.4.1 , я получаю исключение: Error in is.data.frame(x) : object '.' not found .

2. Если это кому-то полезно, причина, по которой я получал ошибку Error in is.data.frame(x) : object '.' not found , заключалась в том, что у меня была старая версия magrittr . Когда я обновился с magrittr_1.0.1 до magrittr_1.5 , все работало нормально.

3. @Конрад, ты мог бы сделать что-то вроде iris %>% mutate(sumVar = rowSums(select(., contains("Sepal")))) %>% head или iris %>% mutate(sumVar = select(., contains("Sepal")) %>% rowSums()) %>% head

4. Комментарий @docendodiscimus действительно должен быть другим (с правом голоса) ответом. Это самое надежное решение в стиле dplyr.

5. Приятно, что это работает, хотя Хэдли говорит, что подобное решение «работает по совпадению, а не по замыслу. Я бы не стал на это полагаться». Но, может быть, сейчас это поддерживается? Кто-нибудь знает? github.com/tidyverse/dplyr/issues/2050

Ответ №2:

Вы можете использовать rowwise() функцию:

 iris %>% 
  rowwise() %>% 
  mutate(sumVar = sum(c_across(Sepal.Length:Petal.Width)))

#> # A tibble: 150 x 6
#> # Rowwise: 
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sumVar
#>           <dbl>       <dbl>        <dbl>       <dbl> <fct>    <dbl>
#>  1          5.1         3.5          1.4         0.2 setosa    10.2
#>  2          4.9         3            1.4         0.2 setosa     9.5
#>  3          4.7         3.2          1.3         0.2 setosa     9.4
#>  4          4.6         3.1          1.5         0.2 setosa     9.4
#>  5          5           3.6          1.4         0.2 setosa    10.2
#>  6          5.4         3.9          1.7         0.4 setosa    11.4
#>  7          4.6         3.4          1.4         0.3 setosa     9.7
#>  8          5           3.4          1.5         0.2 setosa    10.1
#>  9          4.4         2.9          1.4         0.2 setosa     8.9
#> 10          4.9         3.1          1.5         0.1 setosa     9.6
#> # ... with 140 more rows
 

« c_across() использует аккуратный синтаксис выбора, чтобы вы могли кратко выбрать множество переменных»‘

Наконец, если вы хотите, вы можете использовать %>% ungroup в конце для выхода из rowwise.

Комментарии:

1.Для подобных операций sum , у которых уже есть эффективная векторизованная альтернатива по строкам, в настоящее время правильный способ: df %>% mutate(total = rowSums(across(where(is.numeric)))) across может взять все, что select может (например rowSums(across(Sepal.Length:Petal.Width)) , также работает). Прокрутите виньетку по строкам вниз, чтобы найти это и взглянуть на

Ответ №3:

Более сложным способом было бы:

  iris %>% select(Sepal.Length:Petal.Width) %>%
mutate(sumVar = rowSums(.)) %>% left_join(iris)
 

Комментарии:

1. Спасибо, Давид. left_join звучит неплохо, если использовать его с by ключом; однако для этого обстоятельства он не настолько надежен и интуитивно понятен

2. Я также беспокоюсь, что автоматический выбор параметра «по» в соединении может вызвать некоторые проблемы.. в некоторых строках столбцы могут содержать неуникальные значения ..

Ответ №4:

Добавление комментария @docendodiscimus в качестве ответа. 1 к нему!

 iris %>% mutate(sumVar = rowSums(select(., contains("Sepal"))))
 

Ответ №5:

Я использую это простое решение, которое является более надежной модификацией ответа Давиде Пассаретти:

 iris %>% select(Sepal.Length:Petal.Width) %>%
  transmute(sumVar = rowSums(.)) %>% bind_cols(iris, .)
 

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

Комментарии:

1. Привет, не могли бы вы, пожалуйста, объяснить мне значение точки между круглыми скобками? rowSums(.)

Ответ №6:

Вы также можете использовать grep вместо contains или matches , на всякий случай, если вам нужно пофантазировать с регулярными выражениями ( matches по моему опыту, это не очень похоже на негативные взгляды и тому подобное).

 iris %>% mutate(sumVar = rowSums(select(., grep("Sepal", names(.)))))