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


153

สมมติว่าฉันต้องการคำนวณสัดส่วนของค่าต่าง ๆ ภายในแต่ละกลุ่ม ยกตัวอย่างเช่นการใช้mtcarsข้อมูลที่ฉันจะคำนวณญาติความถี่ของจำนวนเกียร์โดย (อัตโนมัติ / คู่มือ) ในหนึ่งไปด้วย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

1
เปอร์เซ็นต์เหล่านั้นเป็นตัวเลขจริงที่คุณต้องการหรือไม่? พวกเขามาจากไหนเกี่ยวกับพีชคณิต Ah, 79% คือ 15 / (15 + 4), 21% คือ 4 / (15 + 4) และสำหรับ am == 1 62% คือ 8 / (8 + 5) เป็นต้นรับไปแล้ว
Spacedman

1
@Spacedman ใช่ผู้ที่มีจำนวนที่ฉันต้องการและแฟรงก์ถูกต้องพวกเขารวมถึง 100% โดยนตัวแปร (79 + 21) และ (62 + 38) ..
jenswirf

2
นี้จริงๆดูเหมือนว่าจะมองหาการดำเนิน dplyr พื้นเมือง/prop.table() sweep()นอกจากนี้ในคำถามอื่น ๆ บางคนกำลังขอตัวเลือกที่จะรวมการนับเป็นศูนย์สำหรับตัวแปรหรือการโต้ตอบตัวแปร
smci

คำตอบ:


285

ลองสิ่งนี้:

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

จากบทความสั้น ๆ :

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

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

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

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


5
ฉันเพิ่งค้นพบวิธีการแก้ปัญหาที่มากเกินไป แต่ผมไม่ทราบว่าทำไมsum(n)ผลงานที่ผ่านมาamของกลุ่มและไม่ได้เป็นgearกลุ่มเกินไป ...
Spacedman

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

7
ดีมาก - ถ้าคุณหยุดหลังจากที่summariseพูดไปแล้วจะเหลือกลุ่มไหน โอ้หิน dplyr ...
Spacedman

ง่ายและชัดเจน ฉันไม่เคยรู้ทฤษฎีที่ลอกออกมาก่อนขอบคุณ!
Shixiang Wang

ดี ง่ายและมีประสิทธิภาพ เยี่ยมมาก!
2550228

38

คุณสามารถใช้count()ฟังก์ชั่นซึ่งมีพฤติกรรมแตกต่างกันไปตามรุ่นของdplyr:

  • dplyr 0.7.1: ส่งคืนตารางที่ไม่ได้จัดกลุ่ม : คุณต้องจัดกลุ่มอีกครั้งภายใน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()แอตทริบิวต์กับ


1
ดูเหมือนว่าคำตอบที่ไม่ถูกต้องในdplyr0.7.1 มันทำการคำนวณความถี่โดยรวมใน "เกียร์" แทนที่จะเป็นภายในแต่ละระดับของ "am"
Edwin

30

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

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%

6
คุณสามารถสร้างคลาส "เปอร์เซ็นต์" S3 ด้วยformatวิธีการที่เพิ่มเครื่องหมายเปอร์เซ็นต์ ... #overkill
Spacedman

การใช้สิ่งนี้อาจน่าสนใจเช่นกัน: stackoverflow.com/questions/13483430/…
Spacedman

ถ้าหากใครจะคำนวณค่าเฉลี่ย, sd และ SE ด้วยในตัวอย่างนี้
user3655531

6

นี่คือฟังก์ชั่นทั่วไปที่ใช้โซลูชันของเฮนริกในวันที่dplyr0.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
}

Error in bind_rows_(x, .id) : Column am` ไม่สามารถแปลงจากตัวเลขเป็นอักขระได้
`

5

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

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

3

แม้จะมีคำตอบมากมายที่อีกหนึ่งวิธีการที่ใช้prop.tableในการรวมกันด้วยหรือdplyrdata.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"]

1
โดยวิธีการที่ง่ายที่สุด
Parseltongue

1

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

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

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

getOption("scipen") 
options("scipen"=10) 
mtcars %>%
count(am, gear) %>% 
mutate(freq = (n / sum(n)) * 100)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.