ความถี่สัมพัทธ์ / สัดส่วนด้วย dplyr

สมมติว่าฉันต้องการคำนวณสัดส่วนของค่าต่างๆ ภายในแต่ละกลุ่ม ตัวอย่างเช่น การใช้ข้อมูล mtcars ฉันจะคำนวณความถี่ สัมพัทธ์ ของจำนวน เกียร์ ด้วย am (อัตโนมัติ/แมนนวล) ในครั้งเดียวได้อย่างไร กับ dplyr?

library(dplyr)
data(mtcars)
mtcars <- tbl_df(mtcars)

# count frequency
mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n())

# am gear  n
#  0    3 15 
#  0    4  4 
#  1    4  8  
#  1    5  5 

สิ่งที่ฉันต้องการบรรลุ:

am gear  n rel.freq
 0    3 15      0.7894737
 0    4  4      0.2105263
 1    4  8      0.6153846
 1    5  5      0.3846154

person jenswirf    schedule 04.07.2014    source แหล่งที่มา
comment
เปอร์เซ็นต์เหล่านี้เป็นตัวเลขจริงที่คุณต้องการหรือไม่? พวกเขามาจากไหนในเชิงพีชคณิต? อ๋อ 79% คือ 15/(15+4) 21% คือ 4/(15+4) แล้วสำหรับ am==1 62% คือ 8/(8+5) เป็นต้น เข้าใจแล้ว   -  person Spacedman    schedule 04.07.2014
comment
@Spacedman ใช่ นั่นคือหมายเลขที่ฉันต้องการและ Frank ถูกต้อง พวกเขารวมเป็น 100% โดยตัวแปร am (79+21) และ (62+38)   -  person jenswirf    schedule 04.07.2014
comment
ดูเหมือนว่าจะกำลังมองหาการใช้งาน dplyr ดั้งเดิมของ prop.table()/sweep() นอกจากนี้ ในคำถามอื่น ๆ บางคน ขอ ตัวเลือกในการรวมจำนวนศูนย์สำหรับตัวแปรหรือการโต้ตอบของตัวแปร   -  person smci    schedule 27.04.2016


คำตอบ (10)


ลองสิ่งนี้:

mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n()) %>%
  mutate(freq = n / sum(n))

#   am gear  n      freq
# 1  0    3 15 0.7894737
# 2  0    4  4 0.2105263
# 3  1    4  8 0.6153846
# 4  1    5  5 0.3846154

จากdplyr vignette : :

เมื่อคุณจัดกลุ่มตามตัวแปรหลายตัว สรุปแต่ละรายการจะหลุดออกจากการจัดกลุ่มหนึ่งระดับ ทำให้ง่ายต่อการสะสมชุดข้อมูลอย่างต่อเนื่อง

ดังนั้น หลังจาก summarise ตัวแปรการจัดกลุ่มสุดท้ายที่ระบุใน group_by 'เกียร์' จะถูกลอกออก ในขั้นตอน mutate ข้อมูลจะถูกจัดกลุ่มตามตัวแปรการจัดกลุ่มที่เหลือ ซึ่งก็คือ 'am' คุณสามารถตรวจสอบการจัดกลุ่มในแต่ละขั้นตอนด้วย groups

แน่นอนว่าผลลัพธ์ของการลอกนั้นขึ้นอยู่กับลำดับของตัวแปรการจัดกลุ่มในการเรียก group_by คุณอาจต้องการดำเนินการ group_by(am) ครั้งต่อไปเพื่อทำให้โค้ดของคุณชัดเจนยิ่งขึ้น

สำหรับการปัดเศษและการทำให้สวยงาม โปรดดูคำตอบที่ดีโดย @Tyler Rinker

person Henrik    schedule 04.07.2014
comment
ฉันเพิ่งค้นพบวิธีแก้ปัญหานั้นเช่นกัน แต่ฉันไม่รู้ว่าทำไม sum(n) จึงทำงานบนกลุ่ม am และไม่ใช่กลุ่ม gear ด้วย... - person Spacedman; 04.07.2014
comment
ดูบทความสั้น: เมื่อคุณจัดกลุ่มตามตัวแปรหลายตัว แต่ละตัวแปร สรุปจะลอกการจัดกลุ่มออกไปหนึ่งระดับ - person Henrik; 04.07.2014
comment
เยี่ยมเลย - ถ้าคุณหยุดตาม summarise มันจะบอกว่ากลุ่มไหนยังเหลืออยู่ โอ้ ดีพลีร์ร็อค... - person Spacedman; 04.07.2014
comment
เรียบง่ายและชัดเจน ฉันไม่เคยรู้จักทฤษฎีนี้มาก่อนเลย ขอบคุณ! - person Shixiang Wang; 07.07.2019
comment
ดี. ง่ายและมีประสิทธิภาพ เยี่ยมมาก! - person user2550228; 06.07.2020

