Tần số / tỷ lệ tương đối với dplyr


153

Giả sử tôi muốn tính tỷ lệ của các giá trị khác nhau trong mỗi nhóm. Ví dụ: bằng cách sử dụng mtcarsdữ liệu, làm cách nào để tính tần số tương đối của số bánh răng theo am (tự động / thủ công) trong một lần với 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 

Những gì tôi muốn đạt được:

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
Là những tỷ lệ phần trăm con số thực tế bạn muốn? Họ đến từ đâu, đại số? À, 79% là 15 / (15 + 4), 21% là 4 / (15 + 4) và sau đó cho am == 1 62% là 8 / (8 + 5), v.v.
Spainedman

1
@Spacesman Có, đó là những con số tôi muốn và Frank là chính xác, chúng tổng hợp tới 100% theo biến am (79 + 21) và (62 + 38) ..
jenswirf

2
Điều này thực sự có vẻ như đang tìm kiếm một triển khai dplyr bản địa của prop.table()/ sweep(). Ngoài ra, trong các câu hỏi khác, một số người đang yêu cầu tùy chọn bao gồm số không cho các biến hoặc tương tác biến
smci

Câu trả lời:


285

Thử cái này:

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

Từ họa tiết dplyr :

Khi bạn nhóm theo nhiều biến, mỗi tóm tắt sẽ tách ra một cấp độ của nhóm. Điều đó làm cho nó dễ dàng cuộn lên một tập dữ liệu.

Do đó, sau khi summarise, biến nhóm cuối cùng được chỉ định trong group_by, 'gear', được bóc ra. Trong mutatebước này, dữ liệu được nhóm theo (các) nhóm nhóm còn lại, ở đây 'am'. Bạn có thể kiểm tra nhóm trong mỗi bước với groups.

Kết quả của việc lột tất nhiên phụ thuộc vào thứ tự của các biến nhóm trong group_bycuộc gọi. Bạn có thể muốn làm tiếp theo group_by(am), để làm cho mã của bạn rõ ràng hơn.

Để làm tròn và làm đẹp, vui lòng tham khảo câu trả lời hay của @Tyler Rinker.


5
Tôi cũng mới phát hiện ra giải pháp đó, nhưng tôi không biết tại sao lại sum(n)hoạt động trong amnhóm chứ không phải cả gearnhóm nữa ...
Spacesman

7
Xem họa tiết : "Khi bạn nhóm theo nhiều biến, mỗi tóm tắt sẽ tách ra một cấp độ của nhóm."
Henrik

7
Đẹp - nếu bạn chỉ dừng lại sau khi summarisenó nói nhóm nào còn lại. Oh dplyr đá ...
Spacesman 04/07 '

Đơn giản và rõ ràng. Tôi chưa bao giờ biết lột bỏ lý thuyết trước đây, cảm ơn!
Shixiang Wang

đẹp. đơn giản và hiệu quả. Bạn đã làm rất tốt!
dùng2550228

38

Bạn có thể sử dụng count()chức năng, tuy nhiên có một hành vi khác nhau tùy thuộc vào phiên bản của dplyr:

  • dplyr 0.7.1: trả về một bảng chưa được nhóm : bạn cần nhóm lại theoam

  • dplyr <0.7.1: trả về một bảng được nhóm , do đó không cần phải nhóm lại, mặc dù bạn có thể muốn ungroup()cho các thao tác sau này

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))

Kết quả này thành một bảng được nhóm , nếu bạn muốn sử dụng nó để phân tích thêm, có thể hữu ích để loại bỏ thuộc tính được nhóm với ungroup().


1
Đây có vẻ là một câu trả lời không hợp lệ trên dplyr0.7.1. Nó thực hiện tính toán tần số tổng thể trên "bánh răng", thay vì trong từng cấp độ "sáng".
Edwin

30

@ Henrik là tốt hơn cho khả năng sử dụng vì điều này sẽ làm cho ký tự cột và không còn là số nhưng phù hợp với những gì bạn yêu cầu ...

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%

EDIT Bởi vì Spacesman yêu cầu nó :-)

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
Bạn luôn có thể tạo lớp "phần trăm" S3 bằng formatphương pháp thêm dấu phần trăm ... #overkill
Spainedman

Việc thực hiện điều này cũng có thể thú vị: stackoverflow.com/questions/13483430/
Kẻ

Điều gì sẽ xảy ra nếu người ta tính trung bình, sd và SE trong ví dụ này?
dùng3655531

6

Đây là một chức năng chung thực hiện giải pháp của Henrik trên 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` không thể được chuyển đổi từ số sang ký
tự`

5

Tôi đã viết một chức năng nhỏ cho nhiệm vụ lặp lại này:

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

Sau đó tôi có thể sử dụng nó như:

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

Nó trở lại:

# 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

Mặc dù có nhiều câu trả lời, một cách tiếp cận khác sử dụng prop.tablekết hợp với dplyrhoặc 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"]

1
Cho đến nay, cách tiếp cận đơn giản nhất
Parseltongue

1

Câu trả lời này dựa trên câu trả lời của Matifou.

Đầu tiên tôi sửa đổi nó để đảm bảo rằng tôi không nhận được cột freq được trả về làm cột ký hiệu khoa học bằng cách sử dụng tùy chọn scipen.

Sau đó, tôi nhân nhiều câu trả lời với 100 để lấy phần trăm thay vì thập phân để làm cho cột freq dễ đọc hơn theo phần trăm.

getOption("scipen") 
options("scipen"=10) 
mtcars %>%
count(am, gear) %>% 
mutate(freq = (n / sum(n)) * 100)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.