Frekuensi / proporsi relatif dengan dplyr

153

Misalkan saya ingin menghitung proporsi nilai yang berbeda dalam setiap kelompok. Sebagai contoh, menggunakan mtcarsdata, bagaimana cara menghitung frekuensi relatif dari jumlah roda gigi dengan am (otomatis / manual) dalam sekali jalan 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 

Apa yang ingin saya capai:

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
jenswirf
sumber
1
Apakah persentase itu adalah angka aktual yang Anda inginkan? Dari mana mereka berasal, secara aljabar? Ah, 79% adalah 15 / (15 + 4), 21% adalah 4 / (15 + 4) dan kemudian untuk am == 1 62% adalah 8 / (8 + 5) dll.
Spacedman
1
@Spacedman Ya, itu adalah angka yang saya inginkan dan Frank benar, mereka menjumlahkan hingga 100% oleh variabel am (79 + 21) dan (62 + 38) ..
jenswirf
2
Ini tampaknya mencari implementasi dplyr asli dari prop.table()/ sweep(). Juga, dalam pertanyaan lain beberapa orang meminta opsi untuk memasukkan nol perhitungan untuk variabel atau interaksi variabel
smci

Jawaban:

285

Coba ini:

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

Dari sketsa dplyr :

Saat Anda mengelompokkan berdasarkan beberapa variabel, setiap ringkasan melepaskan satu tingkat pengelompokan. Itu membuatnya mudah untuk secara bertahap menyatukan dataset.

Jadi, setelah itu summarise, variabel pengelompokan terakhir yang ditentukan dalam group_by, 'roda gigi', dikupas. Pada mutatelangkah ini, data dikelompokkan berdasarkan variabel pengelompokan yang tersisa, inilah dia. Anda dapat memeriksa pengelompokan di setiap langkah dengan groups.

Hasil dari peeling tentu saja tergantung pada urutan variabel pengelompokan dalam group_bypanggilan. Anda mungkin ingin melakukan selanjutnya group_by(am), untuk membuat kode Anda lebih eksplisit.

Untuk pembulatan dan prettifikasi, silakan merujuk ke jawaban yang bagus oleh @Tyler Rinker.

Henrik
sumber
5
Saya baru saja menemukan solusi itu, tetapi saya tidak tahu mengapa sum(n)berhasil di atas amgrup dan bukan di geargrup juga ...
Spacedman
7
Lihat sketsa : "Ketika Anda mengelompokkan beberapa variabel, setiap ringkasan dikupas satu tingkat pengelompokan."
Henrik
7
Bagus - jika Anda hanya berhenti setelah summariseitu mengatakan kelompok mana yang tersisa. Oh dplyr rocks ...
Spacedman
Sederhana dan jelas. Saya tidak pernah tahu teori peeling off sebelumnya, terima kasih!
Shixiang Wang
bagus. sederhana dan efektif. kerja bagus!
user2550228
38

Anda dapat menggunakan count()fungsi, yang memiliki perilaku berbeda tergantung pada versi dplyr:

  • dplyr 0.7.1: mengembalikan tabel yang tidak dikelompokkan : Anda perlu mengelompokkan lagi berdasarkanam

  • dplyr <0.7.1: mengembalikan tabel yang dikelompokkan , jadi tidak perlu dikelompokkan lagi, meskipun Anda mungkin ingin ungroup()memanipulasi nanti

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

Ini menghasilkan tabel yang dikelompokkan , jika Anda ingin menggunakannya untuk analisis lebih lanjut, mungkin berguna untuk menghapus atribut yang dikelompokkan dengannya ungroup().

Matifou
sumber
1
Ini sepertinya jawaban yang tidak valid pada dplyr0.7.1. Itu perhitungan frekuensi secara keseluruhan pada "gear", bukan dalam setiap tingkat "am".
Edwin
30

@ Henrik lebih baik untuk kegunaan karena ini akan membuat karakter kolom dan tidak lagi numerik tetapi cocok dengan apa yang Anda minta ...

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 Karena Spacedman memintanya :-)

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%
Tyler Rinker
sumber
6
Anda selalu dapat membuat kelas S3 "persentase" dengan formatmetode yang menambahkan tanda persen ... #overkill
Spacedman
Menerapkan ini mungkin menarik juga: stackoverflow.com/questions/13483430/…
Spacedman
Bagaimana jika seseorang akan menghitung mean, sd dan SE juga dalam contoh ini?
user3655531
6

Berikut adalah fungsi umum yang mengimplementasikan solusi Henrik pada 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
}
Edwin
sumber
Error in bind_rows_(x, .id) : Column am` tidak dapat dikonversi dari numerik ke karakter`
f0nzie
5

Saya menulis fungsi kecil untuk tugas yang berulang ini:

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

Saya kemudian dapat menggunakannya seperti:

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

Ia mengembalikan:

# 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
slhck
sumber
3

Terlepas dari banyak jawaban, satu pendekatan lagi yang digunakan prop.tabledalam kombinasi dengan dplyratau 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"]
TimTeaFan
sumber
1
Sejauh ini pendekatan yang paling sederhana
Parseltongue
1

Jawaban ini didasarkan pada jawaban Matifou.

Pertama saya memodifikasinya untuk memastikan bahwa saya tidak mendapatkan kolom freq dikembalikan sebagai kolom notasi ilmiah dengan menggunakan opsi scipen.

Lalu saya gandakan jawabannya dengan 100 untuk mendapatkan persen daripada desimal untuk membuat kolom freq lebih mudah dibaca sebagai persentase.

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