คุณสามารถใช้ฟังก์ชัน count() ซึ่งจะมีลักษณะการทำงานที่แตกต่างกันไป ขึ้นอยู่กับเวอร์ชันของ dplyr:

  • dplyr 0.7.1: ส่งคืนตาราง ungrouped: คุณต้องจัดกลุ่มอีกครั้งด้วย am

  • dplyr ‹ 0.7.1: ส่งคืนตาราง จัดกลุ่ม ดังนั้นจึงไม่จำเป็นต้องจัดกลุ่มอีกครั้ง แม้ว่าคุณอาจต้องการ ungroup() สำหรับการดัดแปลงในภายหลัง

dplyr 0.7.1

mtcars %>%
  count(am, gear) %>%
  group_by(am) %>%
  mutate(freq = n / sum(n))

dplyr ‹ 0.7.1

mtcars %>%
  count(am, gear) %>%
  mutate(freq = n / sum(n))

ซึ่งส่งผลให้กลายเป็น ตารางที่จัดกลุ่ม หากคุณต้องการใช้สำหรับการวิเคราะห์เพิ่มเติม การลบแอตทริบิวต์ จัดกลุ่ม ที่มี ungroup() ออกอาจเป็นประโยชน์

person Matifou    schedule 08.08.2016
comment
ดูเหมือนว่าคำตอบที่ไม่ถูกต้องใน dplyr 0.7.1 โดยจะคำนวณความถี่โดยรวมบนเกียร์ แทนที่จะคำนวณภายในแต่ละระดับของเวลา am - person Edwin; 19.07.2017

@Henrik's ดีกว่าสำหรับการใช้งานเพราะจะทำให้อักขระคอลัมน์ไม่ใช่ตัวเลขอีกต่อไป แต่ตรงกับสิ่งที่คุณขอ ...

mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n()) %>%
  mutate(rel.freq = paste0(round(100 * n/sum(n), 0), "%"))

##   am gear  n rel.freq
## 1  0    3 15      79%
## 2  0    4  4      21%
## 3  1    4  8      62%
## 4  1    5  5      38%

แก้ไข เพราะ Spacedman ถามหา :-)

as.rel_freq <- function(x, rel_freq_col = "rel.freq", ...) {
    class(x) <- c("rel_freq", class(x))
    attributes(x)[["rel_freq_col"]] <- rel_freq_col
    x
}

print.rel_freq <- function(x, ...) {
    freq_col <- attributes(x)[["rel_freq_col"]]
    x[[freq_col]] <- paste0(round(100 * x[[freq_col]], 0), "%")   
    class(x) <- class(x)[!class(x)%in% "rel_freq"]
    print(x)
}

mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n()) %>%
  mutate(rel.freq = n/sum(n)) %>%
  as.rel_freq()

## Source: local data frame [4 x 4]
## Groups: am
## 
##   am gear  n rel.freq
## 1  0    3 15      79%
## 2  0    4  4      21%
## 3  1    4  8      62%
## 4  1    5  5      38%
person Tyler Rinker    schedule 04.07.2014
comment
คุณสามารถสร้างคลาสเปอร์เซ็นต์ S3 ด้วยเมธอด format ที่เพิ่มเครื่องหมายเปอร์เซ็นต์... #overkill ได้เสมอ - person Spacedman; 04.07.2014
comment
การใช้สิ่งนี้ก็น่าสนใจเช่นกัน: stackoverflow.com/questions/13483430/ - person Spacedman; 04.07.2014
comment
จะเป็นอย่างไรหากใครจะคำนวณค่าเฉลี่ย sd และ SE ในตัวอย่างนี้ด้วย? - person user3655531; 16.01.2017

ฉันเขียนฟังก์ชันเล็ก ๆ สำหรับงานทำซ้ำนี้:

count_pct <- function(df) {
  return(
    df %>%
      tally %>% 
      mutate(n_pct = 100*n/sum(n))
  )
}

