Сума по кількох стовпцях за допомогою dplyr


98

Моє запитання передбачає підсумовування значень у кількох стовпцях кадру даних та створення нового стовпця, що відповідає цьому підсумовуванню dplyr. Записи даних у стовпцях є двійковими (0,1). Я думаю про рядовий аналог функції summarise_eachабо mutate_eachфункції dplyr. Нижче наведено мінімальний приклад кадру даних:

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

> df
   x1 x2 x3 x4 x5
1   1  1  0  1  1
2   0  1  1  0  1
3   0 NA  0 NA NA
4  NA  1  1  1  1
5   0  1  1  0  1
6   1  0  0  0  1
7   1 NA NA NA NA
8  NA NA NA  0  1
9   0  0  0  0  0
10  1  1  1  1  1

Я міг би використати щось на зразок:

df <- df %>% mutate(sumrow= x1 + x2 + x3 + x4 + x5)

але це передбачало б виписування назв кожної з колонок. У мене близько 50 колонок. Крім того, імена стовпців змінюються на різних ітераціях циклу, в якому я хочу реалізувати цю операцію, тому я хотів би спробувати уникнути необхідності вказувати будь-які імена стовпців.

Як я можу зробити це найбільш ефективно? Будь-яка допомога буде вдячна.


11
Чому dplyr? Чому б не просто простий df$sumrow <- rowSums(df, na.rm = TRUE)з основи R? Або df$sumrow <- Reduce(`+`, df)якщо ви хочете повторити саме те, що зробили dplyr.
Девід Аренбург

7
Ви можете зробити і те, і інше, dplyrяк у df %>% mutate(sumrow = Reduce(`+`, .))абоdf %>% mutate(sumrow = rowSums(.))
Девід Аренбург

2
Оновіть до останньої dplyrверсії, і вона запрацює.
Девід Аренбург

1
Пропозиції Девіда Аренбурга спрацювали після оновлення пакету dplyr @DavidArenburg
amo

1
Коментар @boern Девіда Аренбурга був найкращою відповіддю та найпрямішим рішенням. Ваша відповідь буде працювати, але вона передбачає додатковий крок заміни значень NA на нуль, що в деяких випадках може бути непридатним.
amo

Відповіді:


112

Як на рахунок

підсумуйте кожну колонку

df %>%
   replace(is.na(.), 0) %>%
   summarise_all(funs(sum))

підведіть підсумок кожного рядка

df %>%
   replace(is.na(.), 0) %>%
   mutate(sum = rowSums(.[1:5]))

8
summarise_eachсуми вниз уздовж кожного стовпця в той час як то , що потрібно , це сума по кожному рядку
AMO

1
Я намагаюся досягти того ж, але мій DF має стовпець, який є символом, отже, я не можу підсумувати всі стовпці. Думаю, мені слід змінити (.[1:5])частину, але, на жаль, я не знайомий із синтаксисом і не знаю, як шукати допомогу щодо нього. Намагався, mutate(sum = rowSums(is.numeric(.)))але не працював.
ccamara

5
Розумію. Ви df %>% replace(is.na(.), 0) %>% select_if(is.numeric) %>% summarise_each(funs(sum))можете спробувати?
Боерн,

2
Використовуйте summarise_allзамість того, summarise_eachяк було застарілим.
hmhensen,

2
Синтаксис mutate(sum = rowSums(.[,-1]))може стати в нагоді, якщо ви не знаєте, з якою кількістю стовпців вам потрібно мати справу.
Паулу С. Абреу

33

Якщо ви хочете підсумувати лише певні стовпці, я б використав щось на зразок цього:

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))
df %>% select(x3:x5) %>% rowSums(na.rm=TRUE) -> df$x3x5.total
head(df)

Таким чином ви можете використовувати dplyr::selectсинтаксис.


Мені подобається такий підхід, ніж інші, оскільки він не вимагає примушення НС до 0
Майкл Беллхаус

І краще, ніж grep, тому що легше мати справу з такими речами, як x4: x11
Дов Розенберг,

32

Я б використовував відповідність регулярних виразів для підсумовування змінних з певними іменами шаблонів. Наприклад:

df <- df %>% mutate(sum1 = rowSums(.[grep("x[3-5]", names(.))], na.rm = TRUE),
                    sum_all = rowSums(.[grep("x", names(.))], na.rm = TRUE))

Таким чином, ви можете створити більше однієї змінної як суму певної групи змінних вашого кадру даних.


чудове рішення! Я шукав певну функцію dplyr, яка робила це в останніх випусках, але не міг знайти
agenis

Це рішення чудове. Якщо є стовпці, які ви не хочете включати, вам просто потрібно розробити оператор grep (), щоб вибрати стовпці, що відповідають певному шаблону.
Трентон Хоффман,

1
@TrentonHoffman ось біт скасування виділення стовпців певного шаблону. просто потрібен -знак:rowSums(.[-grep("x[3-5]", names(.))], na.rm = TRUE)
alexb523

