รวมในหลายคอลัมน์ด้วย dplyr


98

dplyrคำถามของฉันที่เกี่ยวข้องกับข้อสรุปถึงค่าในหลายคอลัมน์ของกรอบข้อมูลและการสร้างคอลัมน์ใหม่ที่สอดคล้องกับผลบวกนี้โดยใช้ รายการข้อมูลในคอลัมน์เป็นไบนารี (0,1) ฉันคิดของอนาล็อกแถวที่ชาญฉลาดของsummarise_eachหรือการทำงานของmutate_each dplyrด้านล่างนี้เป็นตัวอย่างขั้นต่ำของ data frame:

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
David Arenburg

7
คุณสามารถทำได้ด้วยdplyrเช่นกันdf %>% mutate(sumrow = Reduce(`+`, .))หรือdf %>% mutate(sumrow = rowSums(.))
David Arenburg

2
อัปเดตเป็นdplyrเวอร์ชันล่าสุดและจะใช้งานได้
David Arenburg

1
ข้อเสนอแนะโดย David Arenburg ใช้งานได้หลังจากอัปเดตแพ็คเกจ dplyr @DavidArenburg
amo

1
ความคิดเห็นของ @boern David Arenburgs เป็นคำตอบที่ดีที่สุดและเป็นทางออกที่ตรงประเด็นที่สุด คำตอบของคุณใช้ได้ผล แต่เกี่ยวข้องกับขั้นตอนเพิ่มเติมในการแทนที่ค่า 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]))อาจมีประโยชน์หากคุณไม่ทราบจำนวนคอลัมน์ที่ต้องจัดการ
Paulo S. Abreu

32

หากคุณต้องการรวมบางคอลัมน์เท่านั้นฉันจะใช้สิ่งนี้:

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ไวยากรณ์ของ


ฉันชอบแนวทางนี้เหนือคนอื่น ๆ เพราะไม่ต้องบังคับ NAs ถึง 0
Michael Bellhouse

และดีกว่า grep เพราะจัดการกับสิ่งต่างๆได้ง่ายกว่าเช่น x4: x11
Dov Rosenberg

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 () เพื่อเลือกคอลัมน์ที่ตรงกับรูปแบบเฉพาะ
Trenton Hoffman

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ฟังก์ชันทั่วไปจะไม่ได้รับการแก้ไข
Derek Sonderegger

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 ในตำแหน่งที่ต่างกันคุณจึงไม่สามารถเพิกเฉยได้ สิ่งนี้จะทำให้เวกเตอร์ไม่ตรงแนว หากคุณต้องการลบค่า NA คุณต้องดำเนินการในภายหลังเช่น drop_na
skd

ฉันลงเอยด้วยการทำrowSums(select(., matches("myregex")) , na.rm = TRUE))เพราะนั่นคือสิ่งที่ฉันต้องการในแง่ของการละเว้น NAs ดังนั้นถ้าตัวเลขเป็นsum(NA, 5)ผลลัพธ์คือ 5 แต่คุณบอกว่าการลดดีกว่าrowSumsฉันเลยสงสัยว่ามีวิธีใช้ในสถานการณ์นี้หรือไม่?
24

ฉันเห็น. หากคุณต้องการผลรวมและละเว้นค่า NA แน่นอนrowSumsเวอร์ชันน่าจะดีที่สุด ข้อเสียเปรียบหลักคือมีเพียงrowSumsและrowMeansมีอยู่ (ช้ากว่าลดลงเล็กน้อย แต่ไม่มาก) หากคุณต้องการดำเนินการอื่น (ไม่ใช่ผลรวม) reduceเวอร์ชันอาจเป็นเพียงตัวเลือกเดียว เพียงหลีกเลี่ยงการใช้applyในกรณีนี้
skd

1

ในเวอร์ชันที่ใหม่กว่า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()

ในกรณีเฉพาะของคุณจะมีตัวแปรแบบ row-wise อยู่เพื่อให้คุณทำสิ่งต่อไปนี้ได้ (สังเกตการใช้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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.