วิธีหาผลรวมของตัวแปรตามกลุ่ม


357

ฉันมีกรอบข้อมูลที่มีสองคอลัมน์ คอลัมน์แรกมีหมวดหมู่เช่น "First", "Second", "Third" และคอลัมน์ที่สองมีตัวเลขที่แสดงถึงจำนวนครั้งที่ฉันเห็นกลุ่มเฉพาะจาก "หมวดหมู่"

ตัวอย่างเช่น:

Category     Frequency
First        10
First        15
First        5
Second       2
Third        14
Third        20
Second       3

ฉันต้องการเรียงลำดับข้อมูลตามหมวดหมู่และรวมความถี่ทั้งหมด:

Category     Frequency
First        30
Second       5
Third        34

ฉันจะทำสิ่งนี้ใน R ได้อย่างไร


1
วิธีที่เร็วที่สุดในฐาน R rowsumคือ
Michael M

คำตอบ:


387

การใช้aggregate:

aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum)
  Category  x
1    First 30
2   Second  5
3    Third 34

listในตัวอย่างข้างต้นหลายมิติสามารถระบุได้ใน สามารถรวมตัวชี้วัดหลายตัวที่รวบรวมประเภทข้อมูลเดียวกันได้ผ่านcbind:

aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...

(ฝังความคิดเห็น @thelememail @) aggregateมีอินเทอร์เฟซสูตรเช่นกัน

aggregate(Frequency ~ Category, x, sum)

หรือถ้าคุณต้องการรวมหลายคอลัมน์คุณสามารถใช้.สัญลักษณ์ (ใช้ได้กับหนึ่งคอลัมน์ด้วย)

aggregate(. ~ Category, x, sum)

หรือtapply:

tapply(x$Frequency, x$Category, FUN=sum)
 First Second  Third 
    30      5     34 

ใช้ข้อมูลนี้:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))

4
@AndrewMcKinlay, R ใช้เครื่องหมายตัวหนอนเพื่อกำหนดสูตรสัญลักษณ์สำหรับสถิติและฟังก์ชั่นอื่น ๆ มันสามารถตีความได้ว่า"รูปแบบความถี่ตามหมวดหมู่"หรือ"ความถี่ขึ้นอยู่กับหมวดหมู่" ไม่ใช่ทุกภาษาที่ใช้ตัวดำเนินการพิเศษเพื่อกำหนดฟังก์ชั่นสัญลักษณ์ดังที่ทำใน R ที่นี่ บางทีด้วย "การตีความภาษาธรรมชาติ" ของโอเปอร์เรเตอร์ตัวหนอนมันจะมีความหมายมากขึ้น ฉันเองพบว่าการแสดงสูตรสัญลักษณ์นี้ดีกว่าทางเลือกที่ละเอียดมากขึ้น
r2evans

1
ใหม่กับ R (และถามคำถามแบบเดียวกันกับ OP) ฉันจะได้ประโยชน์จากรายละเอียดเพิ่มเติมของไวยากรณ์ที่อยู่เบื้องหลังแต่ละทางเลือก ตัวอย่างเช่นหากฉันมีตารางแหล่งข้อมูลที่ใหญ่กว่าและต้องการเลือกเพียงสองมิติพร้อมกับตัวชี้วัดที่สรุปฉันสามารถปรับวิธีการใด ๆ เหล่านี้ได้หรือไม่ ยากที่จะบอก
Dodecaphone

236

คุณยังสามารถใช้แพ็คเกจdplyrเพื่อจุดประสงค์นั้นได้:

library(dplyr)
x %>% 
  group_by(Category) %>% 
  summarise(Frequency = sum(Frequency))

#Source: local data frame [3 x 2]
#
#  Category Frequency
#1    First        30
#2   Second         5
#3    Third        34

หรือสำหรับคอลัมน์สรุปหลายคอลัมน์ (ใช้ได้กับหนึ่งคอลัมน์ด้วย):

x %>% 
  group_by(Category) %>% 
  summarise_all(funs(sum))

นี่คือตัวอย่างเพิ่มเติมของการสรุปข้อมูลตามกลุ่มโดยใช้ฟังก์ชัน dplyr โดยใช้ชุดข้อมูลที่มีอยู่แล้วmtcars:

# several summary columns with arbitrary names
mtcars %>% 
  group_by(cyl, gear) %>%                            # multiple group columns
  summarise(max_hp = max(hp), mean_mpg = mean(mpg))  # multiple summary columns

# summarise all columns except grouping columns using "sum" 
mtcars %>% 
  group_by(cyl) %>% 
  summarise_all(sum)

# summarise all columns except grouping columns using "sum" and "mean"
mtcars %>% 
  group_by(cyl) %>% 
  summarise_all(funs(sum, mean))

# multiple grouping columns
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise_all(funs(sum, mean))

# summarise specific variables, not all
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise_at(vars(qsec, mpg, wt), funs(sum, mean))

# summarise specific variables (numeric columns except grouping columns)
mtcars %>% 
  group_by(gear) %>% 
  summarise_if(is.numeric, funs(mean))

สำหรับข้อมูลเพิ่มเติมรวมทั้ง%>%ผู้ประกอบการดูที่แนะนำให้รู้จักกับ dplyr


1
มันเร็วแค่ไหนเมื่อเปรียบเทียบกับ data.table และทางเลือกโดยรวมที่แสดงในคำตอบอื่น ๆ
asieira

5
@asieira ซึ่งเร็วที่สุดและมีความแตกต่างมากน้อยเพียงใด (หรือหากสังเกตเห็นความแตกต่าง) จะขึ้นอยู่กับขนาดข้อมูลของคุณเสมอ โดยทั่วไปแล้วสำหรับชุดข้อมูลขนาดใหญ่เช่น GB บางตัว data.table น่าจะเร็วที่สุด ในขนาดข้อมูลที่เล็กลง data.table และ dplyr มักจะปิดเช่นกันขึ้นอยู่กับจำนวนของกลุ่ม ทั้งข้อมูลตารางและ dplyr จะเร็วกว่าฟังก์ชั่นพื้นฐานค่อนข้างมาก (สามารถทำงานได้เร็วขึ้น 100-1,000 เท่าสำหรับการทำงานบางอย่าง) ดูที่นี่ด้วย
talat

1
"funs" หมายถึงอะไรในตัวอย่างที่สอง?
lauren.marietta

@ lauren.marietta คุณสามารถระบุฟังก์ชั่น (s) คุณต้องการนำไปใช้เป็นข้อมูลสรุปภายในfuns()ข้อโต้แย้งของsummarise_allและฟังก์ชั่นที่เกี่ยวข้อง ( summarise_at, summarise_if)
Talat

76

คำตอบที่ได้รับจาก rcs ใช้งานได้ง่าย อย่างไรก็ตามถ้าคุณจัดการชุดข้อมูลที่ใหญ่กว่าและต้องการการเพิ่มประสิทธิภาพก็เป็นทางเลือกที่เร็วกว่า:

library(data.table)
data = data.table(Category=c("First","First","First","Second","Third", "Third", "Second"), 
                  Frequency=c(10,15,5,2,14,20,3))
data[, sum(Frequency), by = Category]
#    Category V1
# 1:    First 30
# 2:   Second  5
# 3:    Third 34
system.time(data[, sum(Frequency), by = Category] )
# user    system   elapsed 
# 0.008     0.001     0.009 

ลองเปรียบเทียบกับสิ่งเดียวกันโดยใช้ data.frame และข้างบน:

data = data.frame(Category=c("First","First","First","Second","Third", "Third", "Second"),
                  Frequency=c(10,15,5,2,14,20,3))
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum))
# user    system   elapsed 
# 0.008     0.000     0.015 

และถ้าคุณต้องการเก็บคอลัมน์นี่คือไวยากรณ์:

data[,list(Frequency=sum(Frequency)),by=Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34

ความแตกต่างจะสังเกตเห็นได้ชัดเจนยิ่งขึ้นด้วยชุดข้อมูลขนาดใหญ่เนื่องจากรหัสด้านล่างแสดงให้เห็น:

data = data.table(Category=rep(c("First", "Second", "Third"), 100000),
                  Frequency=rnorm(100000))
system.time( data[,sum(Frequency),by=Category] )
# user    system   elapsed 
# 0.055     0.004     0.059 
data = data.frame(Category=rep(c("First", "Second", "Third"), 100000), 
                  Frequency=rnorm(100000))
system.time( aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum) )
# user    system   elapsed 
# 0.287     0.010     0.296 

สำหรับการรวมหลายครั้งคุณสามารถรวมlapplyและ.SDดังต่อไปนี้

data[, lapply(.SD, sum), by = Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34

13
+1 แต่ 0.296 กับ 0.059 นั้นไม่น่าประทับใจอย่างยิ่ง ขนาดข้อมูลจำเป็นต้องมีขนาดใหญ่กว่า 300k แถวและมีมากกว่า 3 กลุ่มเพื่อให้ data.table ส่องแสง เราจะลองและสนับสนุนแถวมากกว่า 2 พันล้านแถวเร็ว ๆ นี้เนื่องจากผู้ใช้ data.table บางรายมี RAM 250GB และ GNU R รองรับความยาว> 2 ^ 31
Matt Dowle

2
จริง กลับกลายเป็นว่าฉันไม่มี RAM ทั้งหมดและพยายามเพียงแค่ให้หลักฐานบางอย่างเกี่ยวกับประสิทธิภาพที่เหนือกว่าของ data.table ฉันแน่ใจว่าความแตกต่างจะยิ่งใหญ่ขึ้นด้วยข้อมูลเพิ่มเติม
asieira

1
ฉันมีการสังเกต 7 ล้าน dplyr ใช้เวลา. 3 วินาทีและมวลรวม () ใช้เวลา 22 วินาทีในการดำเนินการให้เสร็จ ฉันจะโพสต์ในหัวข้อนี้และคุณเอาชนะฉันมัน!
zazu

3
data[, sum(Frequency), by = Category]มีทางที่สั้นกว่าที่จะเขียนนี้อยู่ คุณสามารถใช้ฟังก์ชัน.Nที่ใช้แทนsum()ได้ data[, .N, by = Category]. นี่คือสูตร
Stophface

3
การใช้. N จะเท่ากับผลรวม (ความถี่) เฉพาะในกรณีที่ค่าทั้งหมดในคอลัมน์ความถี่เท่ากับ 1 เพราะ. N จะนับจำนวนแถวในแต่ละชุดรวม (.SD) และนั่นไม่ใช่กรณีที่นี่
asieira

41

คุณสามารถใช้ฟังก์ชั่นby () :

x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))

แพ็คเกจอื่น ๆ เหล่านั้น (plyr, reshape) มีประโยชน์ในการคืน data.frame แต่มันก็คุ้มค่าที่จะคุ้นเคยกับ () เพราะมันเป็นฟังก์ชั่นพื้นฐาน


28

หลายปีต่อมาเพียงเพิ่มโซลูชัน R พื้นฐานอื่นที่ไม่ได้อยู่ที่นี่ด้วยเหตุผลบางประการ - xtabs

xtabs(Frequency ~ Category, df)
# Category
# First Second  Third 
#    30      5     34 

หรือถ้าคุณต้องการdata.frameกลับ

as.data.frame(xtabs(Frequency ~ Category, df))
#   Category Freq
# 1    First   30
# 2   Second    5
# 3    Third   34


23

หากxเป็น dataframe กับข้อมูลของคุณแล้วสิ่งต่อไปนี้จะทำในสิ่งที่คุณต้องการ:

require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)

19

ในขณะที่ฉันเพิ่งได้รับการแปลงเป็นdplyrส่วนใหญ่ของการดำเนินการประเภทนี้sqldfแพคเกจยังคงดีจริงๆ (และ IMHO อ่านได้มากขึ้น) สำหรับบางสิ่ง

นี่คือตัวอย่างของวิธีการตอบคำถามนี้ sqldf

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                  "Third", "Third", "Second")), 
                Frequency=c(10,15,5,2,14,20,3))

sqldf("select 
          Category
          ,sum(Frequency) as Frequency 
       from x 
       group by 
          Category")

##   Category Frequency
## 1    First        30
## 2   Second         5
## 3    Third        34

18

เพียงเพิ่มตัวเลือกที่สาม:

require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)

แก้ไข: นี่เป็นคำตอบที่เก่ามาก ตอนนี้ฉันอยากจะแนะนำการใช้งานgroup_byและsummariseจากdplyrเช่นเดียวกับใน @docendo คำตอบ


7

ฉันหา aveมีประโยชน์มาก (และมีประสิทธิภาพ) เมื่อคุณต้องการใช้ฟังก์ชันการรวมที่แตกต่างกันในคอลัมน์ต่างๆ (และคุณต้อง / ต้องการติดบนฐาน R):

เช่น

รับข้อมูลนี้:

DF <-                
data.frame(Categ1=factor(c('A','A','B','B','A','B','A')),
           Categ2=factor(c('X','Y','X','X','X','Y','Y')),
           Samples=c(1,2,4,3,5,6,7),
           Freq=c(10,30,45,55,80,65,50))

> DF
  Categ1 Categ2 Samples Freq
1      A      X       1   10
2      A      Y       2   30
3      B      X       4   45
4      B      X       3   55
5      A      X       5   80
6      B      Y       6   65
7      A      Y       7   50

เราต้องการที่จะจัดกลุ่มตามCateg1และCateg2และคำนวณผลรวมของและค่าเฉลี่ยของSamples นี่คือทางออกที่เป็นไปได้โดยใช้:Freq
ave

# create a copy of DF (only the grouping columns)
DF2 <- DF[,c('Categ1','Categ2')]

# add sum of Samples by Categ1,Categ2 to DF2 
# (ave repeats the sum of the group for each row in the same group)
DF2$GroupTotSamples <- ave(DF$Samples,DF2,FUN=sum)

# add mean of Freq by Categ1,Categ2 to DF2 
# (ave repeats the mean of the group for each row in the same group)
DF2$GroupAvgFreq <- ave(DF$Freq,DF2,FUN=mean)

# remove the duplicates (keep only one row for each group)
DF2 <- DF2[!duplicated(DF2),]

ผลลัพธ์ :

> DF2
  Categ1 Categ2 GroupTotSamples GroupAvgFreq
1      A      X               6           45
2      A      Y               9           40
3      B      X               7           50
6      B      Y               6           65

6

dplyr::tally()ตอนนี้ที่เพิ่มเข้ามาเมื่อเร็ว ๆนี้ทำให้ง่ายขึ้นกว่าเดิม:

tally(x, Category)

Category     n
First        30
Second       5
Third        34

6

คุณสามารถใช้ฟังก์ชั่นgroup.sumจากแพคเกจ Rfast

Category <- Rfast::as_integer(Category,result.sort=FALSE) # convert character to numeric. R's as.numeric produce NAs.
result <- Rfast::group.sum(Frequency,Category)
names(result) <- Rfast::Sort(unique(Category)
# 30 5 34

Rfastมีฟังก์ชั่นกลุ่มมากมายและgroup.sumเป็นหนึ่งในนั้น


4

ใช้castแทนrecast(หมายเหตุ'Frequency'ตอนนี้'value')

df  <- data.frame(Category = c("First","First","First","Second","Third","Third","Second")
                  , value = c(10,15,5,2,14,20,3))

install.packages("reshape")

result<-cast(df, Category ~ . ,fun.aggregate=sum)

ที่จะได้รับ:

Category (all)
First     30
Second    5
Third     34

2

โซลูชันอื่นที่ส่งคืนผลรวมตามกลุ่มในเมทริกซ์หรือเฟรมข้อมูลและสั้นและเร็ว:

rowsum(x$Frequency, x$Category)

อย่างรวดเร็วและแน่นอน
jay.sf

0

ตั้งแต่dplyr 1.0.0ที่across()ฟังก์ชั่นสามารถนำมาใช้:

df %>%
 group_by(Category) %>%
 summarise(across(Frequency, sum))

  Category Frequency
  <chr>        <int>
1 First           30
2 Second           5
3 Third           34

หากสนใจตัวแปรหลายตัว:

df %>%
 group_by(Category) %>%
 summarise(across(c(Frequency, Frequency2), sum))

  Category Frequency Frequency2
  <chr>        <int>      <int>
1 First           30         55
2 Second           5         29
3 Third           34        190

และการเลือกตัวแปรโดยใช้ตัวช่วยเลือก:

df %>%
 group_by(Category) %>%
 summarise(across(starts_with("Freq"), sum))

  Category Frequency Frequency2 Frequency3
  <chr>        <int>      <int>      <dbl>
1 First           30         55        110
2 Second           5         29         58
3 Third           34        190        380

ข้อมูลตัวอย่าง:

df <- read.table(text = "Category Frequency Frequency2 Frequency3
                 1    First        10         10         20
                 2    First        15         30         60
                 3    First         5         15         30
                 4   Second         2          8         16
                 5    Third        14         70        140
                 6    Third        20        120        240
                 7   Second         3         21         42",
                 header = TRUE,
                 stringsAsFactors = FALSE)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.