22

Я часто стикаюся з цією проблемою, і найпростіший спосіб зробити це - використовувати apply()функцію в mutateкоманді.

library(tidyverse)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

df %>%
  mutate(sum = select(., x1:x5) %>% apply(1, sum, na.rm=TRUE))

Тут ви можете використовувати все, що завгодно, щоб вибрати стовпці, використовуючи стандартні dplyrтрюки (наприклад, starts_with()або contains()). Роблячи всю роботу в межах однієї mutateкоманди, ця дія може відбуватися де завгодно в межах dplyrпотоку кроків обробки. Нарешті, використовуючи apply()функцію, ви можете гнучко використовувати будь-який підсумок, який вам потрібен, включаючи власну функцію підсумовування.

В іншому випадку, якщо ідея використання функції, яка не має приріст, є непривабливою, тоді ви можете зібрати стовпці, підсумувати їх і, нарешті, приєднати результат назад до вихідного кадру даних.

df <- df %>% mutate( id = 1:n() )   # Need some ID column for this to work

df <- df %>%
  group_by(id) %>%
  gather('Key', 'value', starts_with('x')) %>%
  summarise( Key.Sum = sum(value) ) %>%
  left_join( df, . )

Тут я використовував starts_with()функцію для вибору стовпців і обчислював суму, і ви можете робити що завгодно зі NAзначеннями. Недоліком цього підходу є те, що, хоча він досить гнучкий, він насправді не вписується в dplyrпотік етапів очищення даних.


3
Здається безглуздим у використанні, applyколи саме для цього rowSumsбуло розроблено.
zacdav

6
У цьому випадку це rowSumsпрацює дуже добре rowMeans, але я завжди почувався трохи дивно, задаючись питанням: "А якщо річ, яку мені потрібно обчислити, не є сумою чи середнім значенням?" Однак у 99% випадків мені доводиться робити щось подібне, це або сума, або середнє значення, тому, можливо, додаткова гнучкість у використанні загальної applyфункції не виправдана.
Дерек Сондереггер

22

Використання reduce()from purrrтрохи швидше, ніж, rowSumsбезумовно, швидше, ніж apply, оскільки ви уникаєте ітерації по всіх рядках і просто користуєтеся перевагами векторизованих операцій:

library(purrr)
library(dplyr)
iris %>% mutate(Petal = reduce(select(., starts_with("Petal")), `+`))

Дивіться це щодо термінів


Мені це подобається, але як би ви зробили це, коли вам потрібноna.rm = TRUE
див24

@ see24 Я не впевнений, що знаю, що ти маєш на увазі. Це підсумовує вектори a + b + c, всі однакової довжини. Оскільки кожен вектор може мати або не мати НА в різних місцях, ви не можете їх ігнорувати. Це зробило б вектори незрівнянними. Якщо ви хочете , щоб видалити значення NA ви повинні зробити це потім з, наприклад, drop_na
SKD

У підсумку я зробив це, rowSums(select(., matches("myregex")) , na.rm = TRUE))тому що саме це мені потрібно було з точки зору ігнорування НС. Отже, якщо цифри є sum(NA, 5)результатом 5. Але ви сказали, що зменшити краще, ніж rowSumsтому, мені було цікаво, чи є спосіб використовувати його в цій ситуації?
див.

Розумію. Якщо ви хочете суму і ігнорувати значення NA остаточно, rowSumsверсія, мабуть, найкраща. Головний недолік полягає в тому , що доступні лише rowSumsі rowMeansдоступні (це трохи повільніше, ніж зменшення, але не набагато). Якщо вам потрібно виконати іншу операцію (не суму), то reduceверсія, мабуть, єдиний варіант. Тільки уникайте використання applyв цьому випадку.
skd

2

У новіших версіях dplyrви можете використовувати rowwise()поряд з c_acrossдля виконання агрегування по рядках для функцій, які не мають конкретних варіантів по рядках, але якщо варіант із рядками існує, це має бути швидше.

Оскільки rowwise()це лише спеціальна форма групування та зміна способу роботи дієслів, вам, швидше за все, захочеться спрямувати його, ungroup()виконавши свою операцію по рядках.

Щоб вибрати діапазон рядків:

df %>%
  dplyr::rowwise() %>% 
  dplyr::mutate(sumrange = sum(dplyr::c_across(x1:x5), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

Щоб вибрати рядки за типом:

df %>%
  dplyr::rowwise() %>% 
  dplyr::mutate(sumnumeric = sum(c_across(where(is.numeric)), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

У вашому конкретному випадку існує варіант із рядками, тому ви можете зробити наступне (зверніть увагу на використання acrossзамість цього):

df %>%
  dplyr::mutate(sumrow = rowSums(dplyr::across(x1:x5), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

Для отримання додаткової інформації див. Сторінку на rowwise .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.