ฉันสามารถใช้มันได้เช่น:

mtcars %>% 
  group_by(cyl) %>% 
  count_pct

มันกลับมา:

# A tibble: 3 x 3
    cyl     n n_pct
  <dbl> <int> <dbl>
1     4    11  34.4
2     6     7  21.9
3     8    14  43.8
person slhck    schedule 18.03.2019

แม้จะมีคำตอบมากมาย แต่ก็มีอีกวิธีหนึ่งที่ใช้ prop.table ร่วมกับ dplyr หรือ data.table

library("dplyr")
mtcars %>%
    group_by(am, gear) %>%
    summarise(n = n()) %>%
    mutate(freq = prop.table(n))

library("data.table")
cars_dt <- as.data.table(mtcars)
cars_dt[, .(n = .N), keyby = .(am, gear)][, freq := prop.table(n) , by = "am"]
person TimTeaFan    schedule 30.07.2019
comment
โดยวิธีการที่ง่ายที่สุด - person Parseltongue; 25.01.2020

นี่คือฟังก์ชันทั่วไปที่ใช้โซลูชันของ Henrik บน dplyr 0.7.1

freq_table <- function(x, 
                       group_var, 
                       prop_var) {
  group_var <- enquo(group_var)
  prop_var  <- enquo(prop_var)
  x %>% 
    group_by(!!group_var, !!prop_var) %>% 
    summarise(n = n()) %>% 
    mutate(freq = n /sum(n)) %>% 
    ungroup
}
person Edwin    schedule 19.07.2017
comment
Error in bind_rows_(x, .id) : Column am` ไม่สามารถแปลงจากตัวเลขเป็นอักขระได้ - person f0nzie; 07.08.2018

นี่คือคำตอบฐาน R โดยใช้ aggregate และ ave :

df1 <- with(mtcars, aggregate(list(n = mpg), list(am = am, gear = gear), length))
df1$prop <- with(df1, n/ave(n, am, FUN = sum))
#Also with prop.table
#df1$prop <- with(df1, ave(n, am, FUN = prop.table))
df1

#  am gear  n      prop
#1  0    3 15 0.7894737
#2  0    4  4 0.2105263
#3  1    4  8 0.6153846
#4  1    5  5 0.3846154 

เรายังสามารถใช้ prop.table ได้ แต่ผลลัพธ์จะแสดงแตกต่างออกไป

prop.table(table(mtcars$am, mtcars$gear), 1)
   
#            3         4         5
#  0 0.7894737 0.2105263 0.0000000
#  1 0.0000000 0.6153846 0.3846154
person Ronak Shah    schedule 19.09.2020

เพื่อความสมบูรณ์ของคำถามยอดนิยมนี้ เนื่องจากเวอร์ชัน 1.0.0 ของ dplyr พารามิเตอร์ .groups จะควบคุมโครงสร้างการจัดกลุ่มของฟังก์ชัน summarise หลัง group_by ความช่วยเหลือในการสรุป

ด้วย .groups = "drop_last", summarise จะลดระดับการจัดกลุ่มสุดท้าย นี่เป็นผลลัพธ์เดียวที่ได้รับก่อนเวอร์ชัน 1.0.0

library(dplyr)
library(scales)

original <- mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n()) %>%
  mutate(rel.freq =  scales::percent(n/sum(n), accuracy = 0.1))
#> `summarise()` regrouping output by 'am' (override with `.groups` argument)

original
#> # A tibble: 4 x 4
#> # Groups:   am [2]
#>      am  gear     n rel.freq
#>   <dbl> <dbl> <int> <chr>   
#> 1     0     3    15 78.9%   
#> 2     0     4     4 21.1%   
#> 3     1     4     8 61.5%   
#> 4     1     5     5 38.5%

new_drop_last <- mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n(), .groups = "drop_last") %>%
  mutate(rel.freq =  scales::percent(n/sum(n), accuracy = 0.1))

dplyr::all_equal(original, new_drop_last)
#> [1] TRUE

ด้วย .groups = "drop" การจัดกลุ่มทุกระดับจะถูกยกเลิก ผลลัพธ์กลายเป็น tibble อิสระไร้ร่องรอยของ group_by ก่อนหน้า

# .groups = "drop"
new_drop <- mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n(), .groups = "drop") %>%
  mutate(rel.freq =  scales::percent(n/sum(n), accuracy = 0.1))

new_drop
#> # A tibble: 4 x 4
#>      am  gear     n rel.freq
#>   <dbl> <dbl> <int> <chr>   
#> 1     0     3    15 46.9%   
#> 2     0     4     4 12.5%   
#> 3     1     4     8 25.0%   
#> 4     1     5     5 15.6%

ถ้า .groups = "keep" โครงสร้างการจัดกลุ่มเดียวกันกับ .data (mtcars ในกรณีนี้) summarise จะไม่ลอกตัวแปรใดๆ ที่ใช้ใน group_by ออก

สุดท้ายด้วย .groups = "rowwise" แต่ละแถวจะมีกลุ่มของตัวเอง ก็เท่ากับเก็บเอาไว้ในสถานการณ์นี้

# .groups = "keep"
new_keep <- mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n(), .groups = "keep") %>%
  mutate(rel.freq =  scales::percent(n/sum(n), accuracy = 0.1))

new_keep
#> # A tibble: 4 x 4
#> # Groups:   am, gear [4]
#>      am  gear     n rel.freq
#>   <dbl> <dbl> <int> <chr>   
#> 1     0     3    15 100.0%  
#> 2     0     4     4 100.0%  
#> 3     1     4     8 100.0%  
#> 4     1     5     5 100.0%

# .groups = "rowwise"
new_rowwise <- mtcars %>%
  group_by (am, gear) %>%
  summarise (n=n(), .groups = "rowwise") %>%
  mutate(rel.freq =  scales::percent(n/sum(n), accuracy = 0.1))

dplyr::all_equal(new_keep, new_rowwise)
#> [1] TRUE

อีกประเด็นที่น่าสนใจก็คือ บางครั้งหลังจากใช้ group_by และ summarise แล้ว บรรทัดสรุปก็สามารถช่วยได้

# create a subtotal line to help readability
subtotal_am <- mtcars %>%
  group_by (am) %>% 
  summarise (n=n()) %>%
  mutate(gear = NA, rel.freq = 1)
#> `summarise()` ungrouping output (override with `.groups` argument)

mtcars %>% group_by (am, gear) %>%
  summarise (n=n()) %>% 
  mutate(rel.freq = n/sum(n)) %>%
  bind_rows(subtotal_am) %>%
  arrange(am, gear) %>%
  mutate(rel.freq =  scales::percent(rel.freq, accuracy = 0.1))
#> `summarise()` regrouping output by 'am' (override with `.groups` argument)
#> # A tibble: 6 x 4
#> # Groups:   am [2]
#>      am  gear     n rel.freq
#>   <dbl> <dbl> <int> <chr>   
#> 1     0     3    15 78.9%   
#> 2     0     4     4 21.1%   
#> 3     0    NA    19 100.0%  
#> 4     1     4     8 61.5%   
#> 5     1     5     5 38.5%   
#> 6     1    NA    13 100.0%

สร้างเมื่อวันที่ 11-09-2020 โดยแพ็คเกจ reprex (v0.3.0)

หวังว่าคุณจะพบคำตอบนี้มีประโยชน์

person josep maria porrà    schedule 09.11.2020

คำตอบนี้ขึ้นอยู่กับคำตอบของ Matifou

ก่อนอื่นฉันแก้ไขเพื่อให้แน่ใจว่าฉันจะไม่ได้รับคอลัมน์ความถี่ที่ส่งคืนเป็นคอลัมน์สัญกรณ์ทางวิทยาศาสตร์โดยใช้ตัวเลือก scipen

จากนั้นฉันคูณคำตอบด้วย 100 เพื่อให้ได้เปอร์เซ็นต์แทนที่จะเป็นทศนิยมเพื่อทำให้คอลัมน์ความถี่อ่านเป็นเปอร์เซ็นต์ได้ง่ายขึ้น

getOption("scipen") 
options("scipen"=10) 
mtcars %>%
count(am, gear) %>% 
mutate(freq = (n / sum(n)) * 100)
person Jazzmine    schedule 22.12.2016

นอกจากนี้ ให้ลอง add_count() (เพื่อเลี่ยง group_by .groups ที่น่ารำคาญ)

`mtcars %>% 
  count(am, gear) %>% 
  add_count(am, wt = n) %>% 
  mutate(pct = n / nn)`
person Dan Gustafsson    schedule 04.03